Chapter 1

  • 使用预处理语句“注释掉”代码最安全

    #if 0
        // some statements
    #endif
  • printf返回输出的长度(包括不可见字符),scanf只返回1(成功)或0(失败)
  • scanf读取输入到数组时,直接写数组名和&数组名效果相同

    int array[10]; // 以下两种方式效果一致
    scanf("%s", array);
    scanf("%s", &array);
  • 赋值运算符的返回值大概率为左值(C)或左值的引用(C++),但归根到底是一个undefined行为,取决于编译器的实现。但以下用法通常可以接受:

    while ((ch = getchar()) != EOF && ch != '\n') // clear input buffer
        ; // 分号单独写一行,以免把下一行代码当成循环体

    且此处ch通常被声明为int,因为EOF实际上是一个int

  • 将作为参数接收的数组名声明为const,可以防止意外修改数组中的数据
  • gets函数的安全性问题在于,它读取字符串时无法知道其长度,当遇到超长字符串时很容易溢出

Chapter 2

  • 程序的编译与执行

    • 首先看几个容易混淆的扩展名

      • .out / .exe 可执行文件,Linux和Unix下用.out,Windows下用.exe
      • .o / .obj 编译后产生的尚未链接的中间文件,机器码的初步形式,Linux和Unix下用.o,Windows下用.obj
      • .bin 单纯二进制文件,可能是代码,也可能是数据
    • 编译

      • Compile: source code > object code(对每一个.c生成对应的.o / .obj
      • Link: object code > executable(链接所有.o / .obj文件生成一个完整的.out / .exe
    • 执行

      • 程序被操作系统加载入内存
      • 执行:使用运行时堆栈及静态内存
      • 终止

Chapter 3

  • 字面值

    • 数字

      • 在不明确指定类型时,它是能完整容纳该值的最短类型
    • 字符

      • 使用L前缀指定字符为宽字符常量(unicode只是宽字符编码的一种,两者并不相等!

        wchar_t = L'X';
    • 字符串

      • ⚠️指向字符串常量的指针可以被指向别的地方,但字符串常量是不能被改变的!

        char* strPtr = "blahblah";
        strPtr[2] = 'D'; // 非法操作!!
        strPtr = "Hmmm"; // It's Okay..
  • 没有unsigned的浮点数!(第一次知道。。一个没有想过的问题)
  • 声明指针时,把星号紧挨变量写是一个好习惯:

    int* a, b, c; // 很容易误解为a、b、c三个指针,而实际只有a是指针,b和c仅仅是int
  • 在声明字符串指针的同时赋值实际意味将该指针指向字符串的首地址,毕竟C中根本没有真正的字符串类型

    char *message = "Hello";
    // 实际上等价于
    char *message;
    message = "Hello"; // 而不是 *message = "Hello";
  • 用typedef定义指针类型

    之前一直不能理解这种习惯,觉得真的很容易把指针误认为数据,命名时加个Ptr后缀不好吗??现在才知道原来是方便定义函数指针时用的。。soga

    #define charPtr char* // 危险!
    charPtr a, b, c; // 只有a被声明为指针,b和c为char数据
    // ------------------------------------------------------
    typedef char *charPtr; // It's Okay..
    charPtr a, b, c; // a、b、c的类型都为char*
  • ⚠️const(只表示道义上不会修改,硬要改还是可以用别的指针轻易绕开限制,防君子不防小人。。)

    • 声明常量

      const int a;
      int const a;
      • 两种方式完全等价
      • 函数中声明为const的参数被调用时会得到实参的值(?这句话没有理解)
    • 声明指针

      理解: 类型 、星号、变量名,这三个部分的相对顺序总是固定的,星号左边的const修饰数据,星号右边的const修饰指针

      • 指向常量的指针变量(完全等价)

        int const * p;
        const int * p;
      • 指向变量的常量指针

        int * const p;
      • 指向常量的常量指针

        int const * const ptr;
  • ⚠️链接属性

    • 种类

      • external - 不论声明多少次,位于几个源文件,都表示同一实体
      • internal - 统一源文件内的声明都为同一实体
      • none - 每次声明都是不同实体
    • 手动改变链接属性

      • extern 指定为external(代码块内部使用extern声明变量可以确保使用的是最外部的全局变量)
      • static 将缺省属性为external的声明转变为internal属性
  • ⚠️声明 vs. 定义(重新理解了这两个名词。。)

    • 声明Declaration

      • 仅仅是一个符号,不分配空间
      • 同一变量可声明多次
      • external声明不是定义
    • 定义Definition

      • 定义也是声明,真正地分配空间
      • 一个变量只能定义一次
      • 所有带初始化的语句一定是定义。
  • 储存类型

    • 静态变量 - static

      • 静态变量的初始化将会随着程序被加载入内存同步完成,完全没有额外开销
      • 未显示指定值的静态变量初始化为0
      • 用途

        • 用于函数声明或代码块之外的变量声明:将external的变量改为internal(反正作用域和生命周期已经最大了)
        • 用于代码块内部变量声明:将自动变量改为静态变量(作用域和链接属性不变)
    • 自动变量 - auto

      • 除了静态变量和极少量寄存器变量外所有的变量都为自动变量
      • 初始化和后期赋值的开销相同(除了const,毕竟不存在后期赋值)
    • 寄存器变量 - register

小明的贤鱼
2 声望0 粉丝

while(true) -1s;