开发者

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" };, then struct 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 a sizeof 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.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