[读] C和指针 (Ch1 ~ Ch3)

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 单纯二进制文件,多是代码,也多是数据
    • 编译code

      • 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
相关文章
相关标签/搜索