语义”陷阱“

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陷阱与缺陷》第三章阅读笔记相关推荐

  1. c陷阱与缺陷第三章——Semantic Pitfalls

    c陷阱与缺陷第三章--Semantic Pitfalls 3.1 Pointers and array Array a[i] == i[a] Array subscripting Constrains ...

  2. 西瓜书第三章阅读笔记

    西瓜书第三章阅读笔记 第三章 线性模型 1.机器学习三要素 2.基本形式 3.线性回归 3.1 模型 3.2 策略 3.3 求解算法 4.对数几率回归 4.1 模型 4.2 策略 4.3 求解算法 5 ...

  3. 机器学习(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) ...

  4. 工程伦理第三章学习笔记2020最新

    工程伦理第三章学习笔记2020最新 因为之前自己在网上找答案总是觉得费劲,一道一道的找,很慢,突然找到了前两章的答案,感觉有一种前人种树后人乘凉的感觉,于是自己在艰难找完第三章习题并全对的情况下,将题 ...

  5. 多维随机变量及其分布——《概率论及其数理统计》第三章学习笔记

    多维随机变量及其分布--<概率论及其数理统计>第三章学习笔记 文章目录 多维随机变量及其分布--<概率论及其数理统计>第三章学习笔记 前言 MindMap 二维随机变量 定义与 ...

  6. 周志华西瓜书第三章学习笔记

    第三章学习笔记 文章目录 第三章学习笔记 1.知识脉络 2.我的笔记 参考 1.知识脉络 2.我的笔记 这一章公式推导实在太多了,需要补充的推导过程也有很多,就不写电子档了.扩展公式推导和LDA部分补 ...

  7. 西瓜书第四章阅读笔记

    西瓜书第四章阅读笔记 1.基本概念 1.1 基本算法 1.2 信息熵 1.3 信息增益 2.ID3决策树 3.C4.5决策树 4.CART决策树 5.剪枝操作 6.连续与缺失值处理 7.多变量决策树 ...

  8. 西瓜书第一章阅读笔记

    西瓜书第一章阅读笔记 第一章 绪论 1.机器学习基本术语 2.归纳偏好 3.所有学习算法一样优秀? 4.补充资料 第一章 绪论 1.机器学习基本术语 记录:对一个事件或对象的描述,也称为"示 ...

  9. 西瓜书第二章阅读笔记

    西瓜书第二章阅读笔记 第二章 模型评估与选择 1.经验误差与过拟合 2.模型评估方法 2.1 留出法 hold out 2.2 交叉验证法 cross valildation 2.3 自助法 boot ...

最新文章

  1. 机器视觉-相机内参数和外参数
  2. 09最短小说:意见统一
  3. 【错误总结】Git- remote:error: this exceeds GitHub file size limit of 100.00 MB
  4. Backtrack5 下WEB模糊测试
  5. 如何让python图案旋转_python实现旋转和水平翻转的方法
  6. 关联tomcat源代码
  7. 【控制】《多无人机协同控制技术》周伟老师-第12章-基于 Multi-Agent 的多无人机协同控制仿真平台的设计与实现
  8. 开发一个自己的 CSS 框架(五)
  9. mongodb和mysql的对比_MongoDB和MySQL的区别
  10. 完全自主可控的安全关键领域仿真测试软件SkyEye可替代SCADE
  11. C++Qt笔记-完美屏蔽IDEA中accept2弹窗(无需重装软件、无需重装系统)
  12. CentosNginx
  13. 【算法导论学习-29】动态规划经典问题02:最长公共子序列问题(Longest common subsequence,LCS)...
  14. kali安卓手机木马远控
  15. 数据分析师工资高吗?数据分析师6大职业方向+薪资预估
  16. 夜神模拟器和虚拟机(docker) 在windows上设置不兼容
  17. 现有存储系统技术架构
  18. javascript 判断参数是否为非0整数数字或者整数数字字符串的简单方法(小装逼)
  19. 1 Nginx性能优化方法
  20. 达达开放平台php,达达开放平台对接的使用教程

热门文章

  1. Ubuntu 12.04下安装搜狗拼音 + 安装搜狗皮肤
  2. Java项目管理系统 的设计与实现
  3. 现如今软件测试还有前景吗?
  4. 路径之谜 java_路径之谜 - 给杰瑞一块奶酪~ - 博客园
  5. 设置了监视哨的顺序查找算法效率高?你确定吗?
  6. 工作中及时沟通很重要
  7. Elixir-List
  8. OS实战笔记(4)-- 虚拟地址和物理地址的转换
  9. 替换MP9486A 替代MP4689 替换LM5007 LM5017 国产芯片内置150V做降压恒压4.2V GPS防盗器专用芯片
  10. 从初级工程师发展到高级工程师,需要跨越的鸿沟