Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Welcome To Ask or Share your Answers For Others

Categories

0 votes
440 views
in Technique[技术] by (71.8m points)

c - "int *nums = {5, 2, 1, 4}" causes a segmentation fault

int *nums = {5, 2, 1, 4};
printf("%d
", nums[0]);

causes a segfault, whereas

int nums[] = {5, 2, 1, 4};
printf("%d
", nums[0]);

doesn't. Now:

int *nums = {5, 2, 1, 4};
printf("%d
", nums);

prints 5.

Based on this, I have conjectured that the array initialization notation, {}, blindly loads this data into whatever variable is on the left. When it is int[], the array is filled up as desired. When it is int*, the pointer is filled up by 5, and the memory locations after where the pointer is stored are filled up by 2, 1, and 4. So nums[0] attempts to deref 5, causing a segfault.

If I'm wrong, please correct me. And if I'm correct, please elaborate, because I don't understand why array initializers work the way they do.

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
Welcome To Ask or Share your Answers For Others

1 Reply

0 votes
by (71.8m points)

There is a (stupid) rule in C saying that any plain variable may be initialized with a brace-enclosed initializer list, just as if it was an array.

For example you can write int x = {0};, which is completely equivalent to int x = 0;.

So when you write int *nums = {5, 2, 1, 4}; you are actually giving an initializer list to a single pointer variable. However, it is just one single variable so it will only get assigned the first value 5, the rest of the list is ignored (actually I don't think that code with excess initializers should even compile with a strict compiler) - it does not get written to memory at all. The code is equivalent to int *nums = 5;. Which means, numsshould point at address 5.

At this point you should already have gotten two compiler warnings/errors:

  • Assigning integer to pointer without a cast.
  • Excess elements in initializer list.

And then of course the code will crash and burn since 5 is most likely not a valid address you are allowed to dereference with nums[0].

As a side note, you should printf pointer addresses with the %p specifier or otherwise you are invoking undefined behavior.


I'm not quite sure what you are trying to do here, but if you want to set a pointer to point at an array, you should do:

int nums[] = {5, 2, 1, 4};
int* ptr = nums;

// or equivalent:
int* ptr = (int[]){5, 2, 1, 4};

Or if you want to create an array of pointers:

int* ptr[] = { /* whatever makes sense here */ };

EDIT

After some research I can say that the "excess elements initializer list" is indeed not valid C - it is a GCC extension.

The standard 6.7.9 Initialization says (emphasis mine):

2 No initializer shall attempt to provide a value for an object not contained within the entity being initialized.

/--/

11 The initializer for a scalar shall be a single expression, optionally enclosed in braces. The initial value of the object is that of the expression (after conversion); the same type constraints and conversions as for simple assignment apply, taking the type of the scalar to be the unqualified version of its declared type.

"Scalar type" is a standard term referring to single variables that are not of array, struct or union type (those are called "aggregate type").

So in plain English the standard says: "when you initialize a variable, feel free to toss in some extra braces around the initializer expression, just because you can."


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
OGeek|极客中国-欢迎来到极客的世界,一个免费开放的程序员编程交流平台!开放,进步,分享!让技术改变生活,让极客改变未来! Welcome to OGeek Q&A Community for programmer and developer-Open, Learning and Share
Click Here to Ask a Question

...