《你必须知道的495个C语言问题》读书笔记之第8-10章:字符串、布尔类型和预处理器
一、字符和字符串
1. Q:为什么strcat(string, '!')不行?
A:strcat()用于拼接字符串,所以应该写成strcat(string, "!")。"!"实际上包含两个字符:'!'和'\0'。
2. Q:为什么不能这样检查一个字符串是否跟某个值匹配?
char *string;
...
if (string == "value") {
/* string matches "value"*/
}
A:C语言中的字符串用字符的数组表示,C语言不会把数组作为一个整体来操作(赋值、比较等)。上面代码段的==操作符实际上比较的是两个指针是否相等,要比较两个字符串,一般使用库函数strcmp()。
3. Q:我正开始考虑多语言字符集的问题,是否有必要担心sizeof(char)会被定义为2,以便表达16位的字符集呢?
A:就算char型被定义为16位,sizeof(char)依然是1(sizeof是以char的大小作为一个单位的),而<limits.h>中的CHAR_BIT会被定义为16,届时将不能声明(或用malloc分配)一个8位的对象。
二、布尔表达式和变量
1. Q:C语言中布尔值该用什么类型?为什么它不是一个标准类型?
A:C语言中没有提供标准的布尔类型,部分原因在于选择一个这样的类型设计最好有程序员来决定的空间/时间折中。(使用int型可能更快,使用char型可能更节省数据空间。然而,如果需要和int型反复转换,那么更小的类型也可能生成更大或更慢的代码。)可以使用#define或枚举常量定义真假,无伤大雅。
#define TRUE 1
#define FALSE 0 enum bool {false, true};
2. Q:我使用了来自两个不同的第三方库的头文件,它们都定义了相同的宏,如TRUE, FALSE, Min(), Max()等,但是它们的定义相互冲突,怎么办?
A:这是个典型的命名空间问题。理想状态下,第三方库的厂商在定义符号(预处理库、全局变量和函数名称)的时候应该尽责地确保不会发生命名空间冲突,最好的解决方案是让厂商修改他们的头文件。作为一种迂回措施,你也可以在发生冲突的#include指令之间解除或重新定义冲突的宏。
三、C预处理器
1. 如果一个参数在宏扩展中出现了多次,而实参是带副作用的表达式,则宏可能不能正确运行。
#define square(x) ((x) * (x))
square(i++); //会被扩展为((i++)*(i++)),而这是未定义的
2. Q:如何书写多语句宏?
A:通常的目标时能够像一个包含函数调用的表达式语句一样调用宏:MACRO(arg1, arg2); 这意味着“调用者”需要提供最终的分号,而宏体不需要。因此宏体不能为简单的括号包围的复合语句,因为这个宏可能会用于带else的if/else语句的if分支。传统的解决方法是使用do-while语句。
#define MACRO(arg1, arg2) {stmt1; stmt2;}
if (cond)
MACRO(arg1, arg2);
else /* some other code*/
/*宏展开后,用户提供的最终的分号就会成为语法错误*/
if (cond)
{stmt1; stmt2;};
else /* some other code */
/* 传统的解决方案 */
#define MACRO(arg1, arg2) do { \
stmt1;
stmt2;
...
} while()
如果宏体内的语句都是简单语句(没有声明或循环),那么还有一种技术,就是写一个使用一个或多个逗号操作符的表达式,放在括号中:
#define FUNC(arg1, arg2) (expr1, expr2. expr3) // FUNC返回expr3
有些编译器(如gcc)可以使用非标准的“inline”关键字或其他扩展自动地或根据程序员的请求使用内联扩展小函数。
3. Q:应该把哪些内容放在.h文件?哪些内容放在.c文件?
A:作为一般规则,应该把下面所列的内容放入头文件:(a)宏定义;(b)结构、联合和枚举声明;(c)typedef声明;(d)外部函数声明;(e)全局变量声明。
另一方面,如果定义或声明为一个源文件私有,则最好留在该文件中(并声明为static)。最后,不能把实际的代码或全局变量放在头文件中。
4. Q:可以在一个头文件中包含另一个头文件吗?
A:这是个风格问题。反对者认为,“嵌套包含文件”应该避免,因为它让相关定义更难找到。如果一个文件被包含了两次,它会导致重复定义错误,同时它也会令Makefile的人工维护十分困难。支持者认为,“嵌套包含文件”使模块化使用头文件成为可能(一个头文件可以包含它所需要的一切,而不是让每个源文件都包含需要的头文件)。而自动的Makefile维护工具可以很容易地处理嵌套包含文件的依赖问题。一种流行的头文件定义技巧:
#ifndef __HFILENAME_H__
#define __HFILENAME_H__
...
#endif
5. Q:完整的头文件搜索规则是怎样的?
A:准确的行为是由实现定义的(一般应该有文档说明)。通常,用<>命名的头文件会先在一个或多个标准位置搜索,用""命名的头文件会首先在“当前目录”搜索,如果没有找到再在标准位置搜索。注意“当前目录”的定义不唯一:传统上当前目录是包含#include指令的文件所在的目录,而在其他编译器下,当前目录是编译器启动的目录。
6. Q:sizeof操作符可以用在#if预处理指令中吗?
A:不行。预处理在编译过程之前进行,此时尚未对类型名称进行分析。作为替代,可以考虑使用ANSI的<limits.h>中定义的常量,或者使用配置脚本。当然,更好的方法是编写与类型大小无关的代码。
7. Q:如何用#if表达式来判断机器是高字节在前还是低字节在前?
A:恐怕不能。判断机器字节顺序的代码技术通常都要用到字符数组或联合,但预处理运算仅仅使用长整型,而且没有寻址的概念。另外,通常写出与字节顺序无关的代码更好。
《你必须知道的495个C语言问题》读书笔记之第8-10章:字符串、布尔类型和预处理器的更多相关文章
- 你必须知道的495个c语言问题(笔记)
1.1我该如何决定使用哪种整数类型? 用到较大的数用long:空间很重要(例如有很大的数组或很多的结构)用short:此外用int. win32: int 32bit 4byte char 8b ...
- 你必须知道的495个C语言问题,学习体会一
C语言作为一门古老的语言,其灵活性和容易出错都让人 又爱又恨,书籍<你必须知道的495个C语言问题>,使用问答的形式,告诉读者 C语言使用的各个方面的知识,包括一些冷知识等.以下,我要摘录 ...
- C语言学习书籍推荐《你必须知道的495个C语言问题》
萨米特 (Steve summit) (作者), 孙云 (译者), 朱群英 (译者) 下载地址:点我 <你必须知道的495个C语言问题>以问答的形式组织内容,讨论了学习或使用C语言的过程中 ...
- 《你必须知道的495个C语言问题》知识笔记及补充
1. extern在函数声明中是什么意思? 它能够用作一种格式上的提示表明函数的定义可能在还有一个源文件里.但在 extern int f(); 和 int f(); 之间并没有实质的差别. 补充:e ...
- 《你必须知道的495个C语言问题》读书笔记之第11-14章:ANSI C标准、库函数、浮点数
一.ANSI C标准 1. ANSI向C语言预处理器引入了几项新的功能,包括“字符串化”操作符(#).“符号粘贴”操作符(##).#pragma指令. 2. Q:char a[3] = "a ...
- 《你必须知道的495个C语言问题》读书笔记之第1-2章:声明和初始化
1. C标准中并没有精确定义数值类型的大小,但作了以下约束: (1) char类型可以存放小于等于127的值: (2) short int和int可以存放小于等于32767的值: (3) long i ...
- 你必须知道的495个C语言问题,学习体会四
本文,我们来学习下指针,这是个梦魇啊.无数次折磨着C语言学习者,无数次的内存泄露,无数次的访问失败,无数次的越界溢出, 这些错误造就的仅仅是一个 跟随者,真正的优秀者必须要正视语言的局限,同时在最大限 ...
- 你必须知道的495个C语言问题,学习体会三
本文是 本系列的第三篇,本文主要对C语言的表达式做个小结 先从两个坑爹的表达式说起:i++ 与++i 上大学的时候,学长告诉我,这两个表达式,意义是一样的,后来老师纠正说,还是有区别的,于是让我们记住 ...
- 你必须知道的495个C语言问题,学习体会二
这是本主题的第二篇文章,主要就结构体,枚举.联合体做一些解释 1.结构体 现代C语言编程 结构化的基石,diy时代的最好代言人,是面向对象编程中类的老祖宗. 我们很容易定义一个结构体,比如学生: st ...
随机推荐
- 关于IE8的兼容性问题
DOCTYPE 首先需要确保你的HTML页面开始部分要有DOCTYPE声明.DOCTYPE告诉浏览器使用什么样的HTML或XHTML规范来解析HTML文档,具体会影响: 对标记.attributes ...
- hive安装常见错误
hive编译出错 mvn clean package -DskipTests -Phadoop-2 -Pdist 失败日志1 Failed to execute goal on project hiv ...
- 【LeetCode-86】分隔链表
[题目描述] 给定一个链表和一个特定值 x,对链表进行分隔,使得所有小于 x 的节点都在大于或等于 x 的节点之前. 你应当保留两个分区中每个节点的初始相对位置. 示例: 输入: head = 1-& ...
- Django基础之中间件
1. 引入 在之前学习的过程中,已经学会了给视图函数加装饰器来判断用户是否登录,把没有登录的用户请求跳转到登录页面. 我们通过给几个特定视图函数加装饰器实现了这个需求. 但是以后添加的视图函数可能也需 ...
- springboot的jar在linux运行
springboot项目使用maven打包成jar包,如何在linux优雅部署?平时启动项目使用java -jar命令,关闭程序需要查询pid再查杀进程,这样都太麻烦了,今天发现一个博客已经写好的脚本 ...
- 阿里前端实习生面试总结(两轮技术面+一轮hr面)
投的蚂蚁金服: 一面(只有13分钟): 1.angular里双向绑定的实现原理: 巴拉巴拉巴拉,这个问题很常见,我提到了$scope.$apply()和$scope.$digest(),面试官问app ...
- Linux 操作memcache命令行
telnet 127.0.0.1 11211 连接 memcache stats 查看 memcache 状态 状态说明: pid memcache服务器的进程ID uptime 服务器已经运行的秒数 ...
- 彻底搞清楚javascript中的require、import和export(js模块加载规范的前世今生)
为什么有模块概念 理想情况下,开发者只需要实现核心的业务逻辑,其他都可以加载别人已经写好的模块. 但是,Javascript不是一种模块化编程语言,在es6以前,它是不支持”类”(class),所以也 ...
- 错误Uncaught Error: Bootstrap's JavaScript requires jQuery at bootstrap.min.js:6 错误详解
引入Bootstrap的顺序进行修改 <% //获取以/开始,不以/结尾的部分 pageContext.setAttribute("APP_PATH", request.ge ...
- dnspy使用技巧
打开dnspy,调试–>附加到进程–>选择相应的进程ID–>附加(支持同时附加多个进程) 调试–>窗口–>模块–>搜索要调试的程序集–>双击(这一步很重要, ...