【C++】特殊字符“\0”,以及NULL相关
我们都知道,’\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相关的更多相关文章
- PHP中空字符串介绍0、null、empty和false之间的关系
PHP中空字符串介绍0.null.empty和false之间的关系 作者: 字体:[增加 减小] 类型:转载 时间:2012-09-25 用PHP开发那么久,PHP中空字符串.0.null.emp ...
- 优先选择nullptr而不是0和NULL
我们知道:0是一个int,而不是一个指针.如果C++在一个只有指针才能够使用的上下文中发现它只有一个0,那么它会勉强将0解释成空指针,但那时一种倒退行为.C++的主要方针是0就是一个int,而不是指针 ...
- item 8: 比起0和NULL更偏爱nullptr
本文翻译自modern effective C++,由于水平有限,故无法保证翻译完全正确,欢迎指出错误.谢谢! 博客已经迁移到这里啦 先让我们看一些概念:字面上的0是一个int,不是一个指针.如果C+ ...
- php中0,空,null和false之间区别
$a = 0; $b="0"; $c= ''; $d= null; $e = false; echo "5个变量-原始测试类型"; var_dump($a);/ ...
- 前台报错:Uncaught TypeError: Cannot read property '0' of null
错误现象: var div1=mycss[0].style.backgroundColor; //这一行提示360和chrome提示:Uncaught TypeError: Cannot read ...
- C++中 0 与 NULL 与 nullptr之间的关系,nullptr_t 的实现
C++中 0 与 NULL 与 nullptr之间的关系,nullptr_t 的实现 来源 http://blog.csdn.net/Virtual_Func/article/details/4975 ...
- js 中 0 和 null 、"" Boolean 值关系
在做字符串非空判断时,无意发现一个问题,记录下以便以后回顾. 问题描述:非空判断,只是校验传值的内容是否为"".null .undefined.当变量 赋值的字符串内容为 0,此时 ...
- 【Mac + Appium】之运行报错:[UiAutomator] UiAutomator exited unexpectedly with code 0, signal null
产生下面的原因是因为:与uiautomator2的weditor冲突,两者不能同时使用. 有时打开appium时会报错: [UiAutomator] UiAutomator exited unexpe ...
- php 0、null、empty和false之间的关系
// 判断 0 与 ''.null.empty.false 之间的关系 $a = 0; echo "0 与 ''. empty.null.false 之间的关系:"; if($a ...
随机推荐
- netty系列之:对聊天进行加密
目录 简介 PKI标准 各类证书的后缀和转换 netty中启动SSL server netty中启动SSL client 总结 简介 在之前的文章中,我们讲到了怎么使用netty建立聊天室,但是这样的 ...
- HTML5(十一)——WebSocket 基础教程
一.为什么要学 WebSocket? websocket 是 HTML5 提供的一种长链接双向通讯协议,使得客户端和服务器之间的数据交换更简单,允许服务端主动向客户端推送数据,并且客户端与服务端只需连 ...
- Use Module and Function instead of Class in Python
The following scripts run in ipython demonstrate the differences between instance method and static ...
- Grid布局如何设置动画效果
CS代码 新增 GridLengthAnimation继承自AnimationTimeline public class GridLengthAnimation : AnimationTimeline ...
- Elasticsearch IK分词器
Elasticsearch-IK分词器 一.简介 因为Elasticsearch中默认的标准分词器(analyze)对中文分词不是很友好,会将中文词语拆分成一个一个中文的汉字,所以引入中文分词器-IK ...
- Java HashSet和TreeSet【笔记】
Java HashSet和TreeSet[笔记] PS:HashSet.TreeSet 两个类是在 Map 的基础上组装起来的类 HashSet 类注释 1.底层实现基于 HashMap,所以迭代时不 ...
- 渲染优化之CSS Containment
引言 在开始介绍今天的主角 CSS Containment 之前,我们需要了解一些前置知识回流和重绘,方便我们理解以及应用的场景. 简单回忆下回流和重绘 回流(Reflow):当浏览器必须重新处理和绘 ...
- 【转】Linux命令:ps -ef |grep java
转自:https://www.cnblogs.com/feizifeiyu/p/8492550.html 一.ps -ef |grep java 查看包含"java"的所有进程 二 ...
- spring加载Bean的解析过程(二)
1.例如: BeanFactory bf = new XmlBeanFactory(new ClassPathResource("spring.xml")); User user ...
- SSM:Mybatis中引入通用mapper
如果你是SSM项目引入通用mapper记得要引入hibernate中的一个hibernate-jpa-2.1-api-1.0.0.Final.jar包(注意必须要Mybatis整合Spring噢,其实 ...