C 当我将指针初始化为NULL时,运行结果很奇怪

C 当我将指针初始化为NULL时,运行结果很奇怪,c,pointers,null,C,Pointers,Null,据说我们最好用NULL初始化一个新指针,否则会出现意外的结果。但事实是,当我添加NULL时,如下所示: #include <stdio.h> int main(){ char *str = NULL; gets(str); printf("%s\n", str); return 0; } #include <stdio.h> int main(){ char *str; gets(str); printf("%s\

据说我们最好用NULL初始化一个新指针,否则会出现意外的结果。但事实是,当我添加
NULL
时,如下所示:

#include <stdio.h>
int main(){
    char *str = NULL;
    gets(str);
    printf("%s\n", str);
    return 0;
}
#include <stdio.h>
int main(){
    char *str;
    gets(str);
    printf("%s\n", str);
    return 0;
}
而我只保留新指针,如下所示:

#include <stdio.h>
int main(){
    char *str = NULL;
    gets(str);
    printf("%s\n", str);
    return 0;
}
#include <stdio.h>
int main(){
    char *str;
    gets(str);
    printf("%s\n", str);
    return 0;
}
#包括
int main(){
char*str;
获取(str);
printf(“%s\n”,str);
返回0;
}
没关系!我可以得到我写的东西。
我想知道这背后的原因。

当您将
str
初始化为NULL时,它指向内存地址零。在典型系统中,正常进程的虚拟地址空间中地址为零的页面未映射,操作系统将其标记为不可读和不可写。然后,当您的程序使用
get
尝试将数据读取到
str
点时,硬件报告访问内存时出错,操作系统和外壳向您报告这是分段故障


当您没有初始化
str
时,它碰巧采用编译器碰巧用于它的内存中的任何值。该值恰好是地址空间中的某个地址。(这可能是程序堆栈上已经存在的某个指针,因为在调用
main
之前帮助启动进程的代码使用地址用于各种目的。)然后,当调用
get
时,它将数据写入该内存。显然,这是危险的,因为它将数据写入程序可能需要用于其他目的的某个位置。但是,在这种情况下,您侥幸逃脱了惩罚,数据被写入内存,然后被打印出来。

正如所有注释所示:


C不为您处理内存 你必须自己做

由于声明了指向字符串的指针,因此必须确保指针指向属于您的内存

  • 当您将NULL分配给指针时,指针指向“禁止”内存地址零,并试图将某些内容放在那里(使用
    get
    ),导致内存管理拦截您不允许的尝试并中止程序

  • 当您没有给它赋值时,它是一个未初始化的局部变量,它有一个垃圾值(指向一个随机地址),您可以在其中存储使用
    gets
    读取的字符串,但这可能会在程序的任何后期导致任何称为未定义行为的随机行为。您写入的内存可能是您的,但未被管理(其他内容存储在那里)

有两种方法可以解决此问题:

  • 向堆请求内存(使用
    malloc
    ),例如
    char*str=malloc(1024)

  • 声明足够大的缓冲区,例如
    charstr[1024]


不要使用
获取
。这是不安全的。使用
fgets

您的两个版本都是错误的,并且具有未定义的行为。未定义 行为意味着结果是未定义的,任何事情都可能发生:错误 例如,程序运行没有问题的外观

首先,永远不要再使用
get
,否则这是一个危险的函数 考虑缓冲区的大小可能会导致缓冲区溢出。这 函数在C99中也被弃用,所以没有真正的理由使用它,请使用
fgets

获取
需要指向存储字符串的
字符
数组的指针。如果你 传递一个
NULL
指针,
get
不会通过
NULL
指针,这是未定义的行为

如果你这样做

char *str;
gets(str);
这里您只声明了一个新指针,但它未初始化,这意味着 它指向内存中的一个随机位置。在你的情况下,这似乎是 随机位置是一个有效的位置,因此程序看起来 一切都很顺利。您必须初始化指针并使其 指向某个有效的地方

char buffer[1024];
char *str = buffer;
fgets(str, 1024, stdin);
或者您必须使用
malloc
&friend分配内存

char *str = calloc(1024, 1);
if(str == NULL)
{
    fprintf(stderr, "not enough memory\n");
    return;
}
fgets(str, 1024, stdin);
许多人说应该用
NULL
因为它允许稍后检查指针是否指向某个有效位置 在记忆中。例如,算法可以检查指针是否指向有效的 位置,然后继续,否则首先分配内存,然后 继续。释放内存也是一个很好的策略:

char *ptr = NULL;

if(something_is_true())
{
    // do stuff
    ptr = malloc(...);

    // more stuff
}

free(ptr);
这里如果
something\u is\u true()
返回false,那么
free(ptr)
不会以
错误,因为
空闲(NULL)
是有效的。

:)来吧-这里你需要有一些内存,否则
获取的地方将存储那些读取的字符。两者都是未定义的行为。两者都应该避免。此外,不要使用
get
——现在已经不推荐使用了。请改用
fgets
gets
函数很危险,已从最新的C标准中删除。并且是未定义的。您的两个程序都有。@Eva Red当程序有未定义的行为时,结果看起来并不奇怪。:)顺便说一句,第二个版本也是未定义的行为。区别在于,第二个版本将在随机情况下崩溃(
str
未初始化且具有随机指针),第一个版本总是(至少在您的平台上)。至于区别,未初始化的局部变量实际上是未初始化的。它们的值是不确定的,看起来几乎是随机的。在第二个程序中,
str
NULL
的几率很小,这意味着
get
将写入内存中看似随机的位置。“C不为您处理内存”不,是的。例如,静态内存:……嗯…@ VladfromMoscow,怎么说他必须使用声明或MALOC来处理他的内存?静态内存也不是由C“处理”的;您必须声明它。例如,编译器容纳字符串文本。初始化变量