NULL---空指针
NULL
:
在 C 语言中用于表示指针不指向任何有效的内存地址,即它是一个空指针常量。使用 NULL
可以帮助避免以下一些问题:
-
野指针(Dangling Pointer):当一个指针指向的内存被释放(例如,使用
free()
函数),但该指针的值没有被设置为NULL
时,该指针就变成了野指针。如果之后程序错误地尝试访问这个野指针所指向的内存,可能会导致程序崩溃或者产生不可预期的结果。通过将指针设置为NULL
,可以明确地表示该指针不再指向任何有效的内存区域,从而避免这类问题。 -
未初始化的指针:如果指针变量在使用前没有被初始化,它可能包含任意的值,这可能导致程序访问一个随机的内存地址,造成不可预期的行为。通过将指针初始化为
NULL
,可以确保在使用前指针有一个明确的状态。 -
条件判断:在 C 语言中,
NULL
常用于条件判断中,以检查指针是否指向有效的内存地址。这有助于在指针操作之前进行错误检查,从而避免由于无效指针访问导致的程序崩溃。 -
代码可读性:使用
NULL
可以提高代码的可读性。当看到NULL
时,读者可以立即知道这是一个空指针常量,而不是一个普通的整数或字符常量。 -
跨平台兼容性:在不同的平台上,指针可能具有不同的表示方式和大小。使用
NULL
可以确保代码在不同平台上的兼容性,因为NULL
宏会根据平台的不同进行适当的定义。
下面这个函数中,在堆区申请了一个int大小的空间;
函数的返回类型是int *,即一个指向整型的指针;
我很好奇为什么可以返回NULL;NULL是什么;
int* fun1(){
int *pa = (int*)malloc(sizeof(int));
if(pa == NULL) {
// 错误处理
return NULL;
}
*pa = 11;
return pa;
}
下面是GPT老师的回答:
在C语言中,NULL
的宏定义通常位于头文件stdlib.h
或stddef.h
中;
这些头文件会根据编译器和平台的不同而略有差异,但NULL
的宏定义通常如下之一:
第一种定义:直接定义为0;
#define NULL 0
第二种定义:定义为指向void
的指针字面量;
#define NULL ((void*)0)
现代C语言标准(C99及以后),推荐使用第二种定义方式,因为它提供了更强的类型安全性;
在将NULL
赋值给任何类型的指针时,它都会隐式转换为相应类型的指针,而不仅仅是整数0。
第三种定义:想要自己定义;
#ifndef NULL
#define NULL ((void*)0)
#endif
这里的#ifndef
是一个预处理指令,用于检查NULL
是否已经被定义;
如果没有被定义,那么就会使用#define
来定义它;
这样可以防止在多个地方重复定义NULL
,从而避免潜在的冲突;
后面我在Dev-C++编译器中跳转了NULL的定义:
#ifndef NULL
#ifdef __cplusplus
#ifndef _WIN64
#define NULL 0
#else
#define NULL 0LL
#endif /* W64 */
#else
#define NULL ((void *)0)
#endif
#endif
下面是GPT老师的回答:
#ifndef NULL
#ifdef __cplusplus
#if defined(_WIN64)
// 在 Windows 64 位 C++ 中定义 NULL(不推荐)
#define NULL 0LL
#else
// 在非 Windows 64 位 C++ 中定义 NULL(仍然不推荐,但更常见)
#define NULL 0
#endif
#else // 不是 C++
// 在 C 中定义 NULL
#define NULL ((void *)0)
#endif
#endif
// 但是,更好的做法是在 C++ 中始终使用 nullptr
//#define NULL nullptr
1.#ifdef __cplusplus
:
检查是否正在编译C++代码;
__cplusplus
是一个预定义的宏,当编译器在C++模式下工作时会被定义;
2.#ifndef _WIN64
:
在C++模式下,这段代码会检查是否定义了_WIN64
宏;
这个宏通常在Windows 64位平台上被定义;
3.#define NULL 0
:
定义NULL
为整数0,
在C++中,整数字面量0
可以隐式转换为任何指针类型,所以这种定义是有效的。
4.#define NULL 0LL
:
定义NULL
为长整型字面量0LL
,0LL
是一个64位整数的字面量;
然而,在C++中,通常不需要这样做,因为0
可以隐式转换为任何指针类型,包括64位指针;
所以,这个定义可能是为了某种特定目的,但通常不是推荐的做法;
5.#define NULL ((void *)0)
:
定义NULL
为一个指向void
的空指针,明确地表示了一个空指针,并且与任何指针类型都兼容。
总结:
这段代码的目的是在不同的编译器和平台上正确地定义NULL
宏;
然而,在C++中,对于64位平台的特定定义(#define NULL 0LL
)可能是不必要的;
因为0
本身就可以隐式转换为任何指针类型,包括64位指针;
在C中,使用#define NULL ((void *)0)
是推荐的做法。
GPT好像更推荐一种叫nullptr的定义方法;
NULL
的标准定义(在 C++11 及以后)是 nullptr
,它是一个类型安全的空指针常量;
在 C++ 中,直接使用 0
或 0LL
作为 NULL
的定义通常是不推荐的;
因为 nullptr
提供了更好的类型安全性和可读性。
总结:
- 当指针指向的内存被释放时或者初始化时,最好被设置为NULL,确保指针有明确的状态;
- 提高代码的可读性;
- 用于条件判断,检查指针是否指向有效的内存地址,避免无效指针访问导致程序崩溃;
- 跨平台兼容,不同平台都会对NULL进行宏定义;
崩溃示例:
line 1: 3 Segmentation fault (core dumped) ./a.out
Exited with error status 139
这表示你的程序 ./a.out
在运行时遇到了一个段错误(Segmentation Fault)。
段错误是操作系统发出的一个信号,指示你的程序试图访问它没有权限访问的内存区域,或者访问
了已经被释放的内存。
因为空指针不指向任何有效的内存地址,所以无法访问内存;