我是C的新手,但我得到了“Segmentation Faults”(Seg fault / SIGSEGV)。为什么?

时间:2017-01-06 17:52:22

标签: c arrays string segmentation-fault

当我尝试运行以下逻辑snippits时,我会出现分段错误,为什么?有时候我没有得到分段错误,但我得到的奇怪输出没有意义......

1。更改字符串的字符

char *str       = "Hello!";
str[0]          = 'h'; // SIGSEGV

2。修改指针的值

int *num_ptr;
*num_ptr = 6; // SIGSEGV

3。使用从函数返回的数组或指针

int *getPoint()
{
    int cords[2] = {10, 20};
    return cords;
}

/* ... */

int *point = getPoint();
int total  = point[0] + point[1]; // SIGSEGV (or sometimes total = 0?)

4。在函数

中循环通过n大小的数组
void print_loop(int *array)
{
    for(int i = 0; i < sizeof(array); i++)
         printf("array[%d] = %d\n", i, array[i]); // SIGSEGV
}

/* ... */
int nums[3] = {1, 2, 3};
print_loop(nums);

如何解决这些问题,并了解其发生的原因?

3 个答案:

答案 0 :(得分:3)

char *str;
int *num_ptr;
int *point;

^^这段代码只是为你创建一个变量然后使用它是指向内存的指针。意识到它永远不会分配或保留记忆。

因此对于 2 ,您执行*num_ptr = 6;时,您试图将值6放入num_ptr指向的内存中。但是num_ptr还没有指向任何地方,它指向 NULL 或未初始化并包含一些随机数值。如果它包含一些随机数值,则赔率是无效的内存位置,因此分段违规。如果它不是导致SIGSEV并且代码运行那么那么你很幸运;你永远不会想要以编程的基本概念发生这种方式编程。

同样的原则适用于您的所有其他示例。数据类型无关紧要。这是指向内存的指针的基本问题,以及是否(通过任何方式)保留或分配了一些有效的存储空间范围(RAM,磁盘,无论在哪里)。

str[0] = 'h';   is the same thing as            *str = 'h';
str[1] = 'h';   would be the equivalent of      *(str+1) = 'h';
str[2]          is                              *(str+2)
and so on,
where str is a pointer to the starting location of a range of memory,
then because str is of data type CHAR, +1 moves 1 byte, +2 moves 2 byte.
If it were of type INT where int is 4 bytes, then +1 would be the
initial memory location + 4.

答案 1 :(得分:1)

  1. 使用从函数返回的数组或指针
  2. 这不起作用,因为你创建了一个指针并在函数内保留了一个内存区域,没有malloc。当此功能结束时,您将丢失此保留区域。

    所以,你不能创建一个以这种方式返回指针的函数。阅读一下malloc()函数。

    1. 在函数
    2. 中循环通过n大小的数组

      这不是循环通过n大小数组的正确方法,sizeof返回分配该结构所需的字节数,而不是数组中有多少元素。一个建议是作为一个参数,你有多少元素,如果它是一个字符串,你可以使用strlen,来自string.h。

答案 2 :(得分:0)

当有人尝试从Java,C#,Python或JavaScript等虚拟语言迁移到C时,这些是常见的陷阱。问题是他们不理解内存管理的概念,因为虚拟语言现在会处理这个问题。希望能够学习C语言的新程序员可以直接得到这个答案并避免这些陷阱 - 并最终防止这些类型的问题每天填写c的首页。我将尽可能做短措辞,因为我知道有90%的人已经看过这一段并说“lol tl; dr”。

什么是分段故障(seg fault)

当你尝试访问不属于你的内存时。或者您尝试写入只读的内存 - 操作系统负责执行此操作。

类比:内存中的地址就像街道上的住宅地址。房屋内的东西就是数据本身。如果您拥有一所房子,您可以前往其地址并根据需要添加和移除家具。如果你不拥有房子,你只是打破一个入口而警察(操作系统)逮捕你(seg fault)。

获得它的常用方法

P1。更改字符串的字符

char *str       = "Hello!";
str[0]          = 'h'; // SIGSEGV

问题:不能修改字符串文字(在引号中声明,"LIKE SO")。试图这样做会导致不确定的行为。

解决方案:在数组中分配字符串。

char str[]      = "Hello!";
str[0]          = 'h';

P2。修改指针的值

int *num_ptr;
*num_ptr = 6; // SIGSEGV

问题num是指针,但其地址尚未定义。除非你给它一个有效的地址,否则你不能引用指针。

解决方案:确保指向您的指针®

int *num_ptr;
int num;
num_ptr = &num; // num_ptr equals the address of num
*num_ptr = 6;

P3。使用从函数返回的数组或指针

int *getPoint()
{
    int cords[2] = {10, 20};
    return cords;
}

/* ... */

int *point = getPoint();
int total  = point[0] + point[1]; // SIGSEGV (or sometimes total = 0?)

问题cords是一个在函数getPoint()内自动存储的数组。当此函数返回时,该数组不再存在,这意味着point现在指向无效的内存。

解决方案#1 :使用动态内存分配coords,例如malloc()。只要您的程序正在运行,或者直到您使用free()发布动态内存,就会存在动态内存。

int *getPoint()
{
    int *cords   = malloc(2 * sizeof(int)); // get 2 ints on permanent (heap) memory
    cords[0] = 10;
    cords[1] = 20;
    return cords;
}

/* ... */

int *point = getPoint();
int total  = point[0] + point[1];
free(point); // manual destroy

解决方案#2 :在函数外部声明数组并将其地址传递给getPoint()。现在,不是返回数组,而是接收修改

int *getPoint(int *cords)
{
    cords[0] = 10;
    cords[1] = 20;
    return cords;
}

/* ... */

int cords[2];
int *point = getPoint(chords);
int total  = point[0] + point[1]

P4。在函数

中循环通过n大小的数组
void print_loop(int *array)
{
    for(int i = 0; i < sizeof(array); i++)
         printf("array[%d] = %d\n", i, array[i]); // SIGSEGV
}

/* ... */
int nums[3] = {1, 2, 3};
print_loop(nums);

问题:在C中,没有任何东西牵着你的手。这意味着您必须跟踪自己该死的阵列长度。

解决方案:无论何时将数组传递给函数,都必须传递数组的大小。

void print_loop(int *array, int array_len)
{
    for(int i = 0; i < array_len; i++)
         printf("array[%d] = %d\n", i, array[i]);
}

/* ... */
int nums[3] = {1, 2, 3};
print_loop(nums, 3);

“我的程序没有seg错误和/或它会产生奇怪的结果”

段错误和导致段错误的事物称为“未定义行为”。这意味着错误将根据使用的编译器,操作系统,CPU等等而有所不同。有些情况下根本不会出现错误,但这并不意味着可以。这就像是说“如果OJ可以逃脱它,那么我也可以”,然后对结果感到惊讶。