One of the biggest sources of discussion is how frequent comments should be, where they should go, and what they should say. One particularly famous set of style guidelines for assembly language coding a few decades back said that every statement should have a comment. This lead to lines such as
ADD r1, 8 ; add 8 to register 1.
Programmers, being lazy people in general, even automated this, running their code through a simple program that added a comment like this to the end of any line that didn't contain a comment already.
This directive was well intentioned, but it didn't pass the test I proposed: It couldn't explain why it was there and what it hoped to achieve. What are comments for? You can probably see that the comment here is pretty useless, but why is it useless? The answer is that it contains no more information than the code. This kind of comment is sometimes useful for sanity checking. For example, if I write:
// Multiply x by 2: x <<= 2;
Someone looking at this code can see that either the comment is wrong or the code is wrong. Unfortunately, this is still not particularly useful. It gives a bit more informationthe shift is being used as a multiplicationbut doesn't give you the important information, namely why x needs to be multiplied by two (or possibly four).
The point of comments is to include information that is not visible in the code. This can include explaining why you are doing something, but in big projects there is another important use: Some of the people reading the comments will not be reading the code at all.
Most languages support some automated tools that will extract comments and generate documentation from them. These comments will be read mainly by people who won't want to read the code, and may not be able to. They need to explain how the various bits of the code should be used, rather than how they work.
One problem that arises with some of these tools is whether you document the interface or the implementation. In a language like Java, where there is no distinction, this doesn't arise. In C-family languages, you typically declare code in a header and then implement it in a different file.
The obvious place to put documentation depends on who you ask. Some argue that it's more sensible to write it near the implementation, because then you can compare the documentation and the implementation easily. On the other hand, if you put it in the headers then someone with access to the header files is guaranteed to have access to the documentation, even if they didn't use the automated document extraction tools.
Which approach you choose depends on your workflow. If you are generating documents automatically from your revision control system, then it doesn't matter. If you are not, then people may not have access to the documentation when they are using the code. They will, at a minimum, have access to the compiled library binary and the headers, so putting them in the headers is a better choice. Whichever you choose, be consistent.