0

dummy.h

#ifndef dummy_h
#define dummy_h

extern const int dummy;

#endif

dummy.c

#include "dummy.h"

const int a = 384;        //I modify the question to reflect that 
const int b = 1;          //dummy is the addition of a and b. I cannot
const int dummy = a + b;  //change a and b. Have to stay like this.

//const int dummy = 385; //define dummy in dummy.c to avoid duplicated symbol issue.

foo.h

#ifndef foo_h
#define foo_h

#include "dummy.h"

extern const int foo;

#endif

foo.c

#include "foo.h"

const int foo = dummy + 1; //<--- The problem is that dummy is not a compile-time constant 

main.c

#include <stdio.h>
#include "foo.h"

int main() {
  
  printf("Hello World. foo = %d\n", foo);

  return 0;
}

makefile

all:
    cc main.c dummy.c foo.c -o dummy

clean:
    rm dummy

This is a snapshot of a much larger program that I am currently working on. I understand why I can't use dummy in foo.c. However, I am not sure what will be the more elegant solution to it.

Basically, I want to define dummy variable in dummy.c and foo in foo.c, and at the same time, I need to find a way to initialize foo with dummy in foo.c.

Wonder if this can be done?

Tony
  • 111
  • 5
  • What will be the example of a constant integer vs an integer constant? – Tony Jun 02 '22 at 23:36
  • See the C standard [§6.6 Constant expressions](http://port70.net/~nsz/c/c11/n1570.html#6.6) and in particular [¶6](http://port70.net/~nsz/c/c11/n1570.html#6.6p6) which specifies what an integer constant (expression) is. A constant integer is a `const`-qualified integer; it is not an integer constant. – Jonathan Leffler Jun 02 '22 at 23:44

1 Answers1

1

It can't be done directly because dummy is only a constant integer and not an integer constant, and only integer constants can be used in initializers for global variables.

Option 1

You can do it indirectly with a macro (or enumeration constant) defining the value used to initialize dummy and also then foo:

In dummy.h

enum { DUMMY_INITIALIZER = 385 };

In dummy.c

const int dummy = DUMMY_INITIALIZER;

In foo.c

const int foo = DUMMY_INITIALIZER + 1;

I'm assuming there's a good reason why you need foo and shouldn't write dummy + 1 everywhere you use foo. One good reason would be that you write foo a lot (more than once?).

Option 2

A question, suggested by jamesdlin in a comment:

Do you really need const int 'variables'?

(Are constant variables really variables?) Anyway, you could also solve the problem just using enumeration constants:

In dummy.h

enum { DUMMY = 385 };

In dummy.c, do not attempt to define dummy; use DUMMY instead.

In foo.h

enum { FOO = DUMMY + 1 };

And in main.c, use FOO instead of foo. Since the enumeration values are compile-time integer constants, you don't have the problems distinguishing between integer constants and constant integers.

See also "static const" vs "#define" vs "enum"?. If you do need to pass pointers to constant integers to functions, you can define an appropriate variable locally:

void one_function(void)
{
    const int foo = FOO;
    …
    some_function(&foo);
    …
}
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • That's right. The variable foo needs to be used in many other places. – Tony Jun 02 '22 at 23:38
  • Think of it as OOP in C, and the dummy variable is an attribute of "the dummy object". So what you suggest is to have an initializer macro for every attribute of a C "object"? – Tony Jun 02 '22 at 23:44
  • No — only those you need to repeat. If you don't need to repeat the value, you don't need to name it. Generally, `0` and `1` don't need to be named either. Using `sizeof` often avoids repetition. For example, `char buffer[2048]; while (fgets(buffer, sizeof(buffer), stdin) != 0) { some_function(buffer, sizeof(buffer)); }` avoids repeating `2048`, which means you can change the size of the buffer without having to change the other code. – Jonathan Leffler Jun 02 '22 at 23:49
  • I see why the enum initializer works. However, what if the dummy is the result of the addition of another integer constant of a and b? For example, const int a = 1, and const int b = 2, and I define dummy as "const int dummy = a + b"? – Tony Jun 02 '22 at 23:50
  • Then you need `enum { A_INITIALIZER = 1, B_INITIALIZER = 2 };` and `const int dummy = A_INITIALIZER + B_INITIALIZER;`. And you need to rethink your design. Or you need to switch to C++ where the rules are different — in multiple ways. Variables such as `const int dummy = 385;` are not global variables in C++ — but you could write that into a header and use the name in source files that include the header. – Jonathan Leffler Jun 02 '22 at 23:58
  • I wish I could switch to C++. Is not up to me. Plus the project is too big. So the only way in C to initialize a constant variable from another constant variable defined in a separated .c file is to use an initializer macro, am I right? – Tony Jun 03 '22 at 00:06
  • 1
    @Tony If you want compile-time constant integers, [you usually should use `enum`s](https://stackoverflow.com/q/1674032/). – jamesdlin Jun 03 '22 at 00:16
  • Please see the modified question. – Tony Jun 03 '22 at 00:42
  • Please don't make the question into a chameleon question. It's not polite to invalidate an answer simply because you didn't ask the question you really needed to ask in the first place. If you can't change some code, you're stuck. And your revised `dummy.c` shouldn't compile — though I'd not be surprised to find that GCC (and Clang emulating GCC) allows it. – Jonathan Leffler Jun 03 '22 at 00:43
  • Understood. Tried to simplify the question and didn't think clearly before asking. Not trying to be rude. – Tony Jun 03 '22 at 00:46
  • With tangential changes (declaring `a` and `b` in `dummy.h`), the code in the revised `dummy.c` compiles with Clang (Apple clang version 13.0.0 (clang-1300.0.29.30)) even set very fussy — `clang -Weverything -Wall -Wextra -Werror -std=c11 -pedantic -pedantic-errors -c dummy.c` (though it complains about `/usr/include/local` being unsafe for cross-compilation `[-Werror,-Wpoison-system-directories]`). So, apparently, within a file, the compilers think you're OK. I'm not sure that the C standard permits it, but I can see why the compilers want to allow it. – Jonathan Leffler Jun 03 '22 at 02:05
  • The problem is when foo.c is included in the compilation. This will generate "error: initializer element is not a compile-time constant". – Tony Jun 03 '22 at 02:14
  • The compiler is correct — and I think it should say the same about `dummy` in `dummy.c`, but empirically, it doesn't. You need to use enumerations (or macros) somewhere along the line. If you can't change the code, you'll have to repeat yourself. Or live without the `const` qualifier and arrange to initialize `foo` before it's used — which also involves non-negligible code changes. These options are both minor variations on the theme "it can't be done directly". – Jonathan Leffler Jun 03 '22 at 02:19