这本书短短的100多页,很象是一篇文章。但是指出的很多问题的确容易出现在笔试的改错题中

--------------------------------------------------------------------
第1章 词法陷阱

1.1 = 和 ==

1.3 词法分析的"贪心法则"
编译器从左到右读入字符,每个符号包含尽可能多的字符,直到不是字符为止

如:
a---b  等价于  a-- - b

a/*b   并不是 a/(*b), 而是/*当作注释符

1.4 整型常量

0开头的整数为8进制

如:
014  为8进制, 不要误看作10进制

1.5 字符和字符串

单引号 - ASCII字符,实际上是一个整数
双引号 - 指向匿名的字符数组起始字符的指针,该数组以引用的字符和额外的'\0'初始化

另外,大多数编译器允许一个字符常量中包含多个字符。Borland C++中只取第一个字符,而VC 6.0和GCC中则后面的字符依次覆盖前面的字符,最后得到的是最后一个字符。

如:(GCC 3.5)
char ch='yes';
cout << ch;//output: s, but with warning

练习题:
1-1。某些C编译器允许嵌套注释。请写一个测试程序,要求:无论对是否允许嵌套注释的编译器,该程序都能正常通过编译(无错误消息),但是这两种情况下程序执行的结果却不同。

1-3. n-->0的含义?
(n--) > 0     贪心法则

1-4 a+++++b的含义?
((a++) ++) + b
但是值得一提的是:现代编译器中,此式子是非法的

为什么?
你可以查看operator++(int)的原型: const int operator++(int)
返回的为const型,且为a的临时拷贝。之所以返回为const型,就是为了将返回作为左值,也就防止出现这类式子
总结:a++不能做左值

---------------------------------------------------------------------------------------
第2章 语法陷阱

2.1 理解函数声明
换个角度理解声明语句(declaration)
声明语句构成:类型 + 一组类似表达式的声明符(declarator)

float f, g; //表达式f, g求值为浮点数,即f, g为浮点型
float ff(); //表达式ff()求值是一个浮点数,即ff是一个返回类型为浮点数的函数
float *pf; //*pf求值是一个浮点数,即pf是指向浮点型的指针
更复杂的,
float *g(), (*h)();
依据上述并且()优先级大于*,很容易知道g是一个函数,返回类型为浮点指针(float *)
h为一个函数指针,函数返回类型为float
(float (*h)()) 是一个类型转换符

再看:
(*(void(*)())0)()
实质上是:
void (*fp)(); //declare a function pointer
typedef void (*fp_type)();// for simplifying, otherwise always need void(*)()
conv_0 = (fp_type)0; // converse function 0 into function conv_o
(*conv_0)();//using the conversed function

再让我们看看<signal.h>中声明的signal函数
void (*signal(int, void(*)(int)))(int)
首先,用typedef简化,
typedef void (*handler_type)(int)
得,void (*signal(int, handler_type))(int)
进一步 handler_type signal(int, handler_type);

2.3 作为语句结束的分号
1)多写了分号
if(x[i] > big);
   big = x[i]
这还是很容易辨识
再看
2)漏写了分号

if(n < 3)
    return
logrec.date = x[0];
logrec.time = x[1];
logrec.code = x[2];

看出问题来了没?

继续看下面一个经典的:

struct logrec
{
    int date;
    int time;
    int code;
}

main()
{
    //
}

注:这个问题笔试题已经出现过

2.4 swith语句
这个估计是老生常谈了

也就是case后的break有无的问题了

首先要搞清楚一件事:你可以把(case:)当作语句的标号,就好像汇编中的标号一样。switch之后径直跳到匹配的case处顺序执行下去,以后再碰到case则无视

当然,程序设计中有意不要break的除外

2.6 “空悬”else引发的问题

看下面代码:

if(x == 0)
    if(y == 0) error();
else{
    z = x + y;
    f(&z);
}

这段代码可能与你的本意大相径庭,因为else与最近的if匹配

防止这类问题很简单,只要每次使用if,else都用{}

---------------------------------------------------------
第3章 语义陷阱

3.1 指针和数组

C语言数组需要注意:
1)C语言只有一维数组,数组大小必须在编译期确定为常数。二维数组是通过数组元素也为数组的一维数组实现
2)对于一个数组,只能做2件事情:确定数组大小,取得指向数组首元素的指针。其他相关操作,如下标运算, 都是通过指针进行

int a[3]; //数组元素为int型
struct
{
    int p[4];
    double x;
}b[17]; //数组元素为结构体
int calendar[12][31];
//12个元素的数组,每个元素又是31元素的数组
//并非:31个元素的数组,每个元素是12个元素的数组

记住:数组名是指向该数组首元素的指针
如,int a[11];  //那么a的类型为 (int *)
int *ptr;
ptr = a;
但是ptr = &a;是非法的,这里&a的类型为int (*)[],即指向数组的指针,大多数编译期对这种操作,或者视为非法,或者让其等于a

在C中,a+i和i+a的含义是一样的,但后者不推荐

下面看多维数组:
int calendar[12][31];
int *p;
int i;

我们很容易知道calendar[4]表示什么含义:calendar[4]表示calendar数组的第5个元素,是12个有31个元素的数组之一。
sizeof(calendar[4])结果为31×sizeof(int)

此例中,calendar名字转换为一个指向数组的指针,其类型为int (*)[31]
于是p=calendar; 是非法的

int (*monthp)[31];
monthp = calendar;//OK

calendar[month][day] = 0;
等价于
*(*(calendar+month)+day) = 0;
怎样分析这个呢?
首先,calendar+month是指向12个元素之一的指针,对其解引用得到就是其元素(而元素是数组),所以*(calendar+month)是指向含31个元素的数组首元素的指针,再偏移然后解引用即得到最终的int型元素

总结:
1)数组名表示指向首元素的指针,类型为元素类型的指针
2) 对数组名取地址,为指向数组的指针,类型为数组的指针

3.2 非数组的指针 - 字符串

字符串常量:代表一块包含字符串中所有字符加上额外一个空字符('\0')的内存区的地址。

一般字符串常量用字符数组保存的,且是只读的。

字符串操作函数:
size_t strlen(char *);//计算字符串长度,直到遇到'\0'.且不包括'\0'
int strcpy(char * dest, const char *src);
int strcat(char *dest, char *src);

注意其中的输出参数dest必须是预先分配好,且有足够的空间能容纳

3.3 数组作为函数参数

自动转换成指针

3.6 边界计算与不对称边界

这个主题值得探讨

3.7 求值顺序

C中只有四个运算符(&&, ||, ? :和,)规定了求值顺序,对于其他运算符不要错误的假设求值顺序,他们求值顺序是未定义的。

如:
i = 0;
while(i < n)
    y[i] = x[i++];

这里y[i]的地址在i自增前被求值是没有任何保证的

3.9 整数溢出

C语言中存在2类整数算术运算:有符号运算与无符号运算。

两个无符号数运算不存在溢出。

算术运算中一个是有符号数,另一个是无符号数,则有符号数会转换为无符号数,运算时溢出也不可能发生。

两个有符号数运算,溢出有可能发生。并且溢出发生时,溢出结果是未定义的。

那么如何检测是否发生溢出呢?
看下面的方式:
int a, b;
if(a + b < 0)
    //do something

这种方式是不可靠的,因为对溢出结果做的任何假设都是不可靠的

正确的方式:
#include <limits.h>
int a, b;
if((unsigned)a + (unsigned)b > INT_MAX)
    //...
或者
if(a > INT_MAX - b)
    //...

--------------------------------------------------------------------------------------
第4章 连接

4.2 声明与定义

下面声明语句:
int a;
如果出现在所有函数体(包括main函数)之外, 它被成为外部对象a的定义,并且其初始值默认为0

下面声明语句:
int a = 7;
定义a的同时指定了初始值

下面声明语句:
extern int a;
并不是a的定义,说明a是一个外部整型变量,它的存储空间在程序的其他地方分配

典型情况:
//file1.c
int a = 7;

//file2.c
int a = 9;

这种情况一般在连接时会报错,因为定义只能一次,声明却可以很多

4.3 命名冲突与static修饰符

static将变量或函数的作用域限定在一个源文件中了

4.5 检查外部变量

转载于:https://www.cnblogs.com/chio/archive/2007/10/26/938064.html

《C Traps and Pitfalls》 笔记相关推荐

  1. 《信贷的逻辑与常识》笔记

    序 银行信贷风险管理的反思 现状与趋势 银行贷款的质量变化与经济周期.宏观调控政策等存在很高的相关性 现在银行不良贷款的增加主要是前几年经济快速增长时企业过度投资.银行过度放贷所带来的结果. 从历史情 ...

  2. AI公开课:19.02.27周逵(投资人)《AI时代的投资逻辑》课堂笔记以及个人感悟

    AI公开课:19.02.27周逵(投资人)<AI时代的投资逻辑>课堂笔记以及个人感悟 目录 课堂PPT图片 精彩语录 个人感悟 课堂PPT图片 精彩语录 更新中-- 文件图片已经丢失-- ...

  3. 人工智能入门算法逻辑回归学习笔记

    逻辑回归是一个非常经典的算法,其中也包含了非常多的细节,曾看到一句话:如果面试官问你熟悉哪个机器学习模型,可以说 SVM,但千万别说 LR,因为细节真的太多了. 秉持着精益求精的工匠精神不断对笔记进行 ...

  4. 【逻辑回归学习笔记】

    算法描述 1.逻辑回归要做的事就是寻找分界面实现二分类. 2.问题假设:对一堆三角形和正方形分类. 3.数据输入:已知正方形和三角形的坐标和标签. 4.算法过程: 知识储备 1.分类和回归 ①分类的目 ...

  5. 逻辑回归函数学习笔记

    继续逻辑回归学习,今日笔记记录. 1.逻辑回归和线性回归的关系:对逻辑回归的概率比取自然对数,则得到的是一个线性函数,推导过程如下. 首先,看逻辑回归的定义 其次,计算两个极端y/(1-y),其值为( ...

  6. 2.2 逻辑回归-机器学习笔记-斯坦福吴恩达教授

    逻辑回归 上一节我们知道,使用线性回归来处理 0/1 分类问题总是困难重重的,因此,人们定义了逻辑回归来完成 0/1 分类问题,逻辑一词也代表了是(1) 和 非(0). Sigmoid预测函数 在逻辑 ...

  7. LVM逻辑卷分区笔记

    磁盘的静态分区有其缺点:分区大小难评估,估计不准确,当分区空间不够用的时候,系统管理员可能需要先备份整个系统,清除磁盘空间,然后重新对磁盘进行分区,然后恢复磁盘数据到新分区,且需要停机一段时间进行恢复 ...

  8. 适合理工直男的钟平老师逻辑英语学习笔记

    一切的一切都只是套路!             --鲁迅 核心公式: En: (状语1) 主(定语1) 谓(状语2) (宾)(定语2) (状语1) Ch: (状语1) (定语1)主 (状语2)谓 (定 ...

  9. 【数字逻辑】学习笔记 第四章 Part2 常用组合逻辑电路与竞争、险象

    文章目录 一.常用组合逻辑电路 1. 译码器 (1) 二进制译码器 74LS138(3/8译码器) a. 一般符号和图形符号 b. 74LS138功能表 c. 两片 `74LS138` 构成 `4-1 ...

  10. 线性回归、逻辑回归学习笔记

    学习源代码 import numpy as np import matplotlib.pyplot as plt def true_fun(X): # 这是我们设定的真实函数,即ground trut ...

最新文章

  1. python好学吗mooc中文网-2020年大学mooc用Python玩转数据作业答案
  2. Python编程核心内容 ---- Function(函数)
  3. Gooogle Test中的TEST()宏代码分析
  4. 计算机数据库基础知识笔记,计算机基础及技巧(我的电脑笔记,适合新手!包含使用电脑最基本的知识及DOS、网络及数据库等。)...
  5. 【.NET】XML文件的创建,修改,删除
  6. only has output.xml, how to check failed step.
  7. 【luogu4185】 [USACO18JAN]MooTube [并查集]
  8. 原python基础概念整理_python基础知识脉络梳理总结
  9. MODIS R包下载数据
  10. b站的服务器在哪个文件夹,b站缓存的视频在哪个文件 具体操作步骤
  11. 半部秘籍--分类、回归、集成与无监督
  12. 【ARMv8基础篇】NIC-400控制器简介
  13. 【问题解决】电脑微信聊天记录备份时显示不在同一网络(电脑LAN,手机WIFI)
  14. SVG可伸缩的矢量图形 1
  15. php跳水比赛,2018年“迎春杯”海南省青少年健身游泳比赛成功举行 前奥运跳水冠军许艳梅见证海南游泳健儿风采...
  16. 【Java开源项目】消息推送平台 日志引入
  17. failed to connect to /172.20.100.117 (port 18899): isConnected failed: ECONNREFUSED (Connection refu
  18. 艿艿连肝了几个周末,写了一篇贼长的 Spring 响应式 Web 框架 WebFlux!市面第二完整~...
  19. 【毕业季】Three.js动态相册
  20. 《Node.js》中间件和洋葱圈模型

热门文章

  1. React开发(119):代码原因
  2. React开发(114):不建议用setstate回调
  3. [css] 用CSS绘制一个红色的爱心
  4. 工作277:v-model实战
  5. 前端学习(2754):配置tabber
  6. 工作235:splice
  7. 第七十六期:糟糕!服务器被植入挖矿木马,CPU飙升200%
  8. 第六十七期:全球500强公司的2100万登录信息惊现于暗网上!
  9. 43request对象 续
  10. java学习(24):if..else...if