Re: [PIC] Learning C
Christopher Head <[email protected]>
| Newsgroups | gmane.comp.hardware.microcontrollers.pic |
|---|---|
| Message-ID | <[email protected]> |
On Tue, 19 Nov 2024 11:54:46 -0700 "Harold Hallikainen" <[email protected]> wrote: > On learning C, I really like it, but could use more knowledge on > pointers, void types, and use of CONST. I generally think CONST means > it is constant, does not change, and could be stored in flash. But > I've seen stuff where that does not seem to be the case. Here are a couple of things I can write a bit about! I think some confusion probably stems from the fact that the same word means different things depending on the context (a statement true of both void and const). Regarding void: Used as the return type of a function, it means “nothing”—i.e. the function doesn’t return anything. “int f(int x) { return x + 1; }”, the function “f” returns an int. “void g(int x) { do_something_with(x); }”, the function “g” doesn’t return anything at all. Pretty simple. Used as part of a pointer, it means “unknown” or “unspecified”—i.e. if you have “void *p”, it doesn’t mean “p is a pointer to nothing”, it means “p is a pointer to something, but I’m not saying *what*”. It might point at one or more ints. It might point at one or more doubles. It might point at one or more instances of some struct. Before you can do anything concrete with it, you have to convert it to some other pointer type. Regarding const: Used for a variable itself (e.g. “const int x”), it means you promise not to modify “x”, and the compiler will help you keep that promise as well as it can. On an MCU, the compiler MAY put it in Flash/ROM/etc.. On a PC, the compiler MAY put it in a section of the executable file that gets mapped read-only so you get a crash if you try to write to it. Neither of these is guaranteed, just typical. The compiler helps you by making something like “x = 27;” or “int *p = &x;” errors (or at the very least warnings); however, as is often the case, casting allows you to do things the compiler can’t check, and then it’s up to you to make sure you’re keeping your promise. Because of the confusing shape of declarations of variables of pointer type, “int * const p” is another example of this. The “const” is to the right of the asterisk, and it means “p is const”. In other words, “I promise not to modify p”. You’re welcome to modify whatever p points at, just not p itself. People often recommend reading points right-to-left, so this can be read as “p is a const pointer to int” (the pointer is const, the int is not). Used for the target of a pointer (e.g. “const int *p” or “int const *p”, read as “p is a pointer to an int const/const int”), it means you intend not to modify the thing pointed at THROUGH THIS POINTER. It does NOT mean the thing pointed at won’t be modified! For example, “int x = 1; const int *p = &x; x = 2;” is perfectly legitimate; you modified “x”, just not through “p”. You can bypass this rule via a cast; it’s meant as a helpful tool, not a straightjacket. Generally this is most useful for function parameters, where “void f(int *p)” suggests that the function may modify the pointed-to int (though there’s no requirement for it to do so), while “void f(const int *p)” suggests that it won’t (technically it can, by casting, but that would be considered unusual and ought to be called out loudly in the documentation). In neither case does it mean you have to pass a pointer to a const int; rather, it’s just telling you whether to expect calling “f” to modify the int or not. It is, I think, also technically valid C to write “int x;” in one translation unit (typically a source file) and “extern const int x;” in another (typically a header file) to create a variable that can only be modified in one source file, but read elsewhere. That’s pretty rare though. Finally, I’d recommend enabling as many compiler warnings as you possibly can. There will be false positives. There will also be true positives and you’ll be thankful for them. Personally I like to use -Werror, which turns all warnings into errors, and then use my compiler’s provided method to silence specific warnings in places where I know they’re wrong (in the case of GCC, this is “#pragma GCC diagnostic”). -- Christopher Head