我是C的新手,但我得到了"Segmentation Faults"(Seg错误/ SIGSEGV)。为什么?



当我尝试运行以下逻辑狙击时,我会遇到分段错误,为什么?有时我不会出现分段错误,但我会得到一些没有意义的奇怪输出。。。

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] = %dn", i, array[i]); // SIGSEGV
}
/* ... */
int nums[3] = {1, 2, 3};
print_loop(nums);

我该怎么做才能解决每一个问题并理解它们为什么会发生?

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

^^这段代码只是创建一个变量供您使用,该变量是指向内存的指针。要意识到它从不分配或保留内存。

因此,对于执行*num_ptr = 6;2,您正试图将值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. 使用函数返回的数组或指针

这不起作用,因为您在没有malloc的情况下创建了一个指针并在函数内保留了一个内存区域。当此功能结束时,您将失去此保留区域。

因此,不能用这种方式生成返回指针的函数。阅读一些关于malloc()函数的内容。

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

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

当有人试图从Java、C#、Python或JavaScript等虚拟语言迁移到C时,这些都是常见的陷阱。问题是,他们不理解内存管理的概念,因为虚拟语言现在负责处理这个问题。希望想要学习C的新程序员能够找到这个答案,避免这些陷阱——并最终防止这些类型的问题每天出现在c的头版。我会尽量简短,因为我知道你们当中90%的人已经看过这段话,并说"lol太长了,读不下去了"。

什么是分段故障(seg故障)

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

一个类比:记忆中的地址就像街道上的房子地址。房子里的东西就是数据本身。如果你拥有一所房子,你可以去它的地址,随意增减家具。如果你没有房子,你只是闯入一个入口,警察(OS)就会逮捕你(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] = %dn", 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] = %dn", i, array[i]);
}
/* ... */
int nums[3] = {1, 2, 3};
print_loop(nums, 3);

"我的程序没有seg错误和/或,但它给出了一个奇怪的结果">

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

相关内容

  • 没有找到相关文章

最新更新