Can I statically initialize a char* in a struct in C?
Is there any way to do something like this in C?
char* str[] = { "abc" };
struct test { char* str_in_struct; } tests[] = {
{ str[0] }
};
If I try to compile this, gcc says:
开发者_Python百科main.c:6: error: initializer element is not constant
main.c:6: error: (near initialization for ‘tests[0].str_in_struct’)
main.c:6: warning: missing initializer
main.c:6: warning: (near initialization for ‘tests[0].str_in_struct’)
There are two contexts in which that code could appear - inside a function, and outside a function.
- The code is valid in one context - inside a function.
- The code is invalid in the other context - outside a function.
This probably accounts for the divergent views on whether or not the compiler accepts the code.
Given the code:
char* str[] = { "abc" };
struct test { char* str_in_struct; } tests[] = { { str[0] } };
void somefunc(void)
{
char* str[] = { "abc" };
struct test tests[] = { { str[0] } };
}
the compilation (with GCC 4.1.2 on MacOS X 10.6.7) yields:
$ /usr/bin/gcc -g -std=c99 -Wall -Wextra -Wmissing-prototypes -Wstrict-prototypes -c xx.c
xx.c:2: error: initializer element is not constant
xx.c:2: error: (near initialization for ‘tests[0].str_in_struct’)
xx.c:2: warning: missing initializer
xx.c:2: warning: (near initialization for ‘tests[0].str_in_struct’)
xx.c:5: warning: no previous prototype for ‘somefunc’
xx.c: In function ‘somefunc’:
xx.c:7: warning: unused variable ‘tests’
$
The warning for lines 5 and 7 are quite accurate; the errors on line 2 are accurate too.
What's the trouble?
Basically, str[0]
requires a computation that the linker cannot do (or not required to do).
In this revision of the code, test2
is OK, but tests
and test3
are not:
struct test { char* str_in_struct; };
char *str[] = { "abc" };
char pqr[] = "abc";
char *xyz = "abc";
struct test tests[] = { { str[0] } };
struct test test2[] = { { pqr } };
struct test test3[] = { { xyz } };
void somefunc(void)
{
char* str[] = { "abc" };
struct test tests[] = { { str[0] } };
}
You can refer to array names in the external initializers. You can't refer to array elements, nor can you refer to the value of a pointer variable.
Paraphrasing a comment/question:
[It] seems though that if
char str4[][4] = { "abc", "d" };
, thenstruct test { char* str_in_struct; } test4[] = { { str4[1] } };
is valid. So then, you can refer to array elements, but only if their size is known?
I haven't quite characterized it correctly - you're right. I've given part of the answer, but not the full answer. Basically, the expressions in the 'out of function' initializers have to be constants. It isn't quite as simple as 'only if the size is known'. The issue is whether the expression in the initializer can be calculated without reading a value from memory.
With str[0]
(original version), you have to read the value stored at str[0]
; similarly with xyz
. With the pqr
version and the str4
version (note the extra 4
compared to the comment), the value (address) pqr
or str4[1]
is computable by the linker without reading the value stored there.
In the C99 standard, §6.7.8 Initialization says:
¶4 All the expressions in an initializer for an object that has static storage duration shall be constant expressions or string literals.
§6.6 Constant Expressions says:
¶2 A constant expression can be evaluated during translation rather than runtime, and accordingly may be used in any place that a constant may be.
and:
¶7 More latitude is permitted for constant expressions in initializers. Such a constant expression shall be, or evaluate to, one of the following:
— an arithmetic constant expression,
— a null pointer constant,
— an address constant, or
— an address constant for an object type plus or minus an integer constant expression.¶8 An arithmetic constant expression shall have arithmetic type and shall only have operands that are integer constants, floating constants, enumeration constants, character constants, and
sizeof
expressions. Cast operators in an arithmetic constant expression shall only convert arithmetic types to arithmetic types, except as part of an operand to asizeof
operator whose result is an integer constant.¶9 An address constant is a null pointer, a pointer to an lvalue designating an object of static storage duration, or a pointer to a function designator; it shall be created explicitly using the unary
&
operator or an integer constant cast to pointer type, or implicitly by the use of an expression of array or function type. The array-subscript[]
and member-access.
and->
operators, the address&
and indirection*
unary operators, and pointer casts may be used in the creation of an address constant, but the value of an object shall not be accessed by use of these operators.
Note the qualifier 'but the value of an object shall not be accessed by use of these operators'. This agrees with what I wrote earlier in this expansion of the answer. In particular, the value of str4[1]
requires only the address constant of the array str4
plus an integer constant expression (the last alternative in the bullet list). Similarly, pqr
is an address constant (third alternative in the bullet list). But the str[0]
and xyz
initializers have to access the value of an object, which is not allowed.
精彩评论