I have a module that controls the AGC. 2 files - agc.c and agc.h. The AGC period is given in agc.c : const uint16_t AgcPeriod = 10;

Now it is needed by other modules, but if I transfer it to agc.h , then my builder (Keil 4.53) will give an error: "the name is defined in many object files".

The only way out is to replace the variable with the macro: #define AgcPeriod 10 .

But at the same time, type control disappears and this variable becomes an int , not uint16_t .

It is also possible to write extern const AgcPeriod; in each module that needs this constant extern const AgcPeriod; and refer to it as external, but then I cannot define constants based on this in this module. That is, I can not write: const uint16_t myConst = AgcPeriod + 1; .

Is there a solution to this problem?

  • C language. Does C ++ have its own solution for this? - Reffum
  • one
    Let's leave both labels, I wonder how this is solved in C and in C ++. - Reffum

3 answers 3

In C ++, it suffices to write const uint16_t AgcPeriod = 10; The global const object in C ++ has internal linkage. In C, there is no such rule, so the compiler needs to state its intentions explicitly:

 static const uint16_t AgcPeriod = 10; 

This option will work in C ++, but it is redundant. static assumed in C ++ by default.

  • const uint16_t AgcPeriod = 10; глобальный const объект в C++ имеет internal linkage const uint16_t AgcPeriod = 10; глобальный const объект в C++ имеет internal linkage - but if somewhere before it we write the line extern const uint16_t AgcPeriod; then it becomes global (in the symbol table, “type”? changes from r to R ). - avp
  • @avp, yes, linkage will become external. But this is from another opera. Never saw a constant with external linkage. At least in C ++ code - ixSci
  • And I generally always write them with #define (somehow everything is easier (yes you look in / usr / include / ...)) - avp
  1. To specify the type, use a cast directly in the macro or extend numeric literals:

     #define INT_CONST ((int)10) #define FLOAT_CONST ((float)3.14159) #define UNSIGNED_CONST (10u) #define LONG_DOUBLE_CONST (3.14159l) 

    However, here my VC2012 works strangely, for example for such code:

     const long long int CONST1 = 10; #define CONST2 ((long long int)10) #define CONST3 (10ll) void foo() { float f1 = CONST1; // выводится предупреждение float f2 = CONST2; // предупреждение не выводится float f3 = CONST3; // предупреждение не выводится } 

    Why is it so - a mystery to me, perhaps, other compilers will behave more consistently, and therefore led this decision.

  2. And the iron option to use the functions:

     static uint16_t AGC_PERIOD() {return 10;} 

    I apologize for the volume of the answer (because of the examples), but it is understandable (I hope that it is clear) that I could not write more shortly.

    Usually problems should not arise, although the use of external variables (constants) is a rather subtle point, especially for a mixture of modules (separate compilation) from C and C ++.

    Linux example g ++ and gcc

    Header file that is included in all modules:

     #ifndef _EX_H #define _EX_H #include <stdint.h> extern int64_t v1; extern int8_t v2; extern const uint16_t cv3; #ifndef USEPLUSPLUS // with -DUSEPLUSPLUS g++ makes C++ function names (like _Z6ex_priPKc) in .o #ifdef __cplusplus extern "C" { #endif #endif void ex_pri (const char *title); void ex1 (void); void ex2 (void); #ifndef USEPLUSPLUS #ifdef __cplusplus } #endif #endif #ifdef __cplusplus #define COMP "g++" #else #define COMP "gcc" #endif #endif 

    #ifdef around prototypes are needed only if you plan to use a mixture of modules compiled by gcc (C) and g ++ (C ++).

    Now 4 files with our functions:

     avp@avp-xub11:hashcode$ more ex.c ex1.c ex2.c ex_pri.c | cat :::::::::::::: ex.c :::::::::::::: #include <stdio.h> #include "ex.h" int main () { printf("main (%s): cv3 = %hu\n", COMP, cv3); ex_pri("start"); v1 = v2 = 10; ex_pri("mid"); ex1(); ex2(); ex_pri("finish"); return puts("End") == EOF; } :::::::::::::: ex1.c :::::::::::::: #include <stdio.h> #include "ex.h" void ex1 () { printf("ex1 (%s): change v1 = %lld to cv3 / 2 + 1\n", COMP, v1); v1 = cv3 / 2 + 1; } :::::::::::::: ex2.c :::::::::::::: #include <stdio.h> #include "ex.h" void ex2 () { printf("ex2 (%s): change v2 = %d to cv3 / 2 + 1\n", COMP, v2); v2 = cv3 / 2 + 1; } :::::::::::::: ex_pri.c :::::::::::::: #ifdef __cplusplus #include <iostream> #endif #include <stdio.h> #include "ex.h" int64_t v1 = 777666555444333222LL; int8_t v2 = -127; const uint16_t cv3 = 0xABCD; void ex_pri (const char *title) { printf ("%s\nex_pri (%s): v1 = %lld v2 = %d cv3 = 0x%04x\n", title ? title : "", COMP, v1, v2, cv3); #ifdef __cplusplus std::cout << "++++\n"; #endif } avp@avp-xub11:hashcode$ 

    in which, in every way, we manipulate external variables. By the way, pay attention: the definition of a variable (constant) should be one-time Here are all the definitions in the ex_pri.c file.

    And you can play with different compilation options.

     avp@avp-xub11:hashcode$ gcc -c ex1.c ex2.c ex_pri.c avp@avp-xub11:hashcode$ g++ ex.c ex1.o ex2.o ex_pri.o avp@avp-xub11:hashcode$ ./a.out main (g++): cv3 = 43981 start ex_pri (gcc): v1 = 777666555444333222 v2 = -127 cv3 = 0xabcd mid ex_pri (gcc): v1 = 10 v2 = 10 cv3 = 0xabcd ex1 (gcc): change v1 = 10 to cv3 / 2 + 1 ex2 (gcc): change v2 = 10 to cv3 / 2 + 1 finish ex_pri (gcc): v1 = 21991 v2 = -25 cv3 = 0xabcd End avp@avp-xub11:hashcode$ nm ex_pri.o 00000000 R cv3 00000000 T ex_pri U printf 00000000 D v1 00000008 D v2 avp@avp-xub11:hashcode$ g++ -c ex1.c ex2.c ex_pri.c avp@avp-xub11:hashcode$ g++ ex.c ex1.o ex2.o ex_pri.o avp@avp-xub11:hashcode$ ./a.out main (g++): cv3 = 43981 start ex_pri (g++): v1 = 777666555444333222 v2 = -127 cv3 = 0xabcd ++++ mid ex_pri (g++): v1 = 10 v2 = 10 cv3 = 0xabcd ++++ ex1 (g++): change v1 = 10 to cv3 / 2 + 1 ex2 (g++): change v2 = 10 to cv3 / 2 + 1 finish ex_pri (g++): v1 = 21991 v2 = -25 cv3 = 0xabcd ++++ End avp@avp-xub11:hashcode$ g++ -c ex1.c ex2.c ex_pri.c -DUSEPLUSPLUS avp@avp-xub11:hashcode$ nm ex_pri.o 00000000 R cv3 U __cxa_atexit U __dso_handle 000000b1 t _GLOBAL__sub_I_v1 U printf 00000000 D v1 00000008 D v2 00000072 t _Z41__static_initialization_and_destruction_0ii 00000000 T _Z6ex_priPKc U _ZNSt8ios_base4InitC1Ev U _ZNSt8ios_base4InitD1Ev U _ZSt4cout 00000000 b _ZStL8__ioinit U _ZStlsISt11char_traitsIcEERSt13basic_ostreamIcT_ES5_PKc avp@avp-xub11:hashcode$ g++ ex.c ex1.o ex2.o ex_pri.o /tmp/ccp5Fn5P.o: In function `main': ex.c:(.text+0x33): undefined reference to `ex_pri' ex.c:(.text+0x5a): undefined reference to `ex_pri' ex.c:(.text+0x5f): undefined reference to `ex1' ex.c:(.text+0x64): undefined reference to `ex2' ex.c:(.text+0x70): undefined reference to `ex_pri' collect2: error: ld returned 1 exit status 

    You see, here the compiler (g ++) expects the names of functions in the “sishny” format, and we have already done .o with C ++ function names (this is defined by extern "C" ... in the header file). Therefore, we call:

     avp@avp-xub11:hashcode$ g++ ex.c ex1.o ex2.o ex_pri.o -DUSEPLUSPLUS avp@avp-xub11:hashcode$ ./a.out main (g++): cv3 = 43981 start ex_pri (g++): v1 = 777666555444333222 v2 = -127 cv3 = 0xabcd ++++ mid ex_pri (g++): v1 = 10 v2 = 10 cv3 = 0xabcd ++++ ex1 (g++): change v1 = 10 to cv3 / 2 + 1 ex2 (g++): change v2 = 10 to cv3 / 2 + 1 finish ex_pri (g++): v1 = 21991 v2 = -25 cv3 = 0xabcd ++++ End avp@avp-xub11:hashcode$ 

    Well, and so on.

    • one
      #if defined (__ cplusplus) &&! defined (USEPLUSPLUS) - dsnk