2

I'm using a Keil C51 compiler to program a 8051 microcontroller. For some reason my code didn't run - I managed to track down the bug, but I still have difficulties understanding it. Why is the first code wrong, comparing to the other one? It's worth noting that the compiler didn't throw any error, the code just didn't even start on the microcontroller.

Wrong code:

file1.h

extern STRUCT_TYPEDEF array_var[];

file2.c

// Global variable initialization
STRUCT_TYPEDEF array_var[] = some_struct.array2_var;

After changing these to:

file1.h

extern STRUCT_TYPEDEF *array_var;

file2.c

// Global variable initialization
STRUCT_TYPEDEF *array_var = &some_struct.array2_var[0];

it started working.

Also, this portion of code was referenced only in functions like "array_var[0].property = ...", but none of these functions were ever called from the application.

some_struct variable is declared in yet another module.

Why could it behave like that? Is there some difference between [] and * I don't know about?

EDIT1: It is said that pointers and arrays are different things... but then, how does the "[]" syntax differ from "*"? I thought compiler would just convert it to a pointer in case the square brackets are empty (like it does with the function arguments). I also thought providing an array would result in giving me the address of the first element.

Now, everyone is saying pointers and arrays are different - but I can't find any information about what exactly is different in them. How does compiler see it when I give an array as rvalue instead of a pointer to its first element?

