-4

In my .h file I have

extern int a[4];

in my .c file I have

int a[10];

So are there any issues with this?

Declaration and definition size matters? Not right?

If I write sizeof(a) in one of the files, what will be the output? Is this undefined behavior?

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
Gopi
  • 19,784
  • 4
  • 24
  • 36
  • 2
    They must match. Why seek trouble? – n. m. could be an AI Jul 17 '15 at 08:29
  • 1
    @n.m. The question is what if they don't match? compiler is happy with it.. But memory allocated is 10 * sizeof(int) so sizeof(a) should be 10 * sizeof(int) or sizeof(a) – Gopi Jul 17 '15 at 08:33
  • 1
    Why don't you just try it? Seems it doesn't even compile: `conflicting types for 'a'` – musefan Jul 17 '15 at 08:33
  • 1
    And who are you getting to upvote it for you? This question shows no research effort – musefan Jul 17 '15 at 08:34
  • You did an obviously bad thing. Why not just fix the bad thing - it's a trivial edit? – Martin James Jul 17 '15 at 08:48
  • @musefan Time matters.. I wanted this info very quickly and YES i didn't had time to test this or take a look at C manual so wanted to check with experts here.. I don't care about the down/up votes on this – Gopi Jul 17 '15 at 09:04
  • 3
    @Gopi: It's quicker to test than it is to wait for answers. If you don't have your IDE to hand, then use an online one like I did.. Put it this way: I was able to test it online quicker than anyone could answer, and you had the head start before you started writing the question. And how can you possibly need the answer so quickly yet you are not in any position to code anyway? – musefan Jul 17 '15 at 09:15
  • @musefan I felt this is the fastest way to get the answer. Say the problem was I didn't know this leads to UB or not. As you know the side-effect of UB is we get expected results..:) so I took this way. – Gopi Jul 17 '15 at 09:21
  • @Gopi : you are on your own, but I would advise you to fix that as soon as possible, or at least to document it (and the reason why) in red flashing font if you do not want that future maintainers will kick after you ;-) - even if I find it an interesting question in an educational point of view – Serge Ballesta Jul 17 '15 at 10:58
  • They don't have a choice. In a C program they must match. If you have something that isn't a valid C program, anything could happen. Perhaps your ears will explode. – n. m. could be an AI Jul 17 '15 at 11:21

3 Answers3

7

If you include your header file in your source file the two declarations of a must have the same type as C says:

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

Even if the two declarations are in two translation units, they need to have the same type:

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

ouah
  • 142,963
  • 15
  • 272
  • 331
  • I'm intrigued by the use of "compatible" here. Are there types that would not be the same, but still fit this ? Different cv-qualifications, maybe ? – Quentin Jul 17 '15 at 09:01
  • @Quentin type compatibility is essentially "types are the same" + some other cases. Types with different qualifiers are not compatible: e. g., `extern const int x;` and `int x = 42;` is undefined behavior. – ouah Jul 17 '15 at 09:07
6

Looks like so:

extern int a[4];
int a[10];

int main()
{
    return 0;
}

gcc reports conflicting types for a:

cc -Wall -g -ggdb -pipe -pedantic -std=gnu99    test.c   -o test
test.c:2:5: error: conflicting types for ‘a’
int a[10];
     ^
test.c:1:12: note: previous declaration of ‘a’ was here
extern int a[4];
            ^
