C语言 为什么我收到警告:(接近“ptr”的初始化)和运行时在指针处访问值时分段错误



这是以下代码:为什么当我尝试访问数组的第一个值时会给出分段错误?这些警告是什么?

#include<stdio.h>
int main(void)
{
    int *ptr = {1,2,3,4,5};//Is it not similar to char *ptr="Stackoverflow"?
    printf("%dn",*ptr);// why Segmentation fault(core dumped) instead of 1
    return 0;
}
...
output:
warning: initialization makes pointer from integer without a cast [enabled by default] 
int *ptr = {1,2,3,4,5};
^
warning: (near initialization for ‘ptr’) [enabled by default]
warning: excess elements in scalar initializer [enabled by default]
warning: (near initialization for ‘ptr’) [enabled by default]
warning: excess elements in scalar initializer [enabled by default]
warning: (near initialization for ‘ptr’) [enabled by default]
warning: excess elements in scalar initializer [enabled by default]
warning: (near initialization for ‘ptr’) [enabled by default]
warning: excess elements in scalar initializer [enabled by default]
warning: (near initialization for ‘ptr’) [enabled by default]

它与 char *ptr="Stackoverflow"不同吗?

博士 不,不是。


使用的初始值设定项{1,2,3,4,5}称为大括号括起来的初始值,它应该初始化元素类型的值。这用于聚合或联合类型类型,如 C11 第 §6.7.9 章 ">初始化"中所述

具有聚合或联合类型的对象的初始值设定项应为大括号 元素或命名成员的初始值设定项列表。

在这里,初始值设定项列表包含所有int值,您正在尝试初始化一个pointer认为它。这是错误的。

另外,关于标量类型,引用C11,第§6.2.5章

算术类型和指针类型统称为标量类型。[...]

聚合类型

[...]数组和 结构类型统称为聚合类型。

这里有很多问题,例如

  1. 您正在使用int值来初始化int *
  2. 您最终会为标量对象提供一个包含多个初始值设定项元素的大括号封闭列表

因此,稍后在您的代码中,

 printf("%dn",*ptr);

本质上是无效的内存访问,它会调用未定义的行为。分割错误是众多副作用之一。

说到评论的重点,

char *ptr="Stackoverflow"?

char *ptr="Stackoverflow"; 的情况下,这里,"Stackoverflow" 称为字符串文字ptr 是用字符串文字的基址启动的。


溶液:

您需要有一个 int 数组,您可以使用大括号括起来的初始值设定项对其进行初始化。类似的东西

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

将有效。然后你可以像使用它一样

 for(int i = 0; i < 5; i++)
    printf("%dt", *(ptr+i));

您的原始代码无效。它至少包含两个约束冲突:它为不存在的对象提供初始值设定项,并尝试对int*对象使用初始值设定项1(类型 int(。编译器可以(恕我直言(简单地拒绝它。GCC 过于宽容,在仅警告错误后编译代码。生成的代码具有未定义的行为。

const char *cptr = "Hello";

以上是有效的。 "Hello" 是数组类型(特别是类型 char[6](的表达式。在大多数上下文中,包括此上下文,此类表达式被隐式转换为指向数组第 0 个元素的指针。请注意,我添加了const因此,如果我尝试修改cptr指向的数据,编译器至少会发出警告。

int *iptr = { 1, 2, 3, 4, 5 }; // invalid

这是无效的。您可能期望它的处理方式与 cptr 类似。问题是{ 1, 2, 3, 4, 5 }不是一种表达;它仅在初始值设定项中有效。它可以是数组对象的有效初始值设定项,但由于它不是表达式,因此数组到指针的转换规则不适用。

假设编译器支持 C99 或更高版本(特别是复合文字功能(,则可以编写:

int *iptr = (int[]){ 1, 2, 3, 4, 5 };

(这不是强制转换;语法类似,但{ ... }不是表达式。

复合文本是数组类型的表达式,特别是 int[5] ,并且应用数组到指针的转换。

需要注意的是:字符串文字创建一个数组对象,该对象具有静态存储持续时间,这意味着它存在于程序的整个执行过程中。仅当复合文本出现在任何函数外部时,复合文本才会创建具有静态存储持续时间的对象;在函数内部,它会创建一个具有自动存储持续时间的对象,这意味着当您到达当前块的末尾时,它就不复存在了。在这种情况下,它是在 main 函数中定义的,因此不太可能成为问题。但这是需要注意的。例如,这是安全的:

const char *new_string(void) {
    const char *result = "hello";
    return result;
}

但这不是:

int *new_array(void) {
    int *result = (int[]){ 1, 2, 3, 4, 5 };
    return result; /* BAD! */
}

因为当您离开函数时,数组将不复存在。 为了避免这种情况,您可以显式创建数组对象以使其成为静态的:

int *new_array(void) {
    static const int arr[] = { 1, 2, 3, 4, 5 };
    int *result = arr; /* or &arr[0] */
    return result;     /* or "return arr;" */
}

基本上该行是无效的,C 编译器试图理解它,但实际上无法理解。您需要此处的语法

如何将数组的所有成员初始化为相同的值?

总结

int myArray[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };

尝试:

#include<stdio.h>
int main(void)
{
    int ptr[] = {1,2,3,4,5};//Is it not similar to char *ptr="Stackoverflow"?
    printf("%dn",*ptr);// why Segmentation fault(core dumped) instead of 1
    return 0;
}

在原始代码中:

int *ptr = {1,2,3,4,5};

{1,2,3,4,5}不会初始化整数数组。字符串初始化是一种特殊情况,不适用于其他类型的类型。

因此,此代码将初始化一个整数指针,该指针将指向内存地址0x00000001(初始值设定项块中的第一个(

此地址超出了程序范围,因此出现了分段错误。

指针是一种标量数据类型,标准说 (C11-6.7.9(:

标量的初始值设定项应为单个表达式,可以选择括在大括号中。

不能使用包含多个表达式的大括号括起来的初始值设定项初始化标量数据类型。

在以下情况下

char *ptr="Stackoverflow";  

ptr指向 char 数组类型的对象(查看标准 C11:§6.7.9/11(。它只是将ptr初始化为字符串文本地址的开头。

您正在尝试将值存储在单位化指针中。指针的值必须附加到内存位置,然后才能访问它。只需在之前做一些malloc

int $ptr;
ptr = malloc( 5* sizeof(int)); // since you have 5 values and then you can initialize your data
for (i=0;i<5;i++) {
(*ptr+i) = (i+1);
}

相关内容

最新更新