1

Ok before I go explaining my problem, note that I have seen the following phenomenon happen in the code that I work on in my company, hence I can't share actual code, instead I will giving some snippets of pseudo C code to explain my problem.

Lets say file1.h contains the following lines of code:

#ifndef FILE1_H
#define FILE1_H

struct data {
    int a;
};

struct data foo;
void run(void);
#endif

Now I include this header in another file in the following fashion-:

#include "file1.h"

void run() {
    int count = 0;
    while(1) {
        foo.a = count;
        count = (count + 1) % 500;
    }
}

Now in the main file I create a thread that points to the run() method and create a loop that reads from data.foo in this fashion -:

#include <unistd.h>
#include <stdio.h>
#include <pthread.h>
#include "file1.h"

void main(void) {
    pthread_create(&id, NULL, run, NULL);

    while(1) {
        printf("Data = %d\n",foo.a);
        sleep(1);
    }
}

On normal cases this should give a compiler error that there are multiple declarations of the same structure variable. And I have also tried making small sample programs like this - they all fail at compile like they should.

But I have actually seen and worked on code like this that works fine.

In one file I have the variable to which I write data, and in another file I just read the data from it.

Is there any situation that the C compiler allows something like this? Any compiler flags in GCC? I usually build using Makefiles so any obscure compiler flag or switch that allows this behavior?

Or is there a lapse in my understanding of C?

Please excuse other compile-time errors in my code, since this is pseudo code to explain my problem.

Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
ng.newbie
  • 2,807
  • 3
  • 23
  • 57
  • See [How do I use `extern` to share variables between files?](https://stackoverflow.com/questions/1433204). This is a 'common extension' with a double entendre on 'common' — it is a feature found commonly in compilers in the wild, and it is based on Fortran COMMON blocks. It is recognized by the C standard as an extension — in [C11 Annex J.5.11](https://port70.net/~nsz/c/c11/n1570.html#J.5.11). Unix systems tend to support the extension. – Jonathan Leffler Apr 11 '18 at 15:33
  • @JonathanLeffler I have seen `extern` used in my code, is it automatically applied? – ng.newbie Apr 11 '18 at 15:35
  • 1
    Yes, no, maybe (to the question "is `extern` automatically applied"). According to the strict rules of the standard, the variable specified in the header is defined in each source file that includes the header, and so the header can only be used in one file. However, the Unix real-world compilers are more lax. When there is no initialization, then you can often get away with such double definitions. The keyword `extern` is needed before variable declarations to ensure they are not treated as definitions. You _should_ have one definition of the variable and as many declarations as necessary. – Jonathan Leffler Apr 11 '18 at 15:39
  • 1
    In the context of function declarations, `extern` is applied implicitly if it is omitted. That is, `extern void *malloc(size_t size);` is equivalent to `void *malloc(size_t size);` because it is a function declaration. So in that context, you could say "`extern` is applied automatically". With variables, there is no such special treatment — you have to use `extern` explicitly to ensure it is a declaration and not a definition. But the J.5.11 common extension means you can get away with being sloppy far too often. – Jonathan Leffler Apr 11 '18 at 15:41
  • @JonathanLeffler I asked this question because I seem to getting the same behavior without `extern`, how is that a duplicate? And that too on the same compiler. – ng.newbie Apr 11 '18 at 15:51
  • The main answer (my answer) to the duplicate question covers the points raised in this question in detail. The answer to this question is "you use `extern` to share variables between files reliably", and "this code often works but is not guaranteed to do so by the standard — but the standard does have a note that recognizes that it does often work, despite being in contravention of the letter of the standard". That means that the (answer to the) other question covers the same ground as this one. – Jonathan Leffler Apr 11 '18 at 15:54
  • @JonathanLeffler I am sorry but what do you mean by it often works? How can it work sometimes and not work the other times? that does not make sense. – ng.newbie Apr 11 '18 at 15:56
  • Some compilers support the mechanism that makes it work. Other compilers do not support the mechanism that makes it work. If your code only has to work with one compiler on one platform, and that compiler supports the mechanism, then "it works". But another compiler on the same platform (or more usually on another platform) can treat the code as erroneous. The second compiler is more strictly correct according to the standard, but the standard does recognize that there are real implementations like the first compiler. If you want your code to be maximally portable, don't rely on the extension. – Jonathan Leffler Apr 11 '18 at 15:59
  • @JonathanLeffler Yes but thats my confusion - I have seen my small code snippet actually fail on the same compiler that allows a large program doing this - how can two compiler support two different things? – ng.newbie Apr 11 '18 at 16:01
  • Very easily. Welcome to the real world. Compilers are not perfect. Actually, compilers are rarely buggy — they don't survive if they are. But different compilers are allowed to interpret the standard differently. The standard allows that. If you tread outside what is guaranteed by the standard, all bets are off. You may be OK in the short term; you may have problems in the long term. Or you may be OK in the long term. There are ways to ensure that the code breaks — you've stated that you've simulated your corporate code, not copied it. Stuff in other files could affect the results. – Jonathan Leffler Apr 11 '18 at 16:04
  • Yes — I work on an antique code base (the earliest files date back to 1982). Yes, we have problems with common definitions. Empty source files that include certain headers can end up with 6 variables defined in common. It's a pain. C++ is more rigorous about this; we have to tell the C++ compiler to be more relaxed because of these infelicities. (The code base is primarily C — but there are some chunks of C++ in it. Consequently, it has to be linked with the C++ compiler.) Grumble! – Jonathan Leffler Apr 11 '18 at 16:07
  • @JonathanLeffler Yes of course, I tried to target the issue in an isolated fashion. Then it fails. But when I compile the application as a whole I don't see compiler errors. That's so weird. So you are telling me that there is no way understand why this is happening since the compiler may be taking liberties, am I correct? – ng.newbie Apr 11 '18 at 16:09
  • No; I'm telling you that your reduced code isn't actually representative of what is going on in the big code. The compiler will be self-consistent. Somewhere along the line, you're doing something different between the 'real code' and the 'example code' in the question if the one works and the other fails. But we don't have enough context here to help you deduce what's the key difference. Your code is invoking undefined behaviour by passing a `void (*function)(void)` function pointer to a function that expects a `void *(*thread_function)(void *)`. That might be the trouble; it probably isn't. – Jonathan Leffler Apr 11 '18 at 16:14

0 Answers0