是否可以将C指针初始化为NULL?

fag*_*pni 88 c pointers initialization

我一直在写类似的东西

char *x=NULL;
Run Code Online (Sandbox Code Playgroud)

假设是

 char *x=2;
Run Code Online (Sandbox Code Playgroud)

会创建一个char指向地址2 的指针.

但是,在GNU C编程教程中,它表示int *my_int_ptr = 2;将整数值存储2my_int_ptr分配时的任何随机地址.

这似乎意味着我自己char *x=NULL正在将NULL一个cast 的值分配给char内存中的某个随机地址.

#include <stdlib.h>
#include <stdio.h>

int main()
{
    char *x=NULL;

    if (x==NULL)
        printf("is NULL\n");

    return EXIT_SUCCESS;
}
Run Code Online (Sandbox Code Playgroud)

事实上,印刷品

一片空白

当我编译并运行它时,我担心我依赖于未定义的行为,或者至少是指定不足的行为,我应该写

char *x;
x=NULL;
Run Code Online (Sandbox Code Playgroud)

代替.

Sou*_*osh 111

是否可以将C指针初始化为NULL?

TL; DR是的,非常.


对指南实际主张如下

另一方面,如果你只使用单个初始赋值,int *my_int_ptr = 2;程序将尝试my_int_ptr用值2 my_int_ptr填充指向的内存位置的内容.由于填充了垃圾,它可以是任何地址.[...]

那么,他们错的,你是对的.

对于语句,(暂时忽略指向整数转换的指针是一个实现定义的行为)

int * my_int_ptr = 2;
Run Code Online (Sandbox Code Playgroud)

my_int_ptr是一个变量(类型指针int),它有一个自己的地址(类型:指向整数的指针的地址),你将一个值存储2地址.

现在,my_int_ptr作为指针类型,我们可以说,它指向保存在的值指向的内存位置的"类型" 的值my_int_ptr.所以,你基本上是将值指针变量,存储位置不是值由指针指向.

所以,结论

 char *x=NULL;
Run Code Online (Sandbox Code Playgroud)

将指针变量初始化xNULL,而不是指针指向的内存地址.

这是一样

 char *x;
 x = NULL;    
Run Code Online (Sandbox Code Playgroud)

扩张:

现在,严格遵守,声明如

 int * my_int_ptr = 2;
Run Code Online (Sandbox Code Playgroud)

是非法的,因为它涉及约束违规.要清楚,

  • my_int_ptr 是一个指针变量,类型 int *
  • 根据定义,整数常量2具有类型int.

并且它们不是"兼容"类型,因此这种初始化是无效的,因为它违反了简单赋值的规则,在Lundin的答案中描述的章节§6.5.16.1/ P1中提到.

如果有人对初始化如何与简单赋值约束,引用C11,章节§6.7.9,P11 相关联感兴趣

标量的初始值设定项应为单个表达式,可选择用大括号括起来.对象的初始值是表达式的初始值(转换后); 与简单赋值相同的类型约束和转换适用,将标量的类型作为其声明类型的非限定版本.

  • @SouravGhosh:值为"0"的整数常量是特殊的:它们隐式地转换为空指针,与通常的规则一致地将一般整数表达式转换为指针类型. (10认同)

M.M*_*M.M 53

该教程是错误的.在ISO C中,int *my_int_ptr = 2;是一个错误.在GNU C中,它的含义相同int *my_int_ptr = (int *)2;.这会2以编译器确定的某种方式将整数转换为内存地址.