dragosht
  • 3,237
  • 2
  • 23
  • 32
  • 3
    Plus 1 for doing what the OP should have done themselves before posting the question – musefan Jul 17 '15 at 08:37
  • 2
    @musefan if the .h is not included in the .c but in another .c you won't get error even if it is undefined behavior. – ouah Jul 17 '15 at 08:51
  • @ouah you are perfectly right (see my answer for a working example), but it looks like C programmers do not like such jokes :-( – Serge Ballesta Jul 17 '15 at 09:16
0

It is formally Undefined Behaviour as said by @ouah and is error prone so it should never exists in production code.

But it will be accepted with correct result but most (if not all) common compilers (gcc, clang, msvc do)

If you include the .h file containing extern int a[4]; in the .c containing int a[10]; you will get an error because you are redefining a to a different type (as others have already said).

If you only include the .h in other compilation units, linker should ignore the size and link it correctly.

Simply you will get sizeof(a) == 10 * sizeof(int) in the .c where it is defined and sizeof(a) == 4 * sizeof(int) in the other compilation units that include the .declaring it.

Working example :

foo.c :

#include <stdio.h>

int a[10];

void display();

int main() {
    for(int i=0; i<sizeof(a)/sizeof(a[0]); i++) {
        a[i] = i;
    }
    printf("sizeof(a)=%d\n", sizeof(a));
    display();
    return 0;
}

foo2.c :

#include <stdio.h>

extern int a[4];

void display() {
    printf("sizeof(a)=%d\n", sizeof(a));
    for(int i=0; i<sizeof(a)/sizeof(a[0]); i++) {
        printf(" %2d", a[i]);
    }
    fputs("\n", stdout);
}

Compilation + link : cc foo.c foo2.c -o foo : not even a warning

Execution :

sizeof(a)=40
sizeof(a)=16
  0  1  2  3

This was commonly used in commons in fortran where a compilation unit could only declare the beginning of a common, but I cannot imagine a real use case for such a horror in C.


The reason why it works

Compilers cannot detect at compile time that there are declarations with incompatible types in same program because they are in different translation units so are processed but different compilation phases - possibly at different times.

At link time, linker only sees the addresses of the different declarations of a and make sure that all .o (or .obj) get same address. It would be hard to do differently without breaking multi-language compatibility : it is the way for sharing an array between a C module and an assembly language one.

Why you should not use it

You could say that nothing prevents a compiler to do the write thing when facing what standard defines as undefined behaviour. But Hans Passant once gave me a link to an article on research for future compilers. Here are some extracts :

This article is about a new memory-safe interpretation of the C abstract machine that provides stronger protection to benefit security and debugging ... [Writers] demonstrate that it is possible for a memory-safe implementation of C to support not just the C abstract machine as specified, but a broader interpretation that is still compatible with existing code. By enforcing the model in hardware, our implementation provides memory safety that can be used to provide high-level security properties for C ...

[Implementation] memory capabilities are represented as the triplet (base, bound, permissions), which is loosely packed into a 256-bit value. Here base provides an offset into a virtual address region, and bound limits the size of the region accessed ... Special capability load and store instructions allow capabilities to be spilled to the stack or stored in data structures, just like pointers ... with the caveat that pointer subtraction is not allowed.

The addition of permissions allows capabilities to be tokens granting certain rights to the referenced memory. For example, a memory capability may have permissions to read data and capabilities, but not to write them (or just to write data but not capabilities). Attempting any of the operations that is not permitted will cause a trap.

[The] results confirm that it is possible to retain the strong semantics of a capability-system memory model (which provides non-bypassable memory protection) without sacrificing the advantages of a low-level language.

(emphasize mine)

TL/DR : Nothing prevents future compilers to add size information for arrays inside the object (compiled) module and raise an error if they were not compatible. Researches currently exists for such features

Community
  • 1
  • 1
Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • I didn't downvote but starting your answer by saying *it is harmless* is not good. First it is undefined behavior and moreover you correctly showed that `sizeof` will yield different results which can lead to nasty bugs. – ouah Jul 17 '15 at 09:29
  • 1
    *it's not well defined*: see my answers and the two quotes from the Standard. The first quote is a "shall" and violation of a shall outside a constraint is undefined behavior and the second quote explicitly says it is undefined behavior. – ouah Jul 17 '15 at 13:16
  • @ouah : I've changed my answer. Thanks to your remarks it now should be much better for future references. – Serge Ballesta Jul 18 '15 at 07:33