Reputation: 25
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?
Upvotes: 2
Views: 1522
Reputation: 123548
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];
char message[] = "Hello";
; the string literal "Hello"
is an array expression, but the language treats it as a special case.
Upvotes: 7
Reputation: 181008
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.
Upvotes: 1