As discussed in Access symbols defined in the linker script by application, "Accessing a linker script defined variable from source code is not intuitive" - essentially, accessing their value usually isn't what you want (since they don't really have a block of memory assigned, as a true compiler variable), and only their accessed by their address. Is there an attribute that can be applied to the variable upon declaration, or perhaps a PC-Lint/static-analysis property/rule which can be applied to the variables?
/* Linker config (.icf) file */
define symbol __ICFEDIT_region_ROM_start__ = 0x08000000;
define symbol __ICFEDIT_region_ROM_end__ = 0x080FFFFF;
define symbol __ICFEDIT_region_ROM_size__ = (__ICFEDIT_region_ROM_end__ - __ICFEDIT_region_ROM_start__) + 1;
export symbol __ICFEDIT_region_ROM_start__;
export symbol __ICFEDIT_region_ROM_size__;
/* main.c */
void OS_SetROM(uint32_t start, uint32_t size){} // empty for demonstration only
int main(void)
{
extern unsigned int __ICFEDIT_region_ROM_start__;
extern unsigned int __ICFEDIT_region_ROM_size__;
// INCORRECT - both probably read as '0', depending on what's actually in those locations
// Can I get a warning or error about this usage?
OS_SetROM(__ICFEDIT_region_ROM_start__, __ICFEDIT_region_ROM_size__);
// CORRECT - *addresses of* linker-defined variables read
OS_SetROM((uint32_t)&__ICFEDIT_region_ROM_start__, (uint32_t)&__ICFEDIT_region_ROM_size__);
It would be nice to have the addresses declared and behave as pointers (as below), i.e. where you can use the value of the pointer variable to represent the address, and 'value-of' semantics make more sense (at least logically - more obvious that you wouldn't dereference in this case), but this isn't how they work - for that, the linker would have to assign a memory location as well and store the address there, or some special semantics of the compiler/linker, which doesn't appear to be possible...
void void OS_SetROM(uint32_t * const start, uint32_t size){} // empty for demonstration only
int main(void)
{
// would be nice, but not how it works
extern unsigned int * const __ICFEDIT_region_ROM_start__;
extern unsigned int const __ICFEDIT_region_ROM_size__;
OS_SetROM(__ICFEDIT_region_ROM_start__, __ICFEDIT_region_ROM_size__);
A compromise of-sorts could be to redefine these variables with an appropriate type, ala:
unsigned int * const p_rom_start = &__ICFEDIT_region_ROM_start__;
unsigned int const rom_size = (unsigned int)&__ICFEDIT_region_ROM_size__;
void OS_SetROM(unsigned int * const p_start, unsigned int size);
OS_SetROM(p_rom_start, rom_size);
which helps collect the 'unintuitive' accesses into one place and type-safe accesses thereafter, but that isn't possible in this case as the API is predefined to require uint32_t's.
I realize this is probably uncommon (and probably only used a few times within a project, if at all), and I realize this also depends on using the attribute (e.g. when creating a new project), but I'm curious if there are guards that can be put in place to protect against accidental misuse - or against incorrect 'simplification' (e.g. by some maintainer later who doesn't understand the implications)... I also can't think of another scenario where enforcing 'address-only' access makes sense, so the solution may not exist...
You can declare an identifier to be for an object of incomplete type, such as a structure whose definition is not given:
extern struct NeverDefined __ICFEDIT_region_ROM_start__;
Then you can take its address, but attempting to use or assign it will yield a compiler error.
You shouldn't put c++ and c in the same question; they are different languages for different purposes.
In C, at least, declaring them as:
extern char ROMStart[], ROMEnd[]; /* shortforms are less clumbsy */
gives you much of what you want:
OS_SetROM(ROMStart, ROMEnd, ROMEnd-ROMStart); /* not sure why the last one */
although clever by half compilers might complain if you try to treat these locations as shorts or longs about alignment, so to mollify them, you likely would:
extern long ROMStart[], ROMEnd[];
intptr_t size = (intptr_t)ROMEnd - (intptr_t)ROMStart;
OS_SetROM(ROMStart, ROMEnd, size);
In C++ you would have to consult the language standard du minute.
Related
I have a function that returns an array of strings depending on the OS running the code.
int arrayLength = getMeThatArrayLength();
char* someArray[arrayLenth];
populateTheArray(&someArray, arrayLength);
Once generated, the array should be immutable moving forward.
The data returned should not change throughout the program's lifetime.
Like if I had stored the data in these sort of variables:
const int cArrayLength;
const char* const* cSomeArray;
where cArrayLength would hold whatever arrayLength has and cSomeArray would hold whatever someArray has.
Direct assigning throws error and I understand why.
const int cArrayLength = arrayLength;
const char* const* cSomeArray = someArray;
Maybe I need an immutable pointer to the mutable array? I'm fairly new to C so I'm not completely sure but maybe the pointer could point to the array but it'd be immutable from the pointer's perspective and as an extensions from the function's which is holding that immutable pointer.
Is there a way of doing that?
C doesn't let you access non-const extern/static/_Thread_local data via const-qualified declarations.
You can't have (officially; practically it tends to work) a global int arrayLength; that's exposed elsewhere (in another translation unit) as extern int const arrayLength;.
What you can have is a static/static _Thread_local writable global that's accessed via an accessor that returns a pointer-to-const pointer to it.
Example code:
//PUBLIC HEADER
struct myvec{
int arrayLegth;
char const*const* someArray;
};
struct myvec const* getmyvec(void);
/*const on structs works as if it make each memeber const:
int const arrayLength
char const*const*const someArray;
*/
///PRIVATE IMPLEMENTATION (in a C file that includes the header)
static struct myvec internal;
struct myvec const* getmyvec(void)
{
if(internal.someArray) return &internal;
//(allocate?+) fill internal ...
return &internal;
}
The accessor method (as well as the officially unsupported way of exposing int x; as extern int const x;) rely on the type system.
The underlying data won't be in write-protected memory like it usually (implementation detail) would be if it was static/extern/_Thread_local data that was originally declared const. This means that you can cast away the const and then write to the data via the new pointer.
Using system-dependent mechanisms like mprotect* it's actually possible to mark a page of memory read-only so that attempts to write to it later generate segfaults, but that's a rather coarse and expensive operation that might not be worth it—the types-system-based protection could be sufficient in your use case.
mprotect is how program loaders usually implement write-protection for global data originally declared const: a linker will have aggregated all such data into a single contiguous block, the loader will load it, and then it will mprotect all of it as read-only in one system call.
I am working on a project where I need to create the address range of many, many global variables in C (C++ not possible), with clang. For symbols of complete types, this is easy in a standard-compliant way:
typedef struct range {
void* begin;
void* end;
} range;
extern int foo;
range foo_range = { &(&foo)[0], &(&foo)[1] };
But as I said, it works because the C compiler statically knows the size of foo, so it's able to resolve &(&foo)[1] as foo+4 bytes (assuming that sizeof(int) is 4, of course). This won't work for symbols of an incomplete type:
struct incomplete;
struct trailing_array {
int count;
int elements[];
};
extern int foo[];
extern struct incomplete bar;
extern struct trailing_array baz;
range foo_range = { &(&foo)[0], &(&foo)[1] };
// error: foo has incomplete type
range bar_range = { &(&bar)[0], &(&bar)[1] };
// error: bar has incomplete type
range bar_range = { &(&baz)[0], &(&baz)[1] };
// this one compiles, but the range excludes the elements array
However, it's not a problem for me to describe these symbols some more. For instance, I can easily add metadata:
// foo.h
extern int foo[];
extern size_t foo_size;
// foo.c
int foo[] = {1,2,3};
size_t foo_size = sizeof(foo);
Except that this won't help my problem for references outside of foo.c, because foo_size is not a compile-time constant, and therefore this wouldn't work:
range foo_range = { &foo, (void*)&foo + foo_size };
// error: foo_size not a compile-time constant
What would work, however, is getting the address of a symbol that ends right where my object ends. For instance, if I define foo with this assembly code:
_foo:
.long 1
.long 2
.long 3
_foo_end:
Then, in my C code, I can have:
extern int foo[];
extern int foo_end;
range foo_range = { &foo, &foo_end };
and that effectively solves my problem.
However, while I have the flexibility to add symbols, I don't have the flexibility to rewrite every global declaration as a file-level assembly statement. So, my question is: what is the closest that I can get to that using clang?
I know that I can use sections (since the linker makes start and end symbols for sections), but one section per global variable would be way overkill.
I know that I can't just take the address of a variable immediately after the global whose range I want to get, because the compiler has been known to reorder globals in some cases.
I'm specifically using Apple's linker, but if you have a solution that works for GNU ld/gold or lld, I'll still take it and see if I can get it to work here too.
Hm, there's no real way to do it if you're defining it in another translation unit. If you want, you can include a types.h file with contents
struct incomplete {
char data[SIZE];
}
Where SIZE is whatever integer you please, and do the same for each global variable. This will intersect future definitions however. Really, you'd have to go with
#define INCOMPLETE_SIZE 5
And then use that for range = { &bar, (void*)&bar + INCOMPLETE_SIZE }.
"Incomplete type" is just some standards terminology for properly describing how to parse
struct A {
A* ptr;
}
As far as I know, they don't really get used otherwise.
I also don't recommend &(&foo)[0], &(&foo)[1] as a way to get a range of pointers, it's very esoteric / hard to read. Much preferred is &foo, &foo + 1. You can see how one can turn this into a solution for bar by doing &bar, (void*)&bar + SIZE, where SIZE is some constant you have to specify somewhere in the code (Either by declaring it and using sizeof / &foo+1 solution, or defining the SIZE with a #define)
Which one is better to use among the below statements in C?
static const int var = 5;
or
#define var 5
or
enum { var = 5 };
It depends on what you need the value for. You (and everyone else so far) omitted the third alternative:
static const int var = 5;
#define var 5
enum { var = 5 };
Ignoring issues about the choice of name, then:
If you need to pass a pointer around, you must use (1).
Since (2) is apparently an option, you don't need to pass pointers around.
Both (1) and (3) have a symbol in the debugger's symbol table - that makes debugging easier. It is more likely that (2) will not have a symbol, leaving you wondering what it is.
(1) cannot be used as a dimension for arrays at global scope; both (2) and (3) can.
(1) cannot be used as a dimension for static arrays at function scope; both (2) and (3) can.
Under C99, all of these can be used for local arrays. Technically, using (1) would imply the use of a VLA (variable-length array), though the dimension referenced by 'var' would of course be fixed at size 5.
(1) cannot be used in places like switch statements; both (2) and (3) can.
(1) cannot be used to initialize static variables; both (2) and (3) can.
(2) can change code that you didn't want changed because it is used by the preprocessor; both (1) and (3) will not have unexpected side-effects like that.
You can detect whether (2) has been set in the preprocessor; neither (1) nor (3) allows that.
So, in most contexts, prefer the 'enum' over the alternatives. Otherwise, the first and last bullet points are likely to be the controlling factors — and you have to think harder if you need to satisfy both at once.
If you were asking about C++, then you'd use option (1) — the static const — every time.
Generally speaking:
static const
Because it respects scope and is type-safe.
The only caveat I could see: if you want the variable to be possibly defined on the command line. There is still an alternative:
#ifdef VAR // Very bad name, not long enough, too general, etc..
static int const var = VAR;
#else
static int const var = 5; // default value
#endif
Whenever possible, instead of macros / ellipsis, use a type-safe alternative.
If you really NEED to go with a macro (for example, you want __FILE__ or __LINE__), then you'd better name your macro VERY carefully: in its naming convention Boost recommends all upper-case, beginning by the name of the project (here BOOST_), while perusing the library you will notice this is (generally) followed by the name of the particular area (library) then with a meaningful name.
It generally makes for lengthy names :)
In C, specifically? In C the correct answer is: use #define (or, if appropriate, enum)
While it is beneficial to have the scoping and typing properties of a const object, in reality const objects in C (as opposed to C++) are not true constants and therefore are usually useless in most practical cases.
So, in C the choice should be determined by how you plan to use your constant. For example, you can't use a const int object as a case label (while a macro will work). You can't use a const int object as a bit-field width (while a macro will work). In C89/90 you can't use a const object to specify an array size (while a macro will work). Even in C99 you can't use a const object to specify an array size when you need a non-VLA array.
If this is important for you then it will determine your choice. Most of the time, you'll have no choice but to use #define in C. And don't forget another alternative, that produces true constants in C - enum.
In C++ const objects are true constants, so in C++ it is almost always better to prefer the const variant (no need for explicit static in C++ though).
The difference between static const and #define is that the former uses the memory and the later does not use the memory for storage. Secondly, you cannot pass the address of an #define whereas you can pass the address of a static const. Actually it is depending on what circumstance we are under, we need to select one among these two. Both are at their best under different circumstances. Please don't assume that one is better than the other... :-)
If that would have been the case, Dennis Ritchie would have kept the best one alone... hahaha... :-)
In C #define is much more popular. You can use those values for declaring array sizes for example:
#define MAXLEN 5
void foo(void) {
int bar[MAXLEN];
}
ANSI C doesn't allow you to use static consts in this context as far as I know. In C++ you should avoid macros in these cases. You can write
const int maxlen = 5;
void foo() {
int bar[maxlen];
}
and even leave out static because internal linkage is implied by const already [in C++ only].
Another drawback of const in C is that you can't use the value in initializing another const.
static int const NUMBER_OF_FINGERS_PER_HAND = 5;
static int const NUMBER_OF_HANDS = 2;
// initializer element is not constant, this does not work.
static int const NUMBER_OF_FINGERS = NUMBER_OF_FINGERS_PER_HAND
* NUMBER_OF_HANDS;
Even this does not work with a const since the compiler does not see it as a constant:
static uint8_t const ARRAY_SIZE = 16;
static int8_t const lookup_table[ARRAY_SIZE] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16}; // ARRAY_SIZE not a constant!
I'd be happy to use typed const in these cases, otherwise...
If you can get away with it, static const has a lot of advantages. It obeys the normal scope principles, is visible in a debugger, and generally obeys the rules that variables obey.
However, at least in the original C standard, it isn't actually a constant. If you use #define var 5, you can write int foo[var]; as a declaration, but you can't do that (except as a compiler extension" with static const int var = 5;. This is not the case in C++, where the static const version can be used anywhere the #define version can, and I believe this is also the case with C99.
However, never name a #define constant with a lowercase name. It will override any possible use of that name until the end of the translation unit. Macro constants should be in what is effectively their own namespace, which is traditionally all capital letters, perhaps with a prefix.
#define var 5 will cause you trouble if you have things like mystruct.var.
For example,
struct mystruct {
int var;
};
#define var 5
int main() {
struct mystruct foo;
foo.var = 1;
return 0;
}
The preprocessor will replace it and the code won't compile. For this reason, traditional coding style suggest all constant #defines uses capital letters to avoid conflict.
It is ALWAYS preferable to use const, instead of #define. That's because const is treated by the compiler and #define by the preprocessor. It is like #define itself is not part of the code (roughly speaking).
Example:
#define PI 3.1416
The symbolic name PI may never be seen by compilers; it may be removed by the preprocessor before the source code even gets to a compiler. As a result, the name PI may not get entered into the symbol table. This can be confusing if you get an error during compilation involving the use of the constant, because the error message may refer to 3.1416, not PI. If PI were defined in a header file you didn’t write, you’d have no idea where that 3.1416 came from.
This problem can also crop up in a symbolic debugger, because, again, the name you’re programming with may not be in the symbol table.
Solution:
const double PI = 3.1416; //or static const...
I wrote quick test program to demonstrate one difference:
#include <stdio.h>
enum {ENUM_DEFINED=16};
enum {ENUM_DEFINED=32};
#define DEFINED_DEFINED 16
#define DEFINED_DEFINED 32
int main(int argc, char *argv[]) {
printf("%d, %d\n", DEFINED_DEFINED, ENUM_DEFINED);
return(0);
}
This compiles with these errors and warnings:
main.c:6:7: error: redefinition of enumerator 'ENUM_DEFINED'
enum {ENUM_DEFINED=32};
^
main.c:5:7: note: previous definition is here
enum {ENUM_DEFINED=16};
^
main.c:9:9: warning: 'DEFINED_DEFINED' macro redefined [-Wmacro-redefined]
#define DEFINED_DEFINED 32
^
main.c:8:9: note: previous definition is here
#define DEFINED_DEFINED 16
^
Note that enum gives an error when define gives a warning.
The definition
const int const_value = 5;
does not always define a constant value. Some compilers (for example tcc 0.9.26) just allocate memory identified with the name "const_value". Using the identifier "const_value" you can not modify this memory. But you still could modify the memory using another identifier:
const int const_value = 5;
int *mutable_value = (int*) &const_value;
*mutable_value = 3;
printf("%i", const_value); // The output may be 5 or 3, depending on the compiler.
This means the definition
#define CONST_VALUE 5
is the only way to define a constant value which can not be modified by any means.
Although the question was about integers, it's worth noting that #define and enums are useless if you need a constant structure or string. These are both usually passed to functions as pointers. (With strings it's required; with structures it's much more efficient.)
As for integers, if you're in an embedded environment with very limited memory, you might need to worry about where the constant is stored and how accesses to it are compiled. The compiler might add two consts at run time, but add two #defines at compile time. A #define constant may be converted into one or more MOV [immediate] instructions, which means the constant is effectively stored in program memory. A const constant will be stored in the .const section in data memory. In systems with a Harvard architecture, there could be differences in performance and memory usage, although they'd likely be small. They might matter for hard-core optimization of inner loops.
Don't think there's an answer for "which is always best" but, as Matthieu said
static const
is type safe. My biggest pet peeve with #define, though, is when debugging in Visual Studio you cannot watch the variable. It gives an error that the symbol cannot be found.
Incidentally, an alternative to #define, which provides proper scoping but behaves like a "real" constant, is "enum". For example:
enum {number_ten = 10;}
In many cases, it's useful to define enumerated types and create variables of those types; if that is done, debuggers may be able to display variables according to their enumeration name.
One important caveat with doing that, however: in C++, enumerated types have limited compatibility with integers. For example, by default, one cannot perform arithmetic upon them. I find that to be a curious default behavior for enums; while it would have been nice to have a "strict enum" type, given the desire to have C++ generally compatible with C, I would think the default behavior of an "enum" type should be interchangeable with integers.
A simple difference:
At pre-processing time, the constant is replaced with its value.
So you could not apply the dereference operator to a define, but you can apply the dereference operator to a variable.
As you would suppose, define is faster that static const.
For example, having:
#define mymax 100
you can not do printf("address of constant is %p",&mymax);.
But having
const int mymax_var=100
you can do printf("address of constant is %p",&mymax_var);.
To be more clear, the define is replaced by its value at the pre-processing stage, so we do not have any variable stored in the program. We have just the code from the text segment of the program where the define was used.
However, for static const we have a variable that is allocated somewhere. For gcc, static const are allocated in the text segment of the program.
Above, I wanted to tell about the reference operator so replace dereference with reference.
We looked at the produced assembler code on the MBF16X... Both variants result in the same code for arithmetic operations (ADD Immediate, for example).
So const int is preferred for the type check while #define is old style. Maybe it is compiler-specific. So check your produced assembler code.
I am not sure if I am right but in my opinion calling #defined value is much faster than calling any other normally declared variable (or const value).
It's because when program is running and it needs to use some normally declared variable it needs to jump to exact place in memory to get that variable.
In opposite when it use #defined value, the program don't need to jump to any allocated memory, it just takes the value. If #define myValue 7 and the program calling myValue, it behaves exactly the same as when it just calls 7.
As has been discussed in several recent questions, declaring const-qualified variables in C (as opposed to const variables in C++, or pointers to const in C) usually serves very little purpose. Most importantly, they cannot be used in constant expressions.
With that said, what are some legitimate uses of const qualified variables in C? I can think of a few which have come up recently in code I've worked with, but surely there must be others. Here's my list:
Using their addresses as special sentinel values for a pointer, so as never to compare equal to any other pointer. For example: char *sentinel(void) { static const char s; return &s; } or simply const char sentinel[1]; Since we only care about the address and it actually wouldn't matter if the object were written to, the only benefit of const is that compilers will generally store it in read-only memory that's backed by mmap of the executable file or a copy of the zero page.
Using const qualified variables to export values from a library (especially shared libraries), when the values could change with new versions of the library. In such cases, simply using #define in the library's interface header would not be a good approach because it would make the application dependent on the values of the constants in the particular version of the library it was built with.
Closely related to the previous use, sometimes you want to expose pre-defined objects from a library to the application (the quintessential examples being stdin, stdout, and stderr from the standard library). Using that example, extern FILE __stdin; #define stdin (&__stdin) would be a very bad implementation due to the way most systems implement shared libraries - usually they require the entire object (here, FILE) to be copied to an address determined when the application is linked, and introduce a dependency on the size of the object (the program will break if the library is rebuilt and the size of the object changes). Using a const pointer (not pointer-to-const) here fixes all the problems: extern FILE *const stdin;, where the const pointer is initialized to point to the pre-defined object (which itself is likely declared static) somewhere internal to the library.
Lookup tables for mathematical functions, character properties, etc. This is the obvious one I originally forgot to include, probably because I was thinking of individual const variables of arithmetic/pointer type, as that's where the question topic first came up. Thanks to Aidan for triggering me to remember.
As a variant on lookup tables, implementation of state machines. Aidan provided a detailed example as an answer. I've found the same concept is also often very useful without any function pointers, if you can encode the behavior/transitions from each state in terms of a few numeric parameters.
Anyone else have some clever, practical uses for const-qualified variables in C?
const is quite often used in embedded programming for mapping onto GPIO pins of a microcontroller. For example:
typedef unsigned char const volatile * const tInPort;
typedef unsigned char * const tOutPort;
tInPort my_input = (tInPort)0x00FA;
tOutPort my_output = (tOutPort)0x00FC;
Both of these prevent the programmer from accidentally changing the pointer itself which could be disastrous in some cases. The tInPort declaration also prevents the programmer from changing the input value.
Using volatile prevents the compiler from assuming that the most recent value for my_input will exist in cache. So any read from my_input will go directly to the bus and hence always read from the IO pins of the device.
For example:
void memset_type_thing(char *first, char *const last, const char value) {
while (first != last) *(first++) = value;
}
The fact that last can't be part of a constant-expression is neither here nor there. const is part of the type system, used to indicate a variable whose value will not change. There's no reason to modify the value of last in my function, so I declare it const.
I could not bother declaring it const, but then I could not bother using a statically typed language at all ;-)
PC-lint warning 429 follows from the expectation that a local pointer to an allocated object should be consumed
by copying it to another pointer or
by passing it to a "dirty" function (this should strip the "custodial" property of the pointer) or
by freeing it or
by passing it up the caller through a return statement or a pass-by-pointer parameter.
By "dirty" I mean a function whose corresponding pointer parameter has a non-const base type. The description of the warning absolves library functions such as strcpy() from the "dirty" label, apparently because none of such library functions takes ownership of the pointed object.
So when using static analysis tools such as PC-lint, the const qualifier of parameters of called functions keeps locally allocated memory regions accounted.
const can be useful for some cases where we use data to direct code in a specific way. For example, here's a pattern I use when writing state machines:
typedef enum { STATE1, STATE2, STATE3 } FsmState;
struct {
FsmState State;
int (*Callback)(void *Arg);
} const FsmCallbacks[] = {
{ STATE1, State1Callback },
{ STATE2, State2Callback },
{ STATE3, State3Callback }
};
int dispatch(FsmState State, void *Arg) {
int Index;
for(Index = 0; Index < sizeof(FsmCallbacks)/sizeof(FsmCallbacks[0]); Index++)
if(FsmCallbacks[Index].State == State)
return (*FsmCallbacks[Index].Callback)(Arg);
}
This is analogous to something like:
int dispatch(FsmState State, void *Arg) {
switch(State) {
case STATE1:
return State1Callback(Arg);
case STATE2:
return State2Callback(Arg);
case STATE3:
return State3Callback(Arg);
}
}
but is easier for me to maintain, especially in cases where there's more complicated behavior associated with the states. For example, if we wanted to have a state-specific abort mechanism, we'd change the struct definition to:
struct {
FsmState State;
int (*Callback)(void *Arg);
void (*Abort)(void *Arg);
} const FsmCallbacks[] = {...};
and I don't need to modify both the abort and dispatch routines for the new state. I use const to prevent the table from changing at runtime.
A const variable is useful when the type is not one that has usable literals, i.e., anything other than a number. For pointers, you already give an example (stdin and co) where you could use #define, but you'd get an lvalue that could easily be assigned to. Another example is struct and union types, for which there are no assignable literals (only initializers). Consider for instance a reasonable C89 implementation of complex numbers:
typedef struct {double Re; double Im;} Complex;
const Complex Complex_0 = {0, 0};
const Complex Complex_I = {0, 1}; /* etc. */
Sometimes you just need to have a stored object and not a literal, because you need to pass the data to a polymorphic function that expects a void* and a size_t. Here's an example from the cryptoki API (a.k.a. PKCS#11): many functions require a list of arguments passed as an array of CK_ATTRIBUTE, which is basically defined as
typedef struct {
CK_ATTRIBUTE_TYPE type;
void *pValue;
unsigned long ulValueLen;
} CK_ATTRIBUTE;
typedef unsigned char CK_BBOOL;
so in your application, for a boolean-valued attribute you need to pass a pointer to a byte containing 0 or 1:
CK_BBOOL ck_false = 0;
CK_ATTRIBUTE template[] = {
{CKA_PRIVATE, &ck_false, sizeof(ck_false)},
... };
They can be used for memory-mapped peripherals or registers that cannot be changed by user code, only some internal mechanism of the microprocessor. Eg. on the PIC32MX, certain registers indicating program state are qualified const volatile - so you can read them, and the compiler won't try to optimise out, say, repeated accesses, but your code cannot write to them.
(I don't any code to hand, so I can't cite a good example right now.)
Our static analysis tool complains about a "useless type qualifier on return type" when we have prototypes in header files such as:
const int foo();
We defined it this way because the function is returning a constant that will never change, thinking that the API seemed clearer with const in place.
I feel like this is similar to explicitly initializing global variables to zero for clarity, even though the C standard already states that all globals will be initialized to zero if not explicitly initialized. At the end of the day, it really doesn't matter. (But the static analysis tool doesn't complain about that.)
My question is, is there any reason that this could cause a problem? Should we ignore the errors generated by the tool, or should we placate the tool at the possible cost of a less clear and consistent API? (It returns other const char* constants that the tool doesn't have a problem with.)
It's usually better for your code to describe as accurately as possible what's going on. You're getting this warning because the const in const int foo(); is basically meaningless. The API only seems clearer if you don't know what the const keyword means. Don't overload meaning like that; static is bad enough as it is, and there's no reason to add the potential for more confusion.
const char * means something different than const int does, which is why your tool doesn't complain about it. The former is a pointer to a constant string, meaning any code calling the function returning that type shouldn't try to modify the contents of the string (it might be in ROM for example). In the latter case, the system has no way to enforce that you not make changes to the returned int, so the qualifier is meaningless. A closer parallel to the return types would be:
const int foo();
char * const foo2();
which will both cause your static analysis to give the warning - adding a const qualifier to a return value is a meaningless operation. It only makes sense when you have a a reference parameter (or return type), like your const char * example.
In fact, I just made a little test program, and GCC even explicitly warns about this problem:
test.c:6: warning: type qualifiers ignored on function return type
So it's not just your static analysis program that's complaining.
You can use a different technique to illustrate your intent without making the tools unhappy.
#define CONST_RETURN
CONST_RETURN int foo();
You don't have a problem with const char * because that's declaring a pointer to constant chars, not a constant pointer.
Ignoring the const for now, foo() returns a value. You can do
int x = foo();
and assign the value returned by foo() to the variable x, in much the same way you can do
int x = 42;
to assign the value 42 to variable x.
But you cannot change the 42 ... or the value returned by foo(). Saying that the value returned from foo() cannot be changed, by applying the const keyword to the type of foo() accomplishes nothing.
Values cannot be const (or restrict, or volatile). Only objects can have type qualifiers.
Contrast with
const char *foo();
In this case, foo() returns a pointer to an object. The object pointed to by the value returned can be qualified const.
The int is returned by copy. It may be a copy of a const, but when it is assigned to something else, that something by virtue of the fact that it was assignable, cannot by definition be a const.
The keyword const has specific semantics within the language, whereas here you are misusing it as essentially a comment. Rather than adding clarity, it rather suggests a misunderstanding of the language semantics.
const int foo() is very different from const char* foo(). const char* foo() returns an array (usually a string) whose content is not allowed to change. Think about the difference between:
const char* a = "Hello World";
and
const int b = 1;
a is still a variable and can be assigned to other strings that can't change whereas b is not a variable. So
const char* foo();
const char* a = "Hello World\n";
a = foo();
is allowed but
const int bar();
const int b = 0;
b = bar();
is not allowed, even with the const declaration of bar().
Yes. I would advise writing code "explicitly", because it makes it clear to anyone (including yourself) when reading the code what you meant. You are writing code for other programmers to read, not to please the whims of the compiler and static analysis tools!
(However, you do have to be careful that any such "unnecessary code" does not cause different code to be generated!)
Some examples of explicit coding improving readability/maintainability:
I place brackets around portions of arithmetic expressions to explicitly specify what I want to happen. This makes it clear to any reader what I meant, and saves me having to worry about (or make ay mistakes with) precedence rules:
int a = b + c * d / e + f; // Hard to read- need to know precedence
int a = b + ((c * d) / e) + f; // Easy to read- clear explicit calculations
In C++, if you override a virtual function, then in the derived class you can declare it without mentioning "virtual" at all. Anyone reading the code can't tell that it's a virtual function, which can be disastrously misleading! However you can safely use the virtual keyword: virtual int MyFunc() and this makes it clear to anyone reading your class header that this method is virtual. (This "C++ syntax bug" is fixed in C# by requiring the use of the "override" keyword in this case - more proof if anyone needed it that missing out the "unnecessary virtual" is a really bad idea)
These are both clear examples where adding "unnecessary" code will make the code more readable and less prone to bugs.