是否有可能将C指针初始化为NULL?
我一直在写东西
char *x=NULL;
假设
char *x=2;
会创build一个char
指针到地址2。
但是,在GNU C编程教程中,它说int *my_int_ptr = 2;
将整数值2
存储到分配给my_int_ptr
任何随机地址中。
这似乎意味着我自己的char *x=NULL
是分配任何值的NULL
强制转换为char
是内存中的某个随机地址。
而
#include <stdlib.h> #include <stdio.h> int main() { char *x=NULL; if (x==NULL) printf("is NULL\n"); return EXIT_SUCCESS; }
事实上,打印
一片空白
当我编译和运行它时,我担心的是我依赖于未定义的行为,或者至less是不明确的行为,而且我应该写
char *x; x=NULL;
代替。
是否有可能将C指针初始化为NULL?
TL; DR是的,非常。
在指南上提出的实际要求是这样的
另一方面,如果只使用单个初始赋值,则
int *my_int_ptr = 2;
,程序将尝试用值2填充由my_int_ptr
指向的内存位置的内容。由于my_int_ptr
填充了垃圾,它可以是任何地址。 […]
那么他们错了,你是对的。
对于语句,( 现在忽略指向整数转换的指针是实现定义的行为 )
int * my_int_ptr = 2;
my_int_ptr
是一个variables(指向int
types的指针),它有一个自己的地址(types:指向整数的指针的地址),你将值2
存储到该地址。
现在, my_int_ptr
是一个指针types,我们可以说,它指向 my_int_ptr
保存的值指向的内存位置的“type”值。 所以,你基本上是分配指针variables的值,而不是指针指向的内存位置的值。
所以,结论
char *x=NULL;
将指针variablesx
初始化为NULL
,而不是指针指向的内存地址的值 。
这是一样的
char *x; x = NULL;
扩张:
现在,严格遵守,像一个声明
int * my_int_ptr = 2;
是非法的,因为它涉及违反约束。 要清楚的是,
-
my_int_ptr
是一个指针variables,types为int *
- 一个整数常量,
2
的定义是int
types。
而且它们不是“兼容的”types,所以这个初始化是无效的,因为它违反了简单赋值的规则,这在Lundin的答案中描述的§6.5.16.1/ P1中提到。
如果有人对初始化如何链接到简单的赋值约束感兴趣,引用C11
第6.7.9节,P11
标量的初始值应该是一个单独的expression式,可以用大括号括起来。 对象的初始值是expression式(转换后)的初始值; 与简单赋值相同的types约束和转换适用,将标量types作为其声明types的非限定版本。
教程是错误的。 在ISO C中, int *my_int_ptr = 2;
是一个错误。 在GNU C中,它与int *my_int_ptr = (int *)2;
。 这将以编译器确定的某种方式将整数2
转换为内存地址。
它不会尝试在该地址所在的位置存储任何内容(如果有的话)。 如果你继续写*my_int_ptr = 5;
,那么它会尝试将数字5
存储在该地址所指向的位置。
为了澄清为什么教程是错误的, int *my_int_ptr = 2;
是一个“违反约束”,它是不允许编译的代码,编译器在遇到它时必须给你一个诊断。
按照6.5.16.1简单赋值:
约束
下列其中一项应为:
- 左边的操作数具有primefaces性,合格性或不合格的算术types,右边有算术types;
- 左操作数具有与右侧types兼容的结构或联合types的primefaces,限定或不合格版本;
- 左操作数具有primefaces,限定或非限定指针types,并且(考虑左值在左值转换后将具有的types),两个操作数都是指向兼容types的限定版本或非限定版本的指针,而左边指向的types具有全部右边指出的types限定符;
- 左操作数具有primefaces性,限定性或非限定性指针types,并且(考虑左值在左值转换后将具有的types),一个操作数是指向对象types的指针,另一个是指向合格或不合格版本的指针void,左边指向的types具有右侧指向的所有types的限定符;
- 左边的操作数是一个primefaces,限定或不合格的指针,右边是一个空指针常量; 要么
- 左操作数具有typesprimefaces,限定或不合格的_Bool,右边是一个指针。
在这种情况下,左操作数是一个不合格的指针。 它没有提到允许右操作数是一个整数(算术types)。 所以代码违反了C标准。
GCC被称为行为不佳,除非你明确地告诉它是一个标准的C编译器。 如果将代码编译为-std=c11 -pedantic-errors
,它将正确地给出诊断,因为它必须执行。
int *my_int_ptr = 2
将整数值2存储到分配给my_int_ptr的任何随机地址中。
这是完全错误的。 如果这实际上是写,那么请得到一本更好的书或教程。
int *my_int_ptr = 2
定义了一个指向地址2的整数指针。如果尝试访问地址2
很可能会发生崩溃。
*my_int_ptr = 2
,即没有在行中的int
,将值2存储到任何随机地址my_int_ptr
指向。 说了这个之后,你可以在定义的时候给指针赋NULL
。 char *x=NULL;
是完全有效的C.
编辑:在写这个时,我不知道整型指针转换是实现定义的行为。 有关详细信息,请参阅@MM和@SouravGhosh的良好答案。
关于C语言指针的许多困惑都来自于最初编码风格的一个非常糟糕的select,这在语言的语法中是非常糟糕的select。
int *x = NULL;
是正确的C,但这是非常误导的,我甚至会说无意义的,它已经阻碍了许多新手对语言的理解。 这让人想到以后我们可以做*x = NULL;
这当然是不可能的。 你看,variables的types不是int
,variables的名称不是*x
,声明中的*
也不能和=
协同起作用。 这是纯粹的声明。 所以,更重要的是这个:
int* x = NULL;
这也是正确的C,虽然它不符合原来的K&R编码风格。 它清楚地表明types是int*
,指针variables是x
,所以即使对于不熟悉的事实,值NULL
也被存储到x
,这是一个指向int
的指针。
此外,它更容易推导出一条规则:当星星离开variables名称时,它是一个声明,而附加到名称的星号是指针解除引用。
所以,现在变得更容易理解,进一步下来,我们可以做x = NULL;
或*x = 2;
换句话说,它使新手更容易看到variable = expression
如何导致pointer-type variable = pointer-expression
和dereferenced-pointer-variable = expression
。 (对于发起者来说,“expression”我的意思是“右值”)。
语言语法中不幸的select是当声明局部variables时,你可以说int i, *p;
它声明了一个整数和一个指向整数的指针,所以它使人相信*
是名字的一个有用部分。 但事实并非如此,这句话只是一个古怪的特例,为方便起见,我认为它应该从来没有存在,因为它使我上面提出的规则无效。 据我所知,语言中没有其他地方的语法是有意义的,但即使是这样,它也指向了在C中定义指针types的方式上的差异。在其他地方,在单variables声明中,在参数列表中,在结构成员等,你可以声明你的指针type* pointer-variable
而不是type *pointer-variable
; 这是完全合法的,更有意义。
我想添加一些与许多优秀答案正交的东西。 实际上,初始化为NULL
远不是坏习惯,如果该指针可能或不可用于存储dynamic分配的内存块,则可能会很方便。
int * p = NULL; ... if (...) { p = (int*) malloc(...); ... } ... free(p);
由于根据ISO-IEC 9899标准,当参数为NULL
, free
是一个nop,所以上面的代码(或者在同一行上更有意义)是合法的。
这是一个空指针
int * nullPtr = (void*) 0;
这是对的。
int main() { char * x = NULL; if (x==NULL) printf("is NULL\n"); return EXIT_SUCCESS; }
这个function是正确的。 它将0的地址赋给字符指针x。 也就是说,它将指针x指向内存地址0。
替代scheme:
int main() { char* x = 0; if ( !x ) printf(" x points to NULL\n"); return EXIT_SUCCESS; }
我的猜测是你想要的是:
int main() { char* x = NULL; x = alloc( sizeof( char )); *x = '2'; if ( *x == '2' ) printf(" x points to an address/location that contains a '2' \n"); return EXIT_SUCCESS; } x is the street address of a house. *x examines the contents of that house.
只要记住:
在C中, 左侧值总是评估一个内存位置 ,而右侧总是评估一个值(无论是int或地址或类Foo)。