我试图在一个函数中分配一个字符串,并通过一个
char **
类型的指针返回它。如果我只是简单地创建一个char **
类型的指针,将它传递给一个函数并让该函数将值更改为指向字符串的指针,程序编译但返回Program returned: 139
.
但是,如果我在调用函数之前简单地分配一个值,它就可以工作!
#include <stdio.h>
#include <stdlib.h>
#include<stdbool.h> // Include bool type
void getString(char **str_ptr){
char * my_str = "String I passed." ;
*str_ptr = my_str ;
}
void printMyStr(char **str_ptr){
int i = 0;
char *str = *str_ptr;
printf("Print starts \n\t" );
// looping till the null character is encountered
while(str[i] != '\0')
{
printf("%c", str[i]);
i++;
}
printf("\nPrint ends \n" );
}
int main() {
char *str = "My great String";
char **str_ptr;
str_ptr = &str; \\ If this is commented, errors with 139
getString(str_ptr);
printMyStr(str_ptr);
return 0;
};
以上程序返回
0
输出
Print starts
String I passed.
Print ends
但是,如果我注释掉
str_ptr = &str;
行,那么它返回139
并且什么都不打印。
那么,为什么它会失败,当我评论该行时?
And为什么它适用于那条线?该行如何改变任何东西(但输出的仍然是“我传递的字符串。”)
如果你注释掉这一行,那么你的程序会:
char **str_ptr;
*str_ptr = "String I passed.";
这与例如没有什么不同。
int *int_ptr;
*int_ptr = 42;
然后它向一个未知的内存地址写入一些东西(我们不知道地址是因为我们不知道指针变量包含什么地址)然后崩溃。
如果你特别倒霉,它会覆盖一些重要的东西(或者至少,和这个愚蠢的小程序中的任何东西一样重要)而不是崩溃。
当您注释该行时,现在您告诉它要将数据写入何处:
char **str_ptr;
str_ptr = &str;
*str_ptr = "String I passed.";
与
相同str = "String I passed.";
您的程序不会崩溃/调用包含
str_ptr = &str;
的未定义行为,因为它会将其初始化为进程空间内的有效内存地址。
char *str = "My great String";
char **str_ptr;
str_ptr = &str; // *str_ptr now points to "My great String"
所以当你在
getString
中取消引用时,一切都很好
void getString(char **str_ptr){
// ok to dereference now, because you initizlied it to some valid memory.
*str_ptr = "String I passed.";
}
也许您在想,“
char str*
中的main
不存在于getString
中,那么*str_ptr
中的getString
是否会取消引用超出范围的内容?”那里有两件事:
*str_ptr
也是有效的,因为它指向的东西仍然存在。函数的局部变量在函数的右大括号}
之前不会消失。您使用 str_ptr
中变量的地址初始化 main
,并且它保持活动状态直到 main
退出(在这种情况下,这恰好是程序的生命周期)。我会复制你给的相关行。但首先,我将内联该函数,以便您可以更好地了解发生了什么。函数参数是一个在函数调用之前初始化的 locl 变量,所以我要做的是重命名它,使它成为一个新的局部变量,并像函数调用一样分配它。
所以内联版本看起来像这样:
char *str = "My great String";
char **str_ptr;
str_ptr = &str; \\ If this is commented, errors with 139
// This gets inlined.
// getString(str_ptr);
char **getString_str_ptr = str_ptr;
char * my_str = "String I passed." ;
*getString_str_ptr = my_str ;
pretend 参数与
str_ptr
的类型和值相同,现在是多余的,所以我会去掉它。
char *str = "My great String";
char **str_ptr;
str_ptr = &str; \\ If this is commented, errors with 139
char * my_str = "String I passed." ;
*str_ptr = my_str ;
当你声明
str_ptr
时,它没有任何价值。我们可以给它任何东西,但我会给它NULL
以使其清楚。很明显,如果您尝试将任何内容分配给NULL
,或取消引用NULL
,它将失败。
char *str = "My great String";
char **str_ptr = NULL;
str_ptr = &str; \\ If this is commented, errors with 139
char * my_str = "String I passed." ;
*str_ptr = my_str ;
应该清楚的是,因为你在声明它之后立即分配一个值,这与用赋值替换
NULL
是一样的。
char *str = "My great String";
char **str_ptr = &str;
char * my_str = "String I passed." ;
*str_ptr = my_str ;
str_ptr
现在指向一个指向 16 字节存储空间的指针。存储并不重要,重要的是*str_ptr
指向实际内存(str
的地址),而不是NULL。如果您注释掉该行,代码将如下所示:
char **str_ptr = NULL;
char * my_str = "String I passed." ;
*str_ptr = my_str ;
应该清楚
*str_ptr
试图取消引用NULL
,这是行不通的。