Suryu
  • 25
  • 1
  • 9
  • declaration vs definition. – t0mm13b May 19 '16 at 17:22
  • 3
    `STRUCT_TYPEDEF array_var[] = some_struct.array2_var;` is not valid C. It's hard to tell why your compiler has accepted it. It is not clear what is your question about the difference between `[]` and `*`. One is for arrays, the other is for pointers. Pointers are different from, and not interchangeable with, arrays. – n. m. could be an AI May 19 '16 at 17:22
  • "*my code didn't compile ... It's worth noting that the compiler didn't throw any error*" So did it compile or not, and if not what was the error? – dbush May 19 '16 at 17:24
  • @n.m. For completeness: http://en.cppreference.com/w/c/language/type#Incomplete_types The declaration can not be completed by anything but assigning a constant initializer. – Ext3h May 19 '16 at 17:28
  • Array initializers are enclosed in braces. In the first code, your attempt at initialization is not enclosed in braces; it is syntactically invalid. In the second case, you're initializing a pointer instead of an array; the braces are not mandatory there (they're even aconventional, though they are permitted). – Jonathan Leffler May 19 '16 at 17:28
  • Amongst probably many other questions, see [char array vs char pointer in C](https://stackoverflow.com/questions/10186765/char-array-vs-char-pointer-in-c). It, in turn, has links to other questions. – Jonathan Leffler May 19 '16 at 17:33
  • @dbush Sorry, I meant it didn't run - it actually compiled. – Suryu May 19 '16 at 17:38
  • I suspect some kind of a linker symbol table problem in which the microcontroller loader is attempting to load in the image and the linker information is messing with its head when you are using the array declaration. However the pointer is just fine with a known size. – Richard Chambers May 19 '16 at 17:45
  • There's also [What are the barriers to understanding pointers and what can be done to overcome them?](https://stackoverflow.com/questions/5727/what-are-the-barriers-to-understanding-pointers-and-what-can-be-done-to-overcome/5754#5754) It's dual tagged [tag:c] and [tag:c++] and is also closed, but it has some good information in it. – Jonathan Leffler May 19 '16 at 17:47

2 Answers2

7

STRUCT_TYPEDEF array_var[] = some_struct.array2_var;

is not a valid way to initialize an array in a declaration. An array initializer must be a brace-enclosed list of initializers, such as

T arr[] = { init1, init2, init3 };

You cannot initialize an array with another array1, nor can you assign one array to another this way:

T foo[] = { /* list of initializers */ }
T bar[] = foo; // not allowed
T bar[N];
...
bar = foo; // also not allowed

If you want to copy the contents of some_struct.array2_var to array_var, you must use a library function like memcpy:

memcpy( array_var, some_struct.array2_var, sizeof some_struct.array2_var );

You must also declare array_var with a size; you can't leave it incomplete if you want to use it. If you know ahead of time how big it needs to be, it's easy:

STRUCT_TYPEDEF array_var[SIZE];
...
mempcy( array_var, some_struct.array2_var );

If you don't know ahead of time how big it needs to be, then you'll either have to declare it as a variable-length array (which won't work if it needs to be at file scope or otherwise have static storage duration), or you can declare the memory dynamically:

STRUCT_TYPEDEF *array_var = NULL;
...
array_var = malloc( sizeof some_struct.array2_var );
if ( array_var )
{
  memcpy( array_var, some_struct.array2_var, sizeof some_struct.array2_var );
}

This all assumes that some_struct.array2_var is an array declared like

STRUCT_TYPEDEF array2_var[SIZE]; 

If it's also just a pointer, then you'll have to keep track of the array size some other way.

EDIT

If you want array_var to simply point to the first element of some_struct.array2_var, you'd do the following:

STRUCT_TYPEDEF *array_var = some_struct.array2_var;

Except when it is the operand of the sizeof or unary & operators, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array. The code above is exactly equivalent to

STRUCT_TYPEDEF *array_var = &some_struct.array2_var[0];


  1. Except for string literals, such as char message[] = "Hello";; the string literal "Hello" is an array expression, but the language treats it as a special case.

John Bode
  • 119,563
  • 19
  • 122
  • 198
  • array2_var was declared elsewhere, I just wanted to have a pointer to it like I would do with #2 version of the code which worked. Sorry for not being precise – Suryu May 19 '16 at 17:47
  • @Suryu: if you want a pointer, declare a pointer. Arrays are not pointers and pointers are not arrays, but the two _are_ closely related and frequently confused. The confusion is not helped by the fact that in a function parameter list, `void function(SomeType arr[])` and `void function(SomeType *arr)` _are_ in fact equivalent — but that's a special case (and sometimes just seems designed to throw people off balance). But the only place where they're equivalent is in a function parameter list; in all other contexts, arrays are different from pointers. – Jonathan Leffler May 19 '16 at 17:51
  • Very detailed answer, +1. – Mirakurun May 19 '16 at 18:18
  • Thanks for detailed answer and comments! – Suryu May 20 '16 at 08:12
1

This ...

extern STRUCT_TYPEDEF array_var[];

... is a declaration of an array of unknown size and with external linkage. Because the size is not specified, that declaration leaves array_var with an "incomplete type"; that prevents some uses of that variable until and unless its type is completed by another declaration in the same translation unit. For example, it cannot be the operand of the sizeof operator.

This ...

STRUCT_TYPEDEF array_var[] = some_struct.array2_var;

... claims to be a definition of array_var, on account of providing an initializer. The initializer is not of the correct form for a variable of array type, however. An array initializer consists of a comma delimited sequence of one or more array elements, inside mandatory curly braces ({}). Just as C does not support whole-array assignment, it does not support array values as array initializers.


In contrast, this ...

extern STRUCT_TYPEDEF *array_var;

... is a declaration of a pointer with external linkage. It has a complete type. And this ...

STRUCT_TYPEDEF *array_var = &some_struct.array2_var[0];

... is a valid definition of the variable, with a suitable initializer. Because array values decay to pointers in this context, as in most (but not all) others, it is equivalent to this:

STRUCT_TYPEDEF *array_var = some_struct.array2_var;

In comparing this to the original code, it is essential to understand that although they have a close association, pointers and arrays are completely separate types.

Also, this portion of code was referenced only in functions like "array_var[0].property = ...", but none of these functions were ever called from the application.

Whether the variable is ever accessed normally has no bearing on whether the compiler is willing to accept the code.

Is there some difference between [] and * I don't know about?

Apparently so, since the question seems to assume that there is no difference.

The two forms can be used interchangeably for declaring function parameters. In that context, both declare the parameter as a pointer. This is a notational and code clarity convenience made possible by the fact that array values appearing as function arguments decay to pointers. You can never actually pass an array as a function argument -- when an argument designates an array, a pointer is passed instead.

As described above, however, the two forms are not equivalent for declaring an ordinary variable.

John Bollinger
  • 160,171
  • 8
  • 81
  • 157