3

I ran into this problem debugging an AVR microcontroller: I have a main.c file with numerous variable definitions, amongst them an array of structs, like this:

struct mystruct mystruct_array[COUNT];

In another .c file I refer to this array as external, but I left away the array brackets and size so I wouldn't repeat myself and just declared the variable as a pointer (because arrays are essentially pointers, aren't they?):

extern struct mystruct *mystruct_array;

But when I checked the address of the array using printf("%p\n", mystruct_array);I got a null pointer instead of the array's location in memory. Also if I would access the subsequent items in the array, like printf("%p\n", &(mystruct_array[n])); it would print address 0 plus n times sizeof(struct mystruct).

Only after I changed the definition to

extern struct mystruct mystruct_array[COUNT];

(exactly the same as in main.c), I got the true address of the array.

My question: Why does this make a difference to the compiler (in my case avr-gcc)?

Jonathan Leffler
  • 666,971
  • 126
  • 813
  • 1,185
Joe Zocker
  • 195
  • 8
  • 3
    Arrays are not pointers. Pointers are not arrays. Spend a weekend with a coffee at a lake thinking about that, and the rest of your life will be golden. – Kerrek SB Apr 12 '15 at 13:51
  • @Kerrek SB: Sorry, I just forgot that when I changed my specific code to this more general example. I meant `mystruct_array`. – Joe Zocker Apr 12 '15 at 15:44
  • 1
    OK, no worries. Something to keep in mind for future questions: Re-read before posting :-) – Kerrek SB Apr 12 '15 at 15:53
  • 1
    You could reasonably have declared `extern struct mystruct mystruct_array[];` if it was inconvenient to have the COUNT exposed outside the file where the structure is defined. Of course, avoiding globals is usually a good idea. Indeed, the `extern` declaration should be in a header that is included both in `main.c` and the `other.c` file. This gives you the correct cross-checking on types. See also [How do I use `extern` to share variables between source files in C?](http://stackoverflow.com/questions/1433204/how-do-i-use-extern-to-share-variables-between-source-files-in-c) – Jonathan Leffler Apr 12 '15 at 16:04

3 Answers3

8

That's a fun one.

When you write :

struct mystruct mystruct_array[COUNT];

You create a global array of mystruct structs, there are COUNT of them, and since you didn't initialize it, it'll be filled with zeros.

When you then write:

extern struct mystruct *mystruct_array;

You tell the compiler that there's a variable called mystruct_array somewhere and that it's a pointer. But it's not, it's an array. So the compiler is going to read the content of the array as if it were a pointer.

So when you try to output the value of that pointer, the compiler goes fetch mystruct_array in memory and outputs its content as if it were a pointer. Since it's actually an array full of zeros, you're seeing a null pointer in the output.

Instead you should write something like:

extern struct mystruct mystruct_array[];

So the compiler knows the correct type of your variable. You can specify the length in the square brackets here, but you don't have to.

I'd recommend you go read up on the differences between pointers and arrays to make sure you don't confuse them in the future.

Community
  • 1
  • 1
tux3
  • 6,780
  • 6
  • 40
  • 47
  • 1
    In summary, the program is ill-formed because it declares the same name twice with different types? – Kerrek SB Apr 12 '15 at 14:21
  • 1
    [Yes](http://stackoverflow.com/questions/14697698/what-is-the-behavior-when-there-are-mismatched-types-between-an-extern-declarati) – tux3 Apr 12 '15 at 14:24
  • @JoeZocker Basically an array is just a chunk of data in memory. A pointer is a number that tells you where to find that chunk of data. The part that confuse a lot of people is that arrays can decay into pointers. I can't really write you a full tutorial in a comment, but make sure the type in the "extern ..." matches the type of the definition, and you'll be fine. – tux3 Apr 12 '15 at 15:58
  • When I first declare the array as `struct mystruct mystruct_array[]` I can also later refer to the address of the first element simply as `mystruct_array`. If I wanted the content (the zeros in that case), I would dereference that pointer (`*mystruct_array` == `mystruct_array[0]`). So why is the array notation different from the pointer notation? – Joe Zocker Apr 12 '15 at 16:00
  • "I can also later refer to the address of the first element simply as `mystruct_array`" – tux3 Apr 12 '15 at 16:04
  • More about array decay, because I know that it can be a bit hard to grasp : http://stackoverflow.com/questions/1461432/what-is-array-decaying – tux3 Apr 12 '15 at 16:15
  • It's a fun one - yeah. And shows a usage for declaring array of unknown bound. – AnArrayOfFunctions Apr 12 '15 at 17:15
  • @JoeZocker Array to pointer first element conversion is basically done when 'array' properties can't be used in expression. This is mostly the case as they're very limited: 1) when used in 'sizeof' operator 2) when it's address is taken with '&' (which type is like 'struct (*mystruct)[]') – AnArrayOfFunctions Apr 12 '15 at 17:19
  • Okay, thanks for your answers so far. I found [this](http://stackoverflow.com/a/1462352/4779563) post helpful as well. I just did some quick tests and discovered that, having an array `char a[N]`, both `a` and `&a` evaluate to the base address of the array. But the difference is that `&a` is of type `char (*)[N]. – Joe Zocker Apr 12 '15 at 20:13
2

The ideal way to do this is to put an extern declaration in a header file, and a definition in exactly one file. For example,

header.h

extern struct mystruct mystruct_array[];
/* or extern struct mystruct mystruct_array[COUNT] */

main.c

#include "header.h"

#define COUNT 10
/* should have an initializer to be a definition: */
struct mystruct mystruct_array[COUNT] = { 0 };

/* ... */

other.c

#include "header.h"

/* ... */
    printf("%p\n", mystruct_array);

That saves you repetition, and limits the places where you might need to make changes. Note that if your header does not define the number of elements in the array, then you cannot apply the sizeof operation to that array in files other than the one that also provides the array definition.

Note, too, that although arrays are not pointers, in most contexts in C source code, an array name is converted to a pointer to the first element of the array. This is the source of the misapprehension that arrays are pointers. Although they are not pointers, in many ways they act as if they were.

John Bollinger
  • 121,924
  • 8
  • 64
  • 118
0

The declaration of extern means that the declared object is global in an other C file. Then, when you generate the object file, it is generated also if the declared object (in this case the structure) is not present.

If you declare:

extern struct structname * s;

means that in the other C module there's a global visible pointer to a structure structname s.

If you declare:

extern struct structname s;

means that in the other C module there's a global visible structure structname s!

When you will link the program, if you don't indicate to the linker to link the object that contains the structure, you will get an Undefined reference!

Sir Jo Black
  • 1,800
  • 1
  • 13
  • 20
  • 1
    'Tis curious that you've not covered the case of the array, which the question is about. – Jonathan Leffler Apr 12 '15 at 16:05
  • I don't deny that I have always preferred to reference it as pointers and before to explain how to declare them as array I should have to try :p ... :) But because the name of an array is a pointer ... it's the same!!! – Sir Jo Black Apr 12 '15 at 16:25
  • I'm very interested in the use of AVR! I'm started to use them and I feel it powerfull! ... Now I'm evaluating the 8 bit MCU: tiny 85/84, mega 168/328. I hope they may becomes my new job! – Sir Jo Black Apr 12 '15 at 16:32