当我尝试运行以下逻辑snippits时,我会出现分段错误,为什么?有时候我没有得到分段错误,但我得到的奇怪输出没有意义......
char *str = "Hello!";
str[0] = 'h'; // SIGSEGV
int *num_ptr;
*num_ptr = 6; // SIGSEGV
int *getPoint()
{
int cords[2] = {10, 20};
return cords;
}
/* ... */
int *point = getPoint();
int total = point[0] + point[1]; // SIGSEGV (or sometimes total = 0?)
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);
如何解决这些问题,并了解其发生的原因?
答案 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)
这不起作用,因为你创建了一个指针并在函数内保留了一个内存区域,没有malloc。当此功能结束时,您将丢失此保留区域。
所以,你不能创建一个以这种方式返回指针的函数。阅读一下malloc()函数。
这不是循环通过n大小数组的正确方法,sizeof返回分配该结构所需的字节数,而不是数组中有多少元素。一个建议是作为一个参数,你有多少元素,如果它是一个字符串,你可以使用strlen,来自string.h。
答案 2 :(得分:0)
c
的首页。我将尽可能做短措辞,因为我知道有90%的人已经看过这一段并说“lol tl; dr”。
当你尝试访问不属于你的内存时。或者您尝试写入只读的内存 - 操作系统负责执行此操作。
类比:内存中的地址就像街道上的住宅地址。房屋内的东西就是数据本身。如果您拥有一所房子,您可以前往其地址并根据需要添加和移除家具。如果你不拥有房子,你只是打破一个入口而警察(操作系统)逮捕你(seg fault)。
char *str = "Hello!";
str[0] = 'h'; // SIGSEGV
问题:不能修改字符串文字(在引号中声明,"LIKE SO"
)。试图这样做会导致不确定的行为。
解决方案:在数组中分配字符串。
char str[] = "Hello!";
str[0] = 'h';
int *num_ptr;
*num_ptr = 6; // SIGSEGV
问题:num
是指针,但其地址尚未定义。除非你给它一个有效的地址,否则你不能引用指针。
解决方案:确保指向您的指针®。
int *num_ptr;
int num;
num_ptr = # // num_ptr equals the address of num
*num_ptr = 6;
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]
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);
段错误和应导致段错误的事物称为“未定义行为”。这意味着错误将根据使用的编译器,操作系统,CPU等等而有所不同。有些情况下根本不会出现错误,但这并不意味着可以。这就像是说“如果OJ可以逃脱它,那么我也可以”,然后对结果感到惊讶。