• 《C陷阱与缺陷》阅读笔记(个人版)


    笔记


    第一章:词法陷阱

    提倡显示比较
    if((x = y) != 0)
    foo();

    第二章:语法陷阱

    已知一个类型的声明
    该类型的类型转换:吧声明中的变量名和声明末尾的分号去掉,再将剩余的部分用括号整个“封装”起来即可
    (*(void(*)())0);
    (void(*)()) 这是一个返回值为void的函数指针类型
    typedef void (*funcptr)() 中 funcptr 等价于 (void(*)())

    typedef void (*funcptr)(); //funcptr表示返回值为void的函数指针,
    可以引用同类型返回值的函数,有无参数都可以,
    但是如果显式声明带有参数,如typedef void (*funcptr)(int);则只能引用带有一个int参数返回void的函数
    当然没有参数的函数可以传递一个无效的参数进行引用
    void test(){return;} funcptr cp = (funcptr)test; cp(0);

    优先级问题:
    单目 大 双目 (移位 小 算术 关系紧跟后) 逻辑 赋值 条件
    终极必杀: 加括号

    第三章:语义陷阱

    C语言中只有一维数组,而且数组的大小必须在编译期就作为一个常数确定下来
    二维数组就是以数组为元素的数组
    int (*ap)[31] : *ap是一个拥有31个整型元素的数组,因此ap就是一个指向这样的数组的指针

    空指针 p = NULL 地址是无效的 并不是空字符串 空字符串是地址是有效的 只不过内存中的内容为空

    声明缓冲区
    #define N 1024
    static char buffer[N];

    提到一点:--n 可能要比 n++ 快 理由:n++要先保存n在减1 (理由不是太懂,感觉写的不清楚)

    运算符 && || 首先对左侧操作数求值,只在需要的时候才对右侧操作数求值 -> 让最可能对表达式起到作用的操作数在左边(提高速度)

    对于数组结尾之后的下一个元素,取它的地址是合法的,但是取地址内的内容是不合法的

    整数溢出
    无符号算术运算是以2的n次方为模(n是结果中的位数),相当于时钟循环 11点加2个小时 是1点(实际上也溢出了)
    两个操作数都是有符号整数时,“溢出”会出现
    if((unsigned)a + (unsigned)b > INT_MAX):转换为无符号数来判断是否溢出
    char a = 126;
    char b = 125;
    printf("十六进制:%x ",a);
    printf("十六进制:%x ",b);
    printf("十进制:%d ",a);
    printf("十进制:%d ",b);
    printf("%d ", CHAR_MAX);
    printf("------------------- ");
    printf("十六进制:%x ", (unsigned char)a + (unsigned char)b);
    printf("十进制:%d ", (unsigned)a + (unsigned)b);
    printf("差:%d ", (unsigned)a + (unsigned)b - CHAR_MAX);

    第四章:连接

    若干个文件分别编译 连接器将若干个C源程序整合成一个整体

    外部变量存在定义与声明 声明可以有多个 定义只能有一个 如果没有赋初始值 会自动初始化为0
    前面加 static修饰 可以起到“屏蔽”的作用 使得被修饰的变量或函数 值在本源文件中可见

    定义:char filename[] = "/etc/passed"; /*filename是一个字符数组的名称 类型是“字符数组”*/
    另一文件中声明:extern char *filename; /*filename是一个字符指针 类型是指针*/

    C语言的规则:如果一个未声明的标识符后跟一个开括号 那么它将被视为一个返回整型的函数
    main(){} 有返回值并且是整型

    第五章:库函数

    getchar函数的返回值类型是 整数(int)为了可以接收getchar返回的全部 需要定义一个 int类型的变量来接收它的返回值

    更新顺序文件
    为了保持与过去不能同时进行读写操作的程序的向下兼容性,一个输入操作不能随后直接紧跟一个输出操作,反之亦然。
    如果要同时进行输入和输出操作,必须在其中插入fseek函数的调用

    缓冲区输出与内存分配:setbuf(stdout, buf) 的使用有风险 ---- 经典错误

    errno signal 的使用复杂

    第六章:预处理器

    特别注意 宏定义中的空格 #define f (x) ((x) - 1) f -> (x) ((x) - 1)

    宏不是函数 只是一种替换 要多加括号防止意外
    带有多次计算的时候 要特别注意
    宏不是语句 定义时候一般不需要加 分号
    宏不是类型定义
    #define INTP int *
    INTP a, b; 替换后 int * a, b; 则a是int指针 b是int数据
    typedef int *INTP;
    INTP a, b; a b都是int指针

    第七章:可移植性缺陷

    函数声明中略去参数类型的说明,这在ANSI C 标准中也是合法的
    因为这样的声明并没有对参数类型做出任何说明,就意味着如果在函数调用时传入了错误类型的参数,函数调用就会不声不响地失败

    C语言实现必须能够区别出前6个字符不同的外部名称,而且,这个定义中并没有区分大小写字母

    为了移植性可以定义自己的数据类型:typedef long my_long

    (unsigned) c :c从char型转换为int型整数 再转换为无符号int 从8位到32位
    (unsigned char) c :c从char转换为无符号char
    char c = 'a';
    printf("%d ", sizeof((unsigned char) c)); //输出1
    printf("%d ", sizeof((unsigned) c)); //输出4

    移位运算
    左移 后面补0
    右移 逻辑 :无符号 使用逻辑:左边补0 / 算术 :有符号 使用算术:左边补符号位的副本
    一般非负 右移一位 <=> 除2 (整数运算)

    先释放在重新分配 --- 按照自己现有的方案写

    字符串常量可以用来表示一个字符数组
    "0123456789"[2] => '2' //取数组的下标为2的字符

    建议

    不要舒服自己相信“皇帝的新装”
    while(c == 't' || c = ' ' || c == ' ')
    c = getc(f);

    直接了当地表明意图
    当你编写代码的本意是希望表达某个意思,但这些代码有可能被误解为另一种意思时,请用括号或者其他方式让你的意图尽可能清楚明了
    while(' ' == c || ' ' == c || ' ' == c)
    c = getc(f);

    考察最简单的特例
    着重注意输入数据为空或者只有一个元素

    使用不对称边界
    注意数组小标从0开始

    注意潜伏在暗处的Bug
    程序的生命期往往要长于它运行其上的机器的生命期(可移植性)

    防御性编程
    多考虑坏的情况并加以处理

    附录A

    精度修饰符:指定打印数字的最小位数,不足前面补0
    printf("%.3d ", 7); 007 //整数---一共的位数
    printf("%03d ", 7); 007
    printf("%.3f ", 7.2345); 7.235 //小数---小数点后的位数

    修饰符 l(long) 只对用于整数的格式码有意义
    但是printf("%lf ", 3.14); 可以打印出 3.140000

    可变参数...


    本节完......

  • 相关阅读:
    AxMIMS系统开发环境搭建
    基于霍夫变换的点云分割方法
    微惯性导航系统
    “导航技术”学习笔记
    Kalman滤波学习
    Cell complex单元复形
    矩阵与欧拉角的转换
    CGAL4.10 / CGAL4.13编译
    [OpenCV]代码整理
    KinectFusion测试
  • 原文地址:https://www.cnblogs.com/xinglichao/p/9228682.html
Copyright © 2020-2023  润新知