5

我知道一个char变量可以接受一个null字符(1个字节),即;\0作为它的值,但是,我不明白下面我的应用程序中的 char 变量如何接受一个指针(4 字节)作为它的值并且仍然可以正常工作?

#include<stdio.h>
int main()
{
    char p[10]="Its C";
    printf("%s\n",p);
    p[3]='\0';                // assigning null character 
    printf("%s\n",p);
    p[2]=NULL;               //  assigning null pointer to a char variable
    printf("%s\n",p); 
    p[1]=(void *)0;          //  assigning null pointer to a char variable
    printf("%s\n",p);
    return 0;
}

注意: GCC 编译器(32 位 Linux 平台)。

4

5 回答 5

9

NULL宏需要扩展为“实现定义的空指针常量” 。

空指针常量被定义为“一个值为 0 的整数常量表达式,或这种类型的表达式void *”。与直觉相反,此定义不需要扩展NULL为指针类型的表达式。一个常见的实现是:

#define NULL 0

空指针常量,在需要指针的上下文中使用时,可以隐式转换为指针值;结果是一个空指针。它也可以使用强制转换显式转换,例如(int*)NULL.

但是没有要求只能在这样的上下文中使用符合空指针常量的表达式。这意味着如果实现选择NULL如上定义,那么:

char c = NULL; // legal but ugly

是合法的并初始化c为空字符

这样的初始化是不可移植的(因为NULL也可能扩展((void*)0)和误导,所以应该避免,但是编译器很可能会在没有警告的情况下让它通过;NULL被编译器的预处理阶段扩展到0,后面的阶段会看到它as char c = 0;,这是合法且无害的——尽管我个人更喜欢char c = '\0';

我刚刚在我自己的 32 位 Ubuntu 系统上使用 gcc 4.7 尝试了你的示例。在没有指定选项的情况下,编译器警告了p[2]=NULL;p[1]=(void *)0;

c.c:8:9: warning: assignment makes integer from pointer without a cast [enabled by default]
c.c:10:9: warning: assignment makes integer from pointer without a cast [enabled by default]

任何 C 编译器都会出现第二个警告;第一个表示NULL实际定义为((void*)0)(通过运行代码gcc -E确认这一点)。

编译器并没有简单地“接受”这些赋值;它警告你他们。C 语言标准只要求对任何违反语言规则的行为进行“诊断”,甚至是语法错误;该诊断在法律上可能是非致命的警告信息。-std=c89 -pedantic-errors您可以使用;使 gcc 的行为更加严格 替换或强制执行来自标准更高版本的规则c89。(编辑:我从评论中看到您正在使用隐藏警告的编译器的 Web 界面;请参阅我对您的问题的评论以获取解决方法。警告很重要。)c99c11

如果您发布产生编译器警告的 C 代码, 向我们展示警告并自己密切关注它们。它们通常表明您的程序中存在严重问题,甚至是非法行为。

语言律师的狡辩:甚至不清楚:

char c = (void*)0;

指定从void*到的转换char。我自己的观点是,因为它违反了约束,所以它没有定义的语义。大多数不拒绝它的编译器会将其视为void*转换char,并且也有人认为这是必需的行为。但是,如果您只关注编译器警告和/或一开始不编写类似的代码,您就可以避免此类问题。

(C++ 的规则有点不同,但你问的是 C,所以我不会讨论这个问题。)

于 2013-04-11T17:05:26.687 回答
2

因为,在编译器中,NULL在某些编译器和((void*)0)其他编译器中被 0 取代。

该值0本身是一个有效值,char但通过转换为(void*),您在技术上将 0 转换为指针类型,因此编译器会发出警告。

请注意,如果编译器用NULL0 替换一个整数常量,它将被简单而无声地转换为char.

于 2013-04-11T16:53:32.390 回答
2

NULL是一个宏,对于几乎平台都是这样定义的

#ifndef __cplusplus
#define NULL ((void *)0)
#else   /* C++ */
#define NULL 0
#endif  /* C++ */

stddef.h来自我的 Ubuntu)

当你写

p[2]=NULL;

一样的

p[2]=(void *)0; //for c
p[2]=0; //for c++

一样的

p[2] = 0; // the 0 is casted to char 0 for C --> '\0'
于 2013-04-11T16:54:00.780 回答
2

在您的平台上,指针通常是被视为内存地址的数值。由于char类型是数字,因此将空指针(内存地址0x00)存储在p[1]

指针的 32 位值(在本例中为0x00000000)被截断为 8 位char长度:0x00

于 2013-04-11T16:54:58.243 回答
1

尝试使用-Wall选项编译它,您将看到发生了隐式转换。

于 2013-04-11T16:53:23.467 回答