我们都知道,’\0’是字符串的结束标记。因此,执行这段代码:

#include<bits/stdc++.h>
using namespace std;
int main(){
cout<<"ab\0cd";
}

输出结果:ab

这是因为,cout默认判断字符串到结束符号\0,认为字符串结束了,因此就停止。

事实上,\0是一个非打印字符,也就是不能被打印出来的字符。如果直接尝试使用cout或者putchar输出\0,什么也不会发生。ascii码为0-31之间的字符都是非打印字符。

下面内容引用自《征服C指针》,是NULL相关内容。注意0对应的字符不是NULL,NULL表示空指针,不是字符。

(NULL是来自stdio的一个宏定义,一般是这样:#define NULL ((void*)0) )

补充NULL、0 和'\0'

经常有一种错误的程序写法:使用NULL来结束字符串。

/*通常,C 的字符串使用''结尾,可是因为strncpy()函数在 src 的长度大于len的情况下没有使用'\0'来结束,所以一板一眼地写了一个整理成C 的字符串形式的函数(企图)/

void my_strncpy(char dest, char src, int len) {

strncpy(dest, src, len);

dest[len] = NULL; ←使用NULL 来结束字符串!!

}

上面的代码,尽管在某些运行环境下能跑起来,但无论怎样它就是错误的。因为字符串是使用“空字符”来结束的,而不是用空指针来结束。

在 C 语言标准中,空字符的定义为“所有的位为 0 的字节称为空字符(nullcharacter)”(5.2.1)。也就是说,空字符是值为 0 的字符。空字符在表现上通常使用'\0'。因为'\0'是常量,所以实际上它等同于 0。也许有些吓到你了,'\0'呀'a'呀什么的,它们的数据类型其实并不是char,而是int*。

* 如果是C++,就不是这个结论了。

另外,在我的环境中,NULL在 stdio.h 里的定义如下:

#define NULL 0看到这个,你可能会说:“说来说去,那还不都是 0 嘛。”确实在大部分的情况下是这样的,但背后的事情却异常复杂。正如前面说的那样,写成'\0'和写成常量的0其实是一样的。使用'\0'只不过是习惯使然。如果想让代码容易读,遵从习惯是非常重要的。

将0当作空指针来使用,除了极其例外的情况,通常是不会发生错误的。但是,如果在字符串的最后使用NULL,就必然会发生错误。标准允许将NULL定义成(void*)0,所以在NULL被定义成(void*)的时候,如果使用NULL来结束字符串,编译器必然会提示警告。

看到刚才的关于NULL的定义,可能有人会产生下面的推测:啥呀?所谓空指针,不就是为 0 的地址嘛。在 C 中,为 0 的地址上应该是不能保存有效数据的吧?放什么都起不到任何作用,这没什么大不了的。这种推测好像颇有道理,但也是有问题的。确实在大多数的环境中,空指针就是为 0 的地址。但是,由于硬件状况等原因,世上也存在值不为 0 的空指针。偶尔会有人在获得一个结构体之后,先使用memset()将它的内存区域清零然后再使用。

此外,虽然 C 语言提供了动态内存分配函数malloc()和calloc(),但是抱着“清零后比较好”的观点,偏爱 calloc()的人倒有很多。这样也许可以避免一些难以再现的bug。使用memset()和calloc()将内存区域清零,其实就是单纯地使用 0 来填充位。通过这种处理,当结构体的成员中包含指针的时候,这个指针能不能作为空指针来使用,最终是由运行环境来决定的。顺便说一下,对于浮点数,即使它的位模式为 0,值也不一定为 0*。

* 整数类型还好,但是我还是感觉依赖环境编出来的代码是不干净的。

说到这里,哦,原来这样啊,所以要使用宏定义的NULL呢。对于空指针的值不为 0 的运行环境,NULL的值应该被#define成别的值吧。可能会有人产生以上的想法。实际上,这种想法也是有偏差的,这涉及问题的内部根源。

比如,尝试编译下面的代码:int *p = 3;在我的环境里,会出现以下警告:warning: initialization makes pointer from integer without a cast因为 3 无论怎么说都是int型,指针和int型是不一样的,所以编译器会提示警告。尽管在我的环境里指针和int的长度都是 4 个字节,但还是出现了警告。如今的编译器,几乎都是这样的。继续,让我们尝试编译下面的代码:int *p = 0;这一次没有警告。

如果说将int型的值赋予指针就会得到一个警告,那么为什么值为 3 的时候出现警告,值为 0 的时候却没有警告呢?简直匪夷所思!这是因为在 C 语言中,“当常量 0 处于应该作为指针使用的上下文中时,它就作为空指针使用”。上面的例子中,因为接受赋值的对象为指针,编译器根据上下文判断出“0应该作为指针使用”,所以将常数 0 作为空指针来读取。无论如何,编译器都会针对性地对待“需要将 0 作为指针进行处理的上下文”,所以即便是空指针的值不为 0 的情况下,使用常量 0 来代替空指针也是合法的。

此外,如上所述,有的环境中像下面这样定义NULL:#define NULL ((void*)0)

ANSI C 中,根据“应该将 0 作为指针进行处理的上下文”的原则,将常量 0 作为指针来处理。因此,显式将 0 强制转型成void*是没有意义的。但是在某些情况下,编译器也可能会理解不了“应该将 0 作为指针进行处理的上下文”。这些情况是:

没有原型声明的函数的参数

可变长参数函数中的可变部分的参数

ANSI C 中,因为引入了原型声明,只有在你确实做了原型声明的情况下,编译器才能知道你“想要传递指针”。可是,对于以printf()为代表的可变长参数函数,其可变部分的参数的类型编译器是不能理解的。

另外糟糕的是,在可变长参数的函数中,还经常使用常量NULL来表示参数的结束(比如 UNIX 的系统调用execl()函数)。以上情况下,简单地传递常量 0,会降低程序的可移植性。因此,通过使用宏定义NULL来将 0 强制转型成void*,可以显式地告之编译器当前的0 为指针*。

* 关于这个话题,在 C 语言 FAQ(http://www.catnet.ne.jp/kouno/c_faq/c_faq.htm)中,也花费了一章的笔墨进行了讨论。

【C++】特殊字符“\0”,以及NULL相关的更多相关文章

  1. PHP中空字符串介绍0、null、empty和false之间的关系

    PHP中空字符串介绍0.null.empty和false之间的关系 作者: 字体:[增加 减小] 类型:转载 时间:2012-09-25   用PHP开发那么久,PHP中空字符串.0.null.emp ...

  2. 优先选择nullptr而不是0和NULL

    我们知道:0是一个int,而不是一个指针.如果C++在一个只有指针才能够使用的上下文中发现它只有一个0,那么它会勉强将0解释成空指针,但那时一种倒退行为.C++的主要方针是0就是一个int,而不是指针 ...

  3. item 8: 比起0和NULL更偏爱nullptr

    本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 先让我们看一些概念:字面上的0是一个int,不是一个指针.如果C+ ...

  4. php中0,空,null和false之间区别

    $a = 0; $b="0"; $c= ''; $d= null; $e = false; echo "5个变量-原始测试类型"; var_dump($a);/ ...

  5. 前台报错:Uncaught TypeError: Cannot read property '0' of null

    错误现象: var div1=mycss[0].style.backgroundColor;  //这一行提示360和chrome提示:Uncaught TypeError: Cannot read  ...

  6. C++中 0 与 NULL 与 nullptr之间的关系,nullptr_t 的实现

    C++中 0 与 NULL 与 nullptr之间的关系,nullptr_t 的实现 来源 http://blog.csdn.net/Virtual_Func/article/details/4975 ...

  7. js 中 0 和 null 、"" Boolean 值关系

    在做字符串非空判断时,无意发现一个问题,记录下以便以后回顾. 问题描述:非空判断,只是校验传值的内容是否为"".null .undefined.当变量 赋值的字符串内容为 0,此时 ...

  8. 【Mac + Appium】之运行报错:[UiAutomator] UiAutomator exited unexpectedly with code 0, signal null

    产生下面的原因是因为:与uiautomator2的weditor冲突,两者不能同时使用. 有时打开appium时会报错: [UiAutomator] UiAutomator exited unexpe ...

  9. php 0、null、empty和false之间的关系

    // 判断 0 与 ''.null.empty.false 之间的关系 $a = 0; echo "0 与 ''. empty.null.false 之间的关系:"; if($a ...

随机推荐

  1. netty系列之:对聊天进行加密

    目录 简介 PKI标准 各类证书的后缀和转换 netty中启动SSL server netty中启动SSL client 总结 简介 在之前的文章中,我们讲到了怎么使用netty建立聊天室,但是这样的 ...

  2. HTML5(十一)——WebSocket 基础教程

    一.为什么要学 WebSocket? websocket 是 HTML5 提供的一种长链接双向通讯协议,使得客户端和服务器之间的数据交换更简单,允许服务端主动向客户端推送数据,并且客户端与服务端只需连 ...

  3. Use Module and Function instead of Class in Python

    The following scripts run in ipython demonstrate the differences between instance method and static ...

  4. Grid布局如何设置动画效果

    CS代码 新增 GridLengthAnimation继承自AnimationTimeline public class GridLengthAnimation : AnimationTimeline ...

  5. Elasticsearch IK分词器

    Elasticsearch-IK分词器 一.简介 因为Elasticsearch中默认的标准分词器(analyze)对中文分词不是很友好,会将中文词语拆分成一个一个中文的汉字,所以引入中文分词器-IK ...

  6. Java HashSet和TreeSet【笔记】

    Java HashSet和TreeSet[笔记] PS:HashSet.TreeSet 两个类是在 Map 的基础上组装起来的类 HashSet 类注释 1.底层实现基于 HashMap,所以迭代时不 ...

  7. 渲染优化之CSS Containment

    引言 在开始介绍今天的主角 CSS Containment 之前,我们需要了解一些前置知识回流和重绘,方便我们理解以及应用的场景. 简单回忆下回流和重绘 回流(Reflow):当浏览器必须重新处理和绘 ...

  8. 【转】Linux命令:ps -ef |grep java

    转自:https://www.cnblogs.com/feizifeiyu/p/8492550.html 一.ps -ef |grep java 查看包含"java"的所有进程 二 ...

  9. spring加载Bean的解析过程(二)

    1.例如: BeanFactory bf = new XmlBeanFactory(new ClassPathResource("spring.xml")); User user ...

  10. SSM:Mybatis中引入通用mapper

    如果你是SSM项目引入通用mapper记得要引入hibernate中的一个hibernate-jpa-2.1-api-1.0.0.Final.jar包(注意必须要Mybatis整合Spring噢,其实 ...