《C陷阱与缺陷》第三章阅读笔记
语义”陷阱“
3.1 指针与数组
C语言中数组值得注意的地方有以下两点:
1.C语言中只有一维数组,而且数组的大小必须在编译期就作为一个常数确定下来。然而,C语言中数组的元素可以是任何类型的对象,当然也可以是一个数组。这样,”仿真“出一个多维数组就不是一件难事。
(注:C99标准允许变长数组(VLA)。GCC编译器中实现了变长数组,但细节与C99标准不完全一致。)
2.对于一个数组,我们只能够做两件事:确定数组大小,以及获得指向该数组下标为0的元素的指针。其他有关数组的操作,实际上都是通过指针进行的。
给指针加上一个整数,如p+1,则p指向下一内存中的数据,而给指针的二进制表示(指针指向的地址)加上同样的整数,实际上是将p指向的地址+1,效果不一样。
如果两个指针指向的是同一个数组中的元素,我们可以把这两个指针相减。若两个指针指向的是不同数组中的元素,即使它们指向的地址在内存中位置正好间隔一个数组元素的整数倍,所得的结果仍然无法保证其正确性。
数组命除了被用作sizeof的参数这一情况外,其他所有情况下数组命都代表指向数组a中下标为0的元素的指针。
二维数组遍历代码如下:
int i[12][31];int (*p)[31];for (p = i; p < &i[12]; p++){int *dp;for(dp = *p; dp < &(*p)[31]; dp++){*dp = 0;}}
3.2 非数组指针
在使用内存分配函数(malloc)的时候,需要注意的是,如果分配字符串空间,一定要注意’\0’字符,该字符在使用strlen函数求字符串长度的时候会被忽略。使用malloc函数对应的内存用完要使用free函数释放内存。
示例代码如下:
char *r;char s[] = "Hello";char t[] = "World!";r = malloc(strlen(s) + strlen(t) + 1);if (r != NULL){strcpy(r, s);strcat(r, t);}printf("%s\n", r );free(r);
3.3 作为参数的数组声明
C语言会自动将作为参数的数组声明转换为相应的指针声明。
eg1:
int strlen(char s[])
{
}
与下面的写法完全相同
int strlen(char* s)
{
}
指针并不是所有情况下都指向数组首地址。
eg2:
extern char* hello;
与
extern char hello[];
前者只声明了一个字符型指针,而后者声明了字符数组,二者代表的概念不一样。
3.4 避免“举隅法”
”举隅法“是文学修辞上的手段,以隐喻表示代指物与被指物的相互关系。《牛津英语词典》对”举隅法“(synecdoche)的解释是:以含义更宽泛的词语来代替含义相对较窄的词语,或者相反。
C语言中的一个常见陷阱:混淆指针与指针所指向的数据。
复制指针并不同时复制指针所指向的数据。
3.5 空指针并非空字符串
C语言中将一个整数转换为一个指针,最后得到的结果取决于具体的C编译器实现。存在一个特殊情况0,编译器保证由0转换而来的指针不等于任何有效指针。
char *p;
// p = (char*)3;
// p = NULL;p = (char*)0;printf("%s\n", p); //未定义行为
// printf("%d\n", p); //打印出具体数字
3.6 边界计算与不对称边界
此处存在”栏杆错误“,也常被称为”差一错误“(off-by-one error)
避免”差一错误“的两个通用原则:
1.首先考虑最简单情况下的特例,然后将得到的结果外推
2.仔细计算边界,绝不掉以轻心
eg1:
static char buffer[N];
static char *bufptr;
char* clearbuffer(char* source);
void bufferwrite(char* source, int len)
{char* data = source;while (len > 0){
// if (N - (&buffer[N] - bufptr) < N)
// {
// *bufptr++ = *data++;
// len--;
// }
// else
// {
// bufptr = clearbuffer(buffer);
// }if (bufptr == &buffer[N]){int l = sizeof(buffer);bufptr = clearbuffer(buffer);}else{int k, rem;rem = N - (bufptr - buffer);k = len > rem? rem:len;memcpy(bufptr, data, k);bufptr += k;data += k;len -= k;}}
}
char* clearbuffer(char* source)
{memset(source, 0, N);return source;
}
3.7 求值顺序
C语言中只有”&&“、”||“、”? :“、”,“四个运算符,存在贵定的求值顺序。
”&&“运算符和”||“运算符首先对左操作数求值,只在需要时才对右操作数求值。
运算符”? :“有三个操作数:在a?b:c中,操作数a首先被求值,根据a的值再求操作数b或者操作数c的值。
逗号操作符首先对左操作符求职,然后该值被”丢弃“,再对右操作数求值。
(注:分隔函数参数的逗号并非逗号运算符。例如,x和y在函数f(x,y)中的求值顺序是未定义的,而在函数g((x,y))中是确定的,先求x后求y。在后一个例子中,函数g只有一个参数,这个参数的值是这样求到的,先求x的值,然后x的值被”抛弃“,接着求y的值。)
C语言中其他所有运算符对其操作数求值的顺序是未定义的,特别地,赋值运算符并不保证任何求值顺序。
3.8 运算符&& || 和!
C语言中有两类逻辑运算符,某些时候可以互换:按位运算符& |和~。以及逻辑运算符&& ||和!。
逻辑运算符&&和||在左侧操作数的值能够确定最终结果时根本不会对右侧操作数求值。
运算符&左右两边的操作数都必须被求值。
3.9 整数溢出
C语言中存在两类整数运算,有符号运算与无符号运算。在无符号运算中,没有所谓的”溢出“一说。所有无符号运算都是以2的n次方为模,这里n是结果中的位数。如果超出表示范围,则从0开始继续运算。
如果算术运算符的一个操作数是有符号整数,另一个操作数是无符号整数,那么有符号整数会被转换成无符号整数,”溢出“也不可能发生。
当两个操作数都是有符号整数时,”溢出“就有可能发生,而且”溢出“的结果时未定义的。
3.10 为函数main提供返回值
函数如果未显示声明返回类型,那么函数返回类型的默认类型是整型。
main函数的返回值在不使用的情况下无关紧要,如果返回值表示函数是否执行成功,则需要明确具体的返回值,不能不写返回值,否则有可能系统会判断函数执行失败。
《C陷阱与缺陷》第三章阅读笔记相关推荐
- c陷阱与缺陷第三章——Semantic Pitfalls
c陷阱与缺陷第三章--Semantic Pitfalls 3.1 Pointers and array Array a[i] == i[a] Array subscripting Constrains ...
- 西瓜书第三章阅读笔记
西瓜书第三章阅读笔记 第三章 线性模型 1.机器学习三要素 2.基本形式 3.线性回归 3.1 模型 3.2 策略 3.3 求解算法 4.对数几率回归 4.1 模型 4.2 策略 4.3 求解算法 5 ...
- 机器学习(Machine learning: a probabilistic perspective) 第三章阅读笔记
生成式分类器(generative classifiers) 判定特征向量x是否属于某一类型 p(y=c|x)=p(y=c)p(x|y=c)∑c′p(y=c′|θ)p(x|y=c′) p(y=c|x) ...
- 工程伦理第三章学习笔记2020最新
工程伦理第三章学习笔记2020最新 因为之前自己在网上找答案总是觉得费劲,一道一道的找,很慢,突然找到了前两章的答案,感觉有一种前人种树后人乘凉的感觉,于是自己在艰难找完第三章习题并全对的情况下,将题 ...
- 多维随机变量及其分布——《概率论及其数理统计》第三章学习笔记
多维随机变量及其分布--<概率论及其数理统计>第三章学习笔记 文章目录 多维随机变量及其分布--<概率论及其数理统计>第三章学习笔记 前言 MindMap 二维随机变量 定义与 ...
- 周志华西瓜书第三章学习笔记
第三章学习笔记 文章目录 第三章学习笔记 1.知识脉络 2.我的笔记 参考 1.知识脉络 2.我的笔记 这一章公式推导实在太多了,需要补充的推导过程也有很多,就不写电子档了.扩展公式推导和LDA部分补 ...
- 西瓜书第四章阅读笔记
西瓜书第四章阅读笔记 1.基本概念 1.1 基本算法 1.2 信息熵 1.3 信息增益 2.ID3决策树 3.C4.5决策树 4.CART决策树 5.剪枝操作 6.连续与缺失值处理 7.多变量决策树 ...
- 西瓜书第一章阅读笔记
西瓜书第一章阅读笔记 第一章 绪论 1.机器学习基本术语 2.归纳偏好 3.所有学习算法一样优秀? 4.补充资料 第一章 绪论 1.机器学习基本术语 记录:对一个事件或对象的描述,也称为"示 ...
- 西瓜书第二章阅读笔记
西瓜书第二章阅读笔记 第二章 模型评估与选择 1.经验误差与过拟合 2.模型评估方法 2.1 留出法 hold out 2.2 交叉验证法 cross valildation 2.3 自助法 boot ...
最新文章
- 机器视觉-相机内参数和外参数
- 09最短小说:意见统一
- 【错误总结】Git- remote:error: this exceeds GitHub file size limit of 100.00 MB
- Backtrack5 下WEB模糊测试
- 如何让python图案旋转_python实现旋转和水平翻转的方法
- 关联tomcat源代码
- 【控制】《多无人机协同控制技术》周伟老师-第12章-基于 Multi-Agent 的多无人机协同控制仿真平台的设计与实现
- 开发一个自己的 CSS 框架(五)
- mongodb和mysql的对比_MongoDB和MySQL的区别
- 完全自主可控的安全关键领域仿真测试软件SkyEye可替代SCADE
- C++Qt笔记-完美屏蔽IDEA中accept2弹窗(无需重装软件、无需重装系统)
- CentosNginx
- 【算法导论学习-29】动态规划经典问题02:最长公共子序列问题(Longest common subsequence,LCS)...
- kali安卓手机木马远控
- 数据分析师工资高吗?数据分析师6大职业方向+薪资预估
- 夜神模拟器和虚拟机(docker) 在windows上设置不兼容
- 现有存储系统技术架构
- javascript 判断参数是否为非0整数数字或者整数数字字符串的简单方法(小装逼)
- 1 Nginx性能优化方法
- 达达开放平台php,达达开放平台对接的使用教程