“int * nums = {5,2,1,4}”导致分段错误
int *nums = {5, 2, 1, 4}; printf("%d\n", nums[0]); 导致段错误,而
 int nums[] = {5, 2, 1, 4}; printf("%d\n", nums[0]); 
没有。 现在:
 int *nums = {5, 2, 1, 4}; printf("%d\n", nums); 
打印5。
基于此,我猜想数组初始化符号{}会盲目地将这些数据加载到左侧的任何variables中。 当它是int []时,数组按要求填充。 当它是int *时,指针被填充5,并且存储指针后的存储位置由2,1和4填充。因此,nums [0]尝试deref 5,导致段错误。
如果我错了,请纠正我。 如果我是正确的,请详细说明,因为我不明白为什么数组初始化符以他们的方式工作。
C中有一个(愚蠢的)规则,说任何简单的variables都可以用一个括号括起来的初始化列表进行初始化,就像它是一个数组一样。
 例如,你可以写int x = {0};  ,这完全等价于int x = 0;  。 
 所以当你写int *nums = {5, 2, 1, 4}; 你实际上是给一个初始化列表一个指针variables。 然而,它只是一个单一的variables,所以它只会被赋予第一个值5,其余的列表被忽略(实际上我不认为多余的初始化代码甚至应该编译一个严格的编译器) – 它不得到写入记忆。 该代码相当于int *nums = 5;  。 这意味着,数字应指向地址 5 。 
在这一点上,你应该已经得到了两个编译器警告/错误:
- 将整数指定给没有强制转换的指针。
- 初始化列表中有多余的元素。
 然后,当然代码会崩溃,因为5很可能不是一个有效的地址,你可以用nums[0]解引用。 
 作为一个方面说明,你应该使用%p说明符来printf指针地址,否则就是调用未定义的行为。 
我不太清楚你想在这里做什么,但是如果你想设置一个指向数组的指针,你应该这样做:
 int nums[] = {5, 2, 1, 4}; int* ptr = nums; // or equivalent: int* ptr = (int[]){5, 2, 1, 4}; 
或者如果你想创build一个指针数组:
 int* ptr[] = { /* whatever makes sense here */ }; 
编辑
经过一番研究,我可以说“多余元素初始化列表”实际上是无效的 – 它是一个GCC扩展 。
标准6.7.9初始化说(强调我的):
2 初始化程序不应尝试为未初始化的实体中包含的对象提供值。
/ – /
11 标量的初始化器应该是一个单独的expression式,可以用大括号括起来。 对象的初始值是expression式(转换后)的初始值; 与简单赋值相同的types约束和转换适用,将标量types作为其声明types的非限定版本。
“标量types”是一个标准术语,指的是不是数组,结构或联合types的单个variables(称为“聚合types”)。
所以用简单的英语,这个标准就是这样说的:“当初始化一个variables的时候,可以自由地在初始化expression式附近放一些额外的大括号,只是因为你可以。
情景1
int *nums = {5, 2, 1, 4}; // <-- assign multiple values to a pointer variable printf("%d\n", nums[0]); // segfault
为什么这个段错误?
 您将nums声明为int指针 – 即nums应该保存内存中一个整数的地址。 
 然后尝试将nums初始化为多个值的数组。 因此,在不深入细节的情况下,这在概念上是不正确的 – 将多个值分配给应该保存一个值的variables是没有意义的。 在这方面,如果你这样做,你会看到完全相同的效果: 
 int nums = {5, 2, 1, 4}; // <-- assign multiple values to an int variable printf("%d\n", nums); // also print 5 
 无论哪种情况(将多个值分配给一个指针或一个intvariables),接下来会发生什么,variables将得到第一个值是5 ,而其余的值将被忽略。 此代码符合,但你会得到每个额外的值,不应该在任务中的警告: 
  warning: excess elements in scalar initializer 。 
 对于将多个值分配给指针variables的情况,当访问nums[0]时,程序会发生段错误,这意味着您正在逐字地引用存储在地址5中的任何内容。 在这种情况下,您没有为指针nums分配任何有效的内存。 
值得注意的是,对于将多个值赋给intvariables的情况,没有segfault(你不是在这里引用任何无效的指针)。
情景2
 int nums[] = {5, 2, 1, 4}; 
这个不会出现段错误,因为你在堆栈中合法地分配了4个整数。
情景3
 int *nums = {5, 2, 1, 4}; printf("%d\n", nums); // print 5 
这个不会像预期的那样发生段错误,因为你正在打印指针本身的值 – 而不是取消引用(这是无效的内存访问)。
其他
 当你像这样对一个指针的值进行硬编码时(因为确定哪个进程可以访问什么内存位置是操作系统的任务),几乎总是注定要陷入段错误。 
 int *nums = 5; // <-- segfault 
所以经验法则是总是初始化一个指向某个分配variables地址的指针,比如:
 int a; int *nums = &a; 
要么,
 int a[] = {5, 2, 1, 4}; int *nums = a; 
 int *nums = {5, 2, 1, 4}; 是不合格的代码。 有一个GCC扩展将这个代码看作是一样的: 
 int *nums = (int *)5; 
试graphics成一个指向内存地址5的指针。(这对我来说似乎不是一个有用的扩展,但是我想开发者基础是需要它的)。
 为了避免这种行为(或者至less得到一个警告),你可以在标准模式下编译,例如-std=c11 -pedantic 。 
有效代码的另一种forms是:
 int *nums = (int[]){5, 2, 1, 4}; 
 它指向与nums相同的存储持续时间的可变字面值。 但是, int nums[]版本通常更好,因为它使用较less的存储空间,您可以使用sizeof来检测数组的长度。 
 int *nums = {5, 2, 1, 4}; 
  nums是一个inttypes的指针。 所以你应该把这点指向一些有效的内存位置。  num[0]你试图解引用一些随机的内存位置,因此分段错误。 
 是的,指针的值是5,而你试图取消它在系统上的未定义行为。  (看起来像5不是你的系统上有效的内存位置) 
而
 int nums[] = {1,2,3,4}; 
 是一个有效的声明,你说nums是一个inttypes的数组,而内存是根据初始化过程中传递的元素数量来分配的。 
 通过分配{5, 2, 1, 4} 5,2,1,4 {5, 2, 1, 4} 
 int *nums = {5, 2, 1, 4}; 
 你将nums赋值为5(从int到int的指针之间的隐式types转换之后)。 取消它使访问呼叫0x5处的内存位置。 这可能不允许您的程序访问。 
尝试
 printf("%p", (void *)nums);