5

Say I have a compilation unit file1.c, which declares a file-scope variable like so:

int my_variable = 12;

Then, in another compilation unit file2.c, I create an extern declaration for that variable, but declare it as const:

extern const int my_variable;

This will compile and work fine with gcc , using -Wall -Wextra -ansi -pedantic. However, the C89 standard says For two qualified types to be compatible, both shall have the identically qualified version of a compatible type. Adding const to the declaration adds a restriction rather than avoiding one. Is this safe and valid C? What would be the best practice in setting this up with header files?

Shafik Yaghmour
  • 154,301
  • 39
  • 440
  • 740
Tom Galvin
  • 861
  • 7
  • 12
  • 2
    May be the compiler would complain if it was the other way, if you discard the `const` quialifier. But I'm not really sure. – Iharob Al Asimi Dec 29 '14 at 12:44
  • 2
    I am pretty sure [C — Accessing a non-const through const declaration](http://stackoverflow.com/q/8051969/1708801) covers this case. – Shafik Yaghmour Dec 29 '14 at 12:51
  • Probably worth specifying the exact C standard version you are referring to. – Clifford Dec 29 '14 at 12:54
  • @Clifford: http://port70.net/~nsz/c/c89/c89-draft.html#3.5.3 in *semantics*. – Tom Galvin Dec 29 '14 at 13:03
  • 1
    You should generally "improve" the question rather than answer by comment - done. It was worthwhile because the question @ShafikYaghmour linked to relates to C99 rather than C89; the `-ansi` option is equivalent to `-std=c90` and ISO C90 and ANSI C89 are identical. However it is perhaps too late because it has now been marked as a duplicate. – Clifford Dec 29 '14 at 18:20
  • @ShafikYaghmour : No I was just pointing out that the two questions specified different language standards, and may differ in this respect. I referenced you simply because you made the suggestion (entirely appropriately). – Clifford Dec 29 '14 at 18:31

2 Answers2

5

It's clearly undefined as the declarations don't match. As you noted, const int and int aren't compatible types. A diagnostic is required only if they appear in the same scope.

It isn't safe in practice either, consider

$ cat test1.c
#include <stdio.h>

extern const int n;
void foo(void);

int main(void) {
    printf("%d\n", n);
    foo();
    printf("%d\n", n);
}
$ cat test2.c
int n;
void foo(void) { ++n; }
$ gcc -std=c99 -pedantic test1.c test2.c && ./a.out
0
1
$ gcc -O1 -std=c99 -pedantic test1.c test2.c && ./a.out
0
0

Gcc assumes that n isn't changed by foo() when optimizing, because it may assume the definition of n is of a compatible type, thus const.

Chances are that you get the expected behaviour with also volatile-qualifying n in test1.c, but as far as the C standard is concerned, this is still undefined.

The best way I can think of to prevent the user from accidentally modifying n is to declare a pointer to const, something along

int my_real_variable;
const int *const my_variable = &my_real_variable;

or perhaps some macro

#define my_variable (*(const int *)&my_variable)

With C99, my_real_variable can be avoided via a compound literal:

const int *const my_variable_ptr = &(int){ 12 };

It would be legal to cast away const here (as the int object itself isn't const), but the cast would be required, preventing accidental modification.

mafso
  • 5,433
  • 2
  • 19
  • 40
4

In this case the definition and declaration appear in separate translation units, so the compiler cannot perform any type or qualifier checks. The symbols are resolved by the linker and in this case it seems that the linker is not enforcing this qualifier matching.

If the definition and the declaration appeared in the same translation unit; for example if you placed the extern declaration in a header file and included it in file1.c, then I would imagine that the compiler would complain. By placing them in separate translation units, the compiler never sees both so cannot perform the check.

Clifford
  • 88,407
  • 13
  • 85
  • 165
  • I assume from this that the behaviour of the linker in this situation is implementation-specific? In this situation, all I know is that C name decoration pays no attention to `const` (whereas C++ does, for example.) – Tom Galvin Dec 29 '14 at 13:06