In spite of having read K&R, and having even taught C classes, I find myself embarrassingly unable to fully understand what one might call "modern" C.
There seems to be many unwritten conventions in modern programming that, as far as I know, aren't documented anywhere.
Take, for example, the SQLite source code. In it I find for instance:
SQLITE_API int sqlite3_close(sqlite3 *);
What does SQLITE_API
stand for? How is this even syntactically correct?
Or this:
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
Is there an accepted convention somewhere on when to prefix macros with underscores? Sometimes I see macros prefixed with two underscores.
Or what about the use of fixed-size types, such as uint32
and so forth. When should o开发者_如何学运维ne use this practice, and when not? What about the new-ish bool
type, when should it be preferred over simple ints?
These are some of the questions I pose myself when I read other people's source code. Is there a reference somewhere that might help me answer these questions?
SQLITE_API
in code like this is very likely a preprocessor define, that worries about exposing the call in e.g. a DLL library build. That's pretty common.
If it's all upper case in C, chances are it's a preprocessor symbol, and a good idea is often to run the game through the preprocessor and read what comes out.
This
#ifndef _SQLITE3_H_
#define _SQLITE3_H_
is just a guard against multiple inclusion.
It prevents errors in the case where xxx.h includes this file as well as yyy.h, and yyy.h also includes this file.
Afaik GNU coding standards are under constant revision/update so might be a good snapshot of 'modern' style.
http://www.gnu.org/prep/standards/
Re: specifically single or double undercores, in my experience and from what I often read; it's grossly accepted safer to avoid double underscore prefixes as these are usually 'reserved' for framework/system/compiler-specific and compiler-related elements, thus macros intended soley for use within the module/package/unit/project they are defined should avoid being undescore-prefixed at all.
Most institions have their own language coding standards and guidelines which can vary quite significantly. As ever, consistency is key.
SQLITE_API
is an example of what I call the 'call type idiom'. It's a preprocessor directive used to improve portability in header files, where the header file needs to define some specific calling mode, typically between main code and a DLL or similar.
Depending on the platform and compiler in use SQLITE_API will usually expand to some combination of the available calling conventions, like cdecl
,__stdcall
or similar.
You should find the definition of it in the header file.
I don't think there's one great reference for this stuff. What you're witnessing is a series of conventions, some used very broadly in the industry, and some possibly more specific to your own code base, that have arisen to handle standard challenges or situations in large-scale software projects in C (and derivatives). As you note, K&R is a great teaching tool but does not address any of these large-scale project conventions, which have mostly appeared organically and within industry.
You give two great examples. The first is a #define
d somewhere to decorate a function so that it's either exported correctly or just documented that it's going to be exported. The second pattern is called an "include guard" (see link) and the conventional underscores are optional but are a good way to avoid conflict with normal magic-number definitions.
There are lots of conventions out there, many involving the preprocessor with definitions and macros. Your best bet is probably to look up and ask about each pattern individually. You'll likely get thoughtful responses here about rationale, and good discussion.
Expert C Programming Is a great follow up to K&R. Don't be put off by the title. It is a very accessible book if you know the basics of C. It covers a lot of real world code situations which K&R is pretty short on.
With that said there is really no substitute for reading and writing code. Most of the modern conventions are not really standards and often are adhered to differently in different code bases. They are just common solutions to the problems and limitations everyone faces writing in the language. The most important thing in my opinion is to understand what those problems and limitations are.
First, you should realize that defining "_SQLITE3_H_" is actually prohibited -- all names starting with an underscore followed by another underscore or a capital letter are reserved. When you're doing it, the simple answer is that it's probably best to avoid using a leading underscore on anything you define. Leave them for the implementation.
As far as the SQLLITE_API goes, it's probably defined differently on different platforms. If you were building it as a DLL on Windows, for example, it would probably be defined something like:
#define SQLLITE_API __declspec(dllexport) __stdcall
On the other hand, when you're using the header in your code, it probably gets expanded to something like:
#define SQLLITE_API __declspec(dllimport) __stdcall
These basically tell the compiler that when you're building the DLL you want the function exported (so it's visible to the rest of the world). When you're using it in your own code, it tells the compiler that the function in question will come from a DLL.
I would recommend "C Interfaces and Implementations: Techniques for Creating Reusable Software" by David R. Hanson. I think this is one of the best book about use of C language.
精彩评论