7

I was experimenting with GCC and found out that you can declare external variables const in header files but keep them mutable in implementation files.

EDIT: This does actually not work. The only reason I got my test code to compile was because I did not include "header.h" in "header.c".

header.h:

#ifndef HEADER_H_
#define HEADER_H_

extern const int global_variable;

#endif

header.c:

int global_variable = 17;

This seems like a very good feature to use for keeping global_variable readonly to the users of header.h but keeping them modifable by the implementation (header.c).

NOTE: The following code is just an example on how this way of declaring will prevent assignment to global_variable.

#include "header.h"

int main(void)
{
    global_variable = 34; /* This is an error as `global_variable` is declared const */
    return 0;
}

Because I have never seen technique in practise before. I start to wonder if it is valid.

Is this well defined behaivor or is this an error that GCC fails to warn me about?

wefwefa3
  • 3,872
  • 2
  • 29
  • 51
  • What is the exact wording of the error (copy and paste it)? – Fiddling Bits Dec 31 '14 at 17:44
  • 1
    I would find it confusing that a const variable could change at all. I would limit the scope with static and provide a function to return the current global value to get the same effect. – ryanpattison Dec 31 '14 at 18:16
  • 1
    The only way this can work is if header.h is **not** included in header.c. If one includes header.h into header.c, then the assignment **will** give a syntax error. If this is done in a real program with 1000s of lines of code and something related goes wrong - how easy will it be to fix? – Dirk Koopman Dec 31 '14 at 18:27
  • @OP Does `header.c` include `header.h` and does `header.c` compile without error/warning? – chux - Reinstate Monica Dec 31 '14 at 18:32
  • @DirkKoopman It cannot work EVEN if header.h is not included in header.c. You have two declarations that refer to the same object that have incompatible types. See may answer. – ouah Dec 31 '14 at 18:33
  • The point of using `const` in C (not necessarily C++) is to determine where that variable is stored (if it isn't optimised away). The `const` modifier says "please put this in a read-only segment". That doesn't mean it will or even that the concept applies, but you are trying to second guess the compiler and that is never a good idea. What happens if the compiler optimises it away. – Dirk Koopman Dec 31 '14 at 18:37
  • 2
    @ouah Unfortunately it **does** work in the trivial program above iff you don't include `header.h` in `header.c`. A simple `printf()` in `main()` will show you that. But I agree, just because it "works" doesn't make it a good idea. – Dirk Koopman Dec 31 '14 at 18:40
  • @DirkKoopman by *it cannot work*, I meant it is UB. – ouah Dec 31 '14 at 18:42
  • @ouah Sorry, I am new on here (but not to C programming). What does UB mean? – Dirk Koopman Dec 31 '14 at 18:45
  • @DirkKoopman undefined behavior – ouah Dec 31 '14 at 18:48
  • UB is behavior not defined by the spec. Like what happens with `1/0` – chux - Reinstate Monica Dec 31 '14 at 18:48
  • Sorry for the confusion my code example (with `main`) caused. I meant that this way of declaring `global_variable` prevents assignment to it. – wefwefa3 Dec 31 '14 at 19:12

3 Answers3

6

const int and int are not compatible types.

For example this:

extern const int a;

int a = 2;

is not valid in C as C says that:

(C11, 6.7p4) "All declarations in the same scope that refer to the same object or function shall specify compatible types"

In your case they are not in the same scope (different translation units) but C also says that:

(C11, 6.2.7p2) "All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined."

As you are violating the rule above, you are invoking undefined behavior.

Note that C90 has the same paragraphs.

ouah
  • 142,963
  • 15
  • 272
  • 331
  • I think OP is _counting_ on it not compiling. If code does not compile, there is no UB. – chux - Reinstate Monica Dec 31 '14 at 18:13
  • @chux I don't get your point, could you elaborate? I think OP will compile and link `header.c` and `main.c` together (otherwise you have no definition for the object). – ouah Dec 31 '14 at 18:14
  • NMDV, but OP appears to know that `main.c` code did not compile. The real question is can a non-const variable be declared as non-const in one module and const in another? – chux - Reinstate Monica Dec 31 '14 at 18:17
  • @chux main.c does not compile because the object is const. For the real question, it is my attempt to answer it: answer is no, as there are two declarations with incompatible types. – ouah Dec 31 '14 at 18:19
  • @chux How can the same variable have different class . The point is once the `.c` file include the header than the variable visible to it is `const int` What is the confusion here? If global_variable is there in some other module which has a different class than `const` than that is a different instance altogether – Gopi Dec 31 '14 at 18:22
  • @ouah The first scenario which you explain should be an error . There are 2 variables in the same scope with different class – Gopi Dec 31 '14 at 18:25
  • @Gopi Your comment makes sense, but if `header.c` does not include `header.h`, or at least that portion of `header.h` that declares `extern const int global_variable`, is code valid? – chux - Reinstate Monica Dec 31 '14 at 18:26
  • @Gopi I wrote in my answer it is invalid. My answer for OP scenario is the second part of the answer. – ouah Dec 31 '14 at 18:26
  • @chux If `header.c` doesn't include `header.h` that it doesn't know about `const int global_variable` so now it can have its own instance of `global_variable` and let's say this is global . So this variable is not a constant in `header.c` So to answer your question yes the code is valid That's why I have mentioned in my answer there should be just a single definition of the `extern` variable – Gopi Dec 31 '14 at 18:29
  • 1
    @Gopi Certainly there is only 1 `global_variable` allowed in global space. I think OP's question simplified is: "Is it valid if some files see this as `const int global_variable` and others as `int global_variable`?" – chux - Reinstate Monica Dec 31 '14 at 18:36
  • @chux so could you explain why 6.2.7p2 does not answer this question for you? – ouah Dec 31 '14 at 18:37
  • @chux No way this can happen. – Gopi Dec 31 '14 at 18:48
  • @ouah 6.2.7p2 almost did it - needed to review "compatible type" (§6.7.3 10) which in turn needed to review "qualified version". It appears that this all applies to `const, volatile, restrict`. Nice answer. – chux - Reinstate Monica Dec 31 '14 at 19:09
2

A little late, but anyways.

I think this may work if you do something like this

in header.h:

#ifndef HEADER_H_
#define HEADER_H_

#ifndef HAS_GLOB_VAR
extern const int global_variable;
#endif

#endif

and if you need to include the header in the file that actually defines the variable (header.c) you do something like

#define HAS_GLOB_VAR
#include "header.h"

int global_variable = 17;

...

On the other files you just include the header and don't define HAS_GLOB_VAR.

hello_hell
  • 154
  • 1
  • 8
  • 3
    Hmm, This is UB per (C11, 6.2.7p2) "All declarations that refer to the same object or function shall have compatible type; otherwise, the behavior is undefined." – chux - Reinstate Monica Mar 17 '17 at 17:04
0

You are not assigning a value to global_variable, you are defining it.

user3528438
  • 2,737
  • 2
  • 23
  • 42