《你必须知道的495个C语言问题》读书笔记之第11-14章:ANSI C标准、库函数、浮点数
一、ANSI C标准
1. ANSI向C语言预处理器引入了几项新的功能,包括“字符串化”操作符(#)、“符号粘贴”操作符(##)、#pragma指令。
2. Q:char a[3] = "abc";合法吗?
A:它在ANSI C标准中是合法的,它声明了一个长度为3的数组,把它的3个字符初始化为'a', 'b'和'c',但没有终止符'\0',因此该数组并不是一个真正的C字符串,从而不能用在strcpy, printf %s等语句中。
3. Q:memcpy()和memmove()有什么区别?
A:如果源和目的参数有重叠,memmove()能提供有保证的行为,而memcpy()则不能提供这样的保证,因此可以实现得更加有效率。如果有疑问,最好使用memmove()。
4. Q:为什么ANSI标准竟然有那么多未定义的东西?标准的唯一任务不就是让这些东西标准化吗?
A:某些构造随编译器和硬件的实现而变化,这一直是C语言的一个特点。这种有意的不严格可以让编译器生成效率更高的代码,而不必让所有程序为了不合理的情况承担额外的负担。因此,标准只是把现存的实践整理成文。
编程语言标准可以看做是语言使用者和编译器实现者之间的协议。协议的一部分是编译器实现者同意提供、用户可以使用的功能,而其他部分则包括用户同意遵守和编译器实现者认为会被遵守的规则。只要双方都恪守自己的保证,程序就可以正确运行。如果任何一方违背它的诺言,结果肯定失败。
二、标准输入输出库
1. Q:我有个读取字符直到遇到EOF的程序,我该如何在键盘中输入“EOF”来终止读入呢?
A:根据你的操作系统,可能使用不同的按键组合来表示文件结束,通常是Ctrl-D或Ctrl-Z。(我的电脑是Ctrl-Z)
2. 使用printf输出long型整数时,格式字符串应该使用"%ld",而非"%d"。
3. Q:为什么printf()用%f输出double型,而scanf却用%lf呢?
A:printf的%f说明符确实既可以输出float型又可以输出double型,根据“默认参数提升”规则,float型会被提升为double型,因此printf()只会看到双精度数。对于scanf,情况就不同了,它接受指针,这里没有类似的类型提升。通过指针向float存储和向double存储不一样,因此scanf区别%f和%lf。
4. Q:对于size_t型变量,当我不知道它的具体类型时,如何使用printf输出它的值?
A:把那个值转换为一个已知的长度够大的类型,然后使用与之对应的printf格式。例如:
printf("%lu", (unsigned long)sizeof(sometype));
5. Q:如何用printf实现可变的域宽度?就是说,我想在运行时确定宽度而不是使用%8d?
A:使用printf("%*d", width, x)。格式说明符中的星号表示,参数列表中的一个int值用来表示域的宽度。注意在参数列表中。宽度在输出的值之前。
6. Q:如何将一行的任意多个域读入一个数组中?
A:(a) 使用带有正确格式串的fscanf和sscanf。另外,scanf格式串也可以用来处理紧凑的数据,如"1234ABC5.678"就可以用"%d%3s%f"读出。
(b) 用strtok或等价的工具将数据行分解为用空白隔开的域,然后用atoi或atol等函数单独处理各个域。这个方法尤其适用于将任意多个域的一行读入一个数组中。
#include<stdlib.h>
#define MAXARGS 10 char *av[MAXARGS];
int ac, i;
double array[MAXARGS];
char line[] = "1 2.3 4.5e6 789e10"; ac = makeargv(line, av, MAXARGS);
for (i = ; i < ac; i++) {
array[i] = atof(av[i]);
}
7. Q:我用"%d\n"调用scanf从键盘读取数字时,为什么要多输入一行才返回?
int n;
scanf("%d\n", &n);
printf("you type %d\n", n);
A:"\n"在scanf格式字符串中不表示等待换行符,而是读取并放弃连续的空白字符,事实上scanf格式串中的任何空白字符都表示读取并放弃空白字符。因此,"%d\n"中的"\n"会让scanf读到非空白字符为止。所以应该去掉"\n"仅仅使用"%d"即可。
8. Q:我用scanf和%d读取一个数字,然后再用gets()读取字符串,但是编译器好像跳过了gets()调用!
int n;
char str[]; printf("Enter a number:\n");
scanf("%d", &n);
printf("Enter a string:\n");
gets(str);
printf("you typed %d and \"%s\"\n", n, str);
A:如果你向问题中的程序输入两行,假设分别是"42"和"a string",scanf会读取42,但却不会读到紧接其后的换行符。换行符会保留在输入流中,然后被gets()读取,后者会读入一个空行,而第二行的"a string"则根本不会被读取。
作为一个一般规则,不能混用scanf和gets或任何其他的输入例程的调用,scanf对换行符的特殊处理几乎一定会带来问题。要么就用scanf处理所有的输入,要么干脆不用。
9. Q:为什么下面的程序有时候会陷入无限循环?
int n;
while ()
{
printf("Enter a number:\n");
if ( == scanf("%d", &n)) // if input isn't a number, the program would calculate infinitely.
break;
printf("try again:\n");
}
A:在scanf转换数字时,它遇到的任何非数字字符都会终止转换并被保留在输入流中。因此,除非采用了其他步骤,否则未预料的非数字输入会不断“阻塞”scanf,因为scanf永远都不能越过错误的非数字字符而处理后面的合法数字字符。
你可能很奇怪为什么scanf会把未匹配的字符留在输入流。假设你有一行文本"123code",你希望用"%d%s"格式的scanf来解析这行文本。但是如果%d不把未匹配的字符保留在输入流中,则%s会错误地读入"ode"而不是"code"。这是词法分析中的一个标准问题:在扫描任意长度的数字常量或字母数字字符串时,只有读到越界位置,你才能知道它已结束。
10. Q:为什么大家都说不要使用scanf?那我该用什么代替?
A:scanf存在许多问题,除了上述问题,它的"%s"格式和gets()一样,都很难保证接收的缓冲区不溢出。
一般来说,scanf正如其名"scan formatted"所示,适用于相对结构化的、格式整齐的输入,如果你注意,它会告诉你成功或失败,但它只能提供失败的大概位置,对scanf做错误恢复几乎是不可能的。对缺乏格式化的输入,可以先用fgets读入整行,然后再用scanf等函数(strtol, strtok和atoi等)进行解释。
如果数据文件格式已知,使用fscanf可能很合适。如果希望匹配失败后可以很容易地恢复控制、重启扫描或放弃输入,可以使用sscanf。
11. Q:为什么连一个最简单的fopen调用都不成功!
FILE *fp = fopen(filename, 'r');
A:问题在于fopen的mode参数必须是字符串,如"r",而非字符('r')。
12. Q:为什么我不能用完整路径名打开一个文件?
fopen("C:\newdir\file.dat", "r");
A:你可能需要重复那些反斜杠。因为反斜杠字符是'\\',而不是'\'。
三、库函数
1. Q:怎样把数字转换成字符串?
A:C提供sprintf作为通用解决方法,以把int型、long型或浮点数转换成字符串。
sprintf(str, "%d", inum);
sprintf(str, "%ld", lnum);
sprintf(str, "%f", fnum);
2. Q:为什么strncpy不能总在目标串放上终止符'\0'?
A:strncpy最初被设计为用来处理一种现已废弃的数据结构——定长、不必以'\0'结束的字符串,其他环境下使用strncpy有些麻烦,因为必须经常在目的串末尾手工加上'\0'。另外,strncpy的另一个相关的怪癖是它会用多个'\0'填充短串,直到达到指定的长度,这样允许更有效的字符串比较。
3. Q:C语言有没有类似其他语言中的"substr"(取出子串)的例程?
A:没有。C语言没有提供自动管理的字符串类型。要从字符串的pos位置取长度为len的子串,可以用:
// method 1:
char dest[LEN+];
strncpy(dest, &src[POS], LEN);
dest[LEN] = '\0'; // method 2:
char dest[LEN+] = "";
strncat(dest, &src[POS], LEN);
4. Q:怎样将字符串分割成用空白分隔的字段?
A:标准中唯一用于这种分隔的函数是strtok,虽然用起来需要些技巧。
#include<stdio.h>
#include<string.h>
char str[] = "today is a rainy day.";
char *p;
for (p = strtok(str, " \t\n"); p != NULL; p = strtok(NULL, " \t\n"))
printf("\"%s\"\n", p);
5. Q:我想用strcmp作为比较函数,调用sort对一个字符串数组排序,但是不行,为什么?
A:qsort比较函数的参数是被排序对象的指针,在这里是字符指针的指针,然而strcmp只接受字符指针。因此不能直接使用strcmp,要用下边这样的间接比较函数:
/* compare strings via pointers */
int pstrcmp(const void *p1, cosnt void *p2)
{
return strcmp(*(char * const *)p1, *(char * const *)p2);
} /* use pstrcmp() as the compare function of qsort()*/
#include<stdlib.h>
char *str[LEN];
qsort(str, LEN, sizeof(char *), pstrcmp);
注意,因为qsort用了通用的方式来处理未知类型的内存块,所以它用通用指针(void *)来引用它们。当qsort调用比较函数时,它把将要比较的两块内存以两个通用指针的形式传入你的函数。因为它使用通用指针,比较函数必须接受通用指针,然后在使用前(例如比较前)将其转换回它们本来的类型。
四、浮点数
1. Q: 如何检查浮点数在“足够接近”情况下的相等?
A:浮点数的定义决定它的绝对精度会随着其量级而变化,所以比较两个浮点数的最好方法是利用一个与浮点数的量级相关的精确阈值。
double a, b;
...
if (a == b) // error
if (fabs(a-b) < 0.0001) // not good
if (fabs(a-b) <= epsilon * fabs(a)) // good
2. Q:怎样取整?
A:C语言的浮点数到整数的转换会去掉小数部分,因此取整之前加上0.5会使>=0.5的小数部分进位(对负数是减去0.5)。
// 浮点数四舍五入取整
(int) ((x < )? (x - 0.5) : (x + 0.5))
《你必须知道的495个C语言问题》读书笔记之第11-14章:ANSI C标准、库函数、浮点数的更多相关文章
- 你必须知道的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语言问题》读书笔记之第4-7章:指针
1. Q:为什么我不能对void *指针进行算术运算? A:因为编译器不知道所值对象的大小,而指针的算法运算总是基于所指对象的大小的. 2. Q:C语言可以“按引用传参”吗? A:不可以.严格来说,C ...
- 《你必须知道的495个C语言问题》知识笔记及补充
1. extern在函数声明中是什么意思? 它能够用作一种格式上的提示表明函数的定义可能在还有一个源文件里.但在 extern int f(); 和 int f(); 之间并没有实质的差别. 补充:e ...
- 你必须知道的495个C语言问题,学习体会四
本文,我们来学习下指针,这是个梦魇啊.无数次折磨着C语言学习者,无数次的内存泄露,无数次的访问失败,无数次的越界溢出, 这些错误造就的仅仅是一个 跟随者,真正的优秀者必须要正视语言的局限,同时在最大限 ...
- 你必须知道的495个C语言问题,学习体会三
本文是 本系列的第三篇,本文主要对C语言的表达式做个小结 先从两个坑爹的表达式说起:i++ 与++i 上大学的时候,学长告诉我,这两个表达式,意义是一样的,后来老师纠正说,还是有区别的,于是让我们记住 ...
- 你必须知道的495个C语言问题,学习体会二
这是本主题的第二篇文章,主要就结构体,枚举.联合体做一些解释 1.结构体 现代C语言编程 结构化的基石,diy时代的最好代言人,是面向对象编程中类的老祖宗. 我们很容易定义一个结构体,比如学生: st ...
- 《你必须知道的495个C语言问题》读书笔记之第8-10章:字符串、布尔类型和预处理器
一.字符和字符串 1. Q:为什么strcat(string, '!')不行? A:strcat()用于拼接字符串,所以应该写成strcat(string, "!")." ...
随机推荐
- 彻底搞懂prototype和__proto__
prototype是函数特有的属性,是Function的静态属性:__proto__是对象特有的属性. 因为函数本身是一种对象,所以函数既有prototype属性也有__proto__属性. 当函数使 ...
- spring boot定时任务
介绍 该demo是基于注解(@Scheduled)以及多线程执行的定时任务. 步骤 启用异步执行 springboot实现异步调用 入口类添加启动注解 @EnableScheduling @Enabl ...
- C#winform如何实现文本编辑框(TextBox)的Hint提示文字效果
C#winform如何实现文本编辑框(TextBox)的Hint提示文字效果 private const int EM_SETCUEBANNER = 0x1501; [DllImport(" ...
- 解决Maven的jar包冲突问题
1. 问题描述 控制台说:无法将 com.zpx.servlet.MyServlet 识别为 javax.servlet.Servlet 2. 问题原因 Maven的一个核心功能就是一键构建,所以Ma ...
- GA函数优化
一.遗传算法简介 遗传算法(Genetic Algorithms,GA)是1962年美国人提出,模拟自然界遗传和生物进化论而成的一种并行随机搜索最优化方法. 与自然界中“优胜略汰,适者 ...
- Ubuntu18.04初始的systemd service
Ubuntu18.04初始的systemd service 两个位置 /etc/systemd/system root@dev2:~# ls /etc/systemd/system aliyun.se ...
- 在HTML中直接使用onclick很不专业
原因 1.onclick添加的事件处理函数是在全局环境下执行的,这污染了全局环境,很容易产生意料不到的后果: 2.给很多DOM元素添加onclick事件,可能会影响网页的性能,毕竟网页需要的事件处理函 ...
- 在基于Android以及Jetson TK平台上如何写32位的Thumb-2指令
由于Android以及Jetson TK的编译工具链中的汇编器仍然不支持大部分的32位Thumb-2指令,比如add.w,因此我们只能通过手工写机器指令码来实现想要的指令.下面我将简单地介绍如何在AR ...
- linux高可用集群(HA)原理详解
高可用集群 一.什么是高可用集群 高可用集群就是当某一个节点或服务器发生故障时,另一个节点能够自动且立即向外提供服务,即将有故障节点上的资源转移到另一个节点上去,这样另一个节点有了资源既可以向外提供服 ...
- 16. kubernetes RBAC
16. kubernetes RBAC授权插件: Node,ABAC,RBAC,webhock RBAC: role based access contrl 基于角色的授权. 角色:(role)许可( ...