它不会尝试在该地址所寻址的位置存储任何内容(如果有的话).如果你继续写*my_int_ptr = 5;,那么它会尝试将该号码存储5在该地址所寻址的位置.

  • @taskinoor:是的,C中的各种转换都很混乱.这个Q有关于转换的有趣信息:[C:什么时候在指针类型之间转换而不是未定义的行为?](http://stackoverflow.com/questions/4810417/c-when-is-casting-between-pointer-types-not -undefined行为). (2认同)

Lun*_*din 17

为了澄清教程错误的原因,int *my_int_ptr = 2;是"约束违规",它是不允许编译的代码,编译器必须在遇到它时给你一个诊断.

根据6.5.16.1简单分配:

约束

以下其中一项应持有:

  • 左操作数具有原子,限定或非限定算术类型,右边有算术类型;
  • 左操作数具有与右侧类型兼容的结构或联合类型的原子,限定或非限定版本;
  • 左操作数具有原子,限定或非限定指针类型,并且(考虑左值操作数在左值转换后将具有的类型)两个操作数都是指向兼容类型的限定或非限定版本的指针,左侧指向的类型具有全部右边指出的那种限定词;
  • 左操作数具有原子,限定或非限定指针类型,并且(考虑左值操作数在左值转换后将具有的类型)一个操作数是指向对象类型的指针,另一个是指向合格或非限定版本的指针void,左边指向的类型具有右边指向的所有类型的限定符;
  • 左操作数是一个原子,限定或非限定指针,右边是一个空指针常量; 要么
  • 左操作数的类型为atomic,qualified或nonqualified _Bool,右边是指针.

在这种情况下,左操作数是一个不合格的指针.它没有提到右操作数被允许为整数(算术类型).所以代码违反了C标准.

众所周知,GCC表现不佳,除非您明确告诉它是标准的C编译器.如果您将代码编译为-std=c11 -pedantic-errors,它将正确地提供必要的诊断.

  • 赞成建议-pedantic-errors.虽然我可能会使用相关的-Wpedantic. (4认同)
  • 您的语句的一个例外是右操作数不允许是整数:第6.3.2.3节说,"值为0的整型常量表达式,或类型为`void*`的表达式,称为空指针常数."注意你的报价中倒数第二个要点.因此,`int*p = 0;`是写`int*p = NULL;`的合法方式.虽然后者更清晰,更传统. (2认同)

tas*_*oor 15

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的好答案.


Mik*_*kis 14

关于C指针的许多混淆来自于最初针对编码风格做出的非常糟糕的选择,这是由语言语法中的一个非常糟糕的小选择所证实的.

int *x = NULL;是正确的C,但它是非常误导,我甚至会说荒谬,并且它阻碍了许多新手对语言的理解.它让人觉得以后我们可以做到*x = NULL;这当然是不可能的.你看,变量的类型不是int,变量的名称不是*x,*声明中的声明也没有起到任何作用=.这纯粹是陈述性的.那么,更有意义的是:

int* x = NULL;这也是正确的C,虽然它不符合原始的K&R编码风格.它非常清楚地表明类型是int*,并且指针变量是x,所以即使对于未经证实的值NULL存储到的值,它也变得明显x,这是指向它的指针int.

此外,它更容易派生规则:当星号远离变量名称时,它是一个声明,而附加到名称的星号是指针解除引用.

所以,现在变得更容易理解,我们可以做到更进一步,x = NULL;或者*x = 2;换句话说,它让新手更容易看到如何variable = expression导致pointer-type variable = pointer-expressiondereferenced-pointer-variable = expression.(对于发起的,'表达'我的意思是'rvalue'.)

语言语法中的不幸选择是,在声明局部变量时,您可以说int i, *p;哪个声明了一个整数和一个指向整数的指针,因此它使人们相信它*是名称的一个有用部分.但事实并非如此,这种语法只是一个古怪的特殊情况,为方便起见而添加,在我看来它应该从未存在过,因为它使我上面提出的规则无效.据我所知,语言中没有其他地方是这种语法有意义,但即使它是,它指的是指针类型在C中定义的方式的差异.在其他地方,在单变量声明中,在参数列表中,在struct members等中你可以声明你的指针type* pointer-variable而不是type *pointer-variable; 它是完全合法的,更有意义.

  • @SouravGhosh作为一个观点,我认为C**应该被设计成`int*somePtr,someotherPtr`声明两个指针,事实上,我曾经写过`int*somePtr`但是这导致了bug你形容. (5认同)
  • @fagricipni好吧,如果我可以从头开始设计linux,我会使用`create`而不是`creat`.:)重点是,它是如何形成的,我们需要塑造自己以适应这一点.这一切都归结为当天结束时的个人选择,同意. (2认同)

Luc*_*iti 6

我想补充一些与许多优秀答案正交的东西.实际上,NULL如果指针可能会或可能不会用于存储动态分配的内存块,则初始化为非常糟糕的做法并且可能很方便.

int * p = NULL;
...
if (...) {
    p = (int*) malloc(...);
    ...
}
...
free(p);
Run Code Online (Sandbox Code Playgroud)

因为根据ISO-IEC 9899标准, free当论证是一个nop时NULL,上面的代码(或者沿着相同的行更有意义的东西)是合法的.

  • 在C中强制转换malloc的结果是多余的,除非C代码也应该编译为C++. (5认同)