一、C语言基础复习

对部分知识点查漏补缺。

(一)C语言基本数据类型

  • _Bool类型 P48
  • 可移植类型:stdint.hinttypes.h P48
    • stdin.h提供可移植的类型名,定义了精确宽度整数类型和最快最小宽度类型。
    • inttype.h提供相应的输入和输出,提供字符串宏来显示可以值类型,为printf()等函数提供转换说明。
  • 十六进制表示浮点型常量 P50
    • 在十六进制数前加上十六进制前缀(0x或0X),用p和P分别代替e和E,用2的幂代替10的幂。
    • 注意,给未在函数原型中显式说明参数类型的函数传递参数时,C编译器会把float类型的值自动转换成double类型。

(二)数组和指针

  • 数组

    • 初始化数组
      计算数组大小时,sizeof后可直接接变量,不需要加括号。

      int days[] = {1, 2, 3, 4, 5};
      printf("%d %d", sizeof days, sizeof days[1]);
  • 指定初始化器

  • 指定数组的大小

    • 在定义数组时,必须明确确定数组的大小
    • 在C99新增变长数组前,数组的大小必须是整型常量,包括由整形常量组成的表达式
  • 多维数组

    • 多维数组的元素按顺序存储

    • 二维数组的初始化

      • 可以省略行数,不能省略列数。

        int arr1[4][5] = {
                {1, 1, 1, 1, 1},
                {2, 2, 2, 2, 2},
                {3, 3, 3, 3, 3},
                {4, 4, 4, 4, 4}}; // 正确而完整
        int arr2[4][5] = {1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4}; // 正确而省略
        int arr3[][5] = {1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4};  // 正确而省略
        int arr3[4][] = {1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 4, 4, 4, 4, 4};  // 错误而省略
  • 数组与指针

  • 指针与多维数组

    • 指向多维数组的指针
      声明pz为指向一个内含两个int类型值的数组:

      int (* pz)[2];// []的优先级高于*,应使pz为指针,然后定义其指向含两个int类型值的数组。
      int arr_2d[4][2] = { { 1, 2}, { 3, 4}, { 5, 6}, { 7, 8} };
      pz = arr_2d;

    应注意的是,可以用数组表示法或指针表示法来表示一个数组元素。既可以使用数组名,也可以使用指针名。 例如:

    arr_2d[m][n] == *(*(arr_2d + m) + n);
    pz[m][n] == *(*(pz + m) + n);

    换言之,C把数组解释为该数组首元素的地址,数组名与指向该数组首元素的指针等价

    • 函数与数组

      • C语言不能把整个数组作为参数传递给函数,但是可以传递数组的地址,然后函数可以使用传入的地址操控原始数组。在被调函数中可以使用数组表示法或指针表示法,无论哪种表示法实际上使用的都是指针变量。

      • 函数传递多维数组的方式是把数组名传递给类型匹配的指针形参。指针形参声明时需要指定除了第1个维度的所有维度,而第1个维度的带下通常作为第2个参数。声明函数的形参有两种方式:

        void somefunction( int (* pz)[4], int rows);
        void somefunction( int pz[][4], int rows);// 第一个空的方括号表明pz是一个指针

(三)字符串和字符串函数

  • 定义字符串

    • 字符串字面量(字符串常量)

      • 属于静态存储类别,只会被存储一次,在整个程序的生命期内存在,类似静态变量,存储在静态存储区。

      • 用双括号括起来的内容被视为指向该字符串存储位置的指针。

        printf("%s, %p, %c\n", "We", "are", *"space farers");
        // 输出:We \, 0x100000f61, s
    • 指针表示法

      const char ar1[] = "Something.";
      const char * pt1[] = "Something.";

      以上两个声明表明,pt1和ar1都是该字符串的地址。在这两种情况下,带双引号的字符串本身决定了预留给字符串的存储空间。

    • 字符串数组形式和指针形式的区别 详见教材P277

      • 程序载入内存时,字符串被载入并存储在静态存储区中。初始化数组把静态存储区的字符串拷贝到数组中,而初始化指针只把字符串的地址拷贝给指针。 初始化完成后,指针名的地址和字符串常量相同,而数组名的地址是不同的新地址。

      • 字符串数组是可修改的(除非为const),但无法通过字符串指针修改字符串常量。

      • 字符串数组的元素是变量,但数组名是常量;指向字符串的指针名是变量。例如:

        char arr[] = "Wow~";
        char *pt = "Wow~";

        两者都可以使用数组表示法,都可以进行指针加法;只有指针表示法可以递增;一般来说,只对数组表示法的字符串进行修改。
        应注意的是,虽然因编译器不同有所差别,但不应该通过指针修改字符串。建议在把指针初始化为字符串常量时使用const限定符。经实测,GCC编译器中字符串常量为const类型,存储在静态存储区,不可通过指针修改。

        pt[0] = ’C‘;// 不推荐,实测不可行(Segmentation Fault)
        const char *p1 = "Wow~";// 推荐用法
  • 字符串数组
    方式同样有数组表示法和指针表示法。

    const char *sp[3] = { "Sun", "Yah", "Sen" };// 内含3个指针的数组,占用40字节。指向初始化时所用的字符串字面量的位置,字符串字面量存储在静态内存中
    char sa[3][3] = { "Sun", "Yah", "Sen" };// 内含3个数组的数组,每个数组3个char类型的值,共占用9字节。数组存储了字符串字面量的副本。

比较而言,指针数组效率更高,分配的内存少,但指向的字符串字面量无法修改;如果要改变字符串或为字符串输入预留空间,应使用char类型二维数组。

  • 字符串输入
    • gets()函数
    • fgets()函数
      注意,读到换行符时,gets()会丢弃,而fgets会保留。相应地,puts()会在待输出字符串末尾添加一个换行符,而fputs()不会。
    • 删除换行符与丢弃缓存区多余字符
  • 字符串输出
    • puts()
    • fputs()

(四)存储类别、链接和内存管理

  • 存储类别

    • 对象是存储的值占用的一块物理内存。
    • 作用域:块作用域、函数作用域、(内部链接/外部链接的)文件作用域(全局)。
    • 链接:内部链接(static声明)和外部链接。
    • 存储期:静态、线程、自动、动态分布。
  • 分配内存:malloc()和free()

    • malloc()分配未赋名的内存,返回动态分配内存块的首字节地址。可以把该地址赋给一个指针变量,并使用指针访问这块内存。

      int n;
      double * ptd;
      ptd = (double *) malloc(n * sizeof(double));// 创建动态数组

(五)文件输入/输出

  • 标准I/O

    • 检查命令行参数

    • fopen()和fclose()

    • getc()和putc()

    • 文件读取范例

      int ch;
      FILE *fp;
      fp = fopen("wenben.txt","r");
      while (( ch = getc(fp)) != EOF)
      {
          putchar(ch);
      }
    • 指向标准文件的指针:stdin、stdout和stderr

  • 文件I/O:fprintf()、fscanf()、fgets()和fputs()

(六)结构和其他数据形式

  • 建立结构声明

  • 定义结构变量
    注意,结构标记是可选的。若声明结构只用来定义一个结构变量,结构标记可以省略;若打算多次使用结构模板,则必须使用带标记的结构声明。

    • 初始化结构
    • 访问结构成员
    struct book{
      char title[MAXTITL];
      char author[MAXAUTL];
      float value;
    };// 带标记“book”的结构声明
    struct book library;// 定义结构变量
    
    struct book{
      char title[MAXTITL];
      char author[MAXAUTL];
      float value;
    } library;// 声明结构并定义结构变量
    
    struct {
      char title[MAXTITL];
      char author[MAXAUTL];
      float value;
    } library;// 一次性声明结构并定义结构变量
  • 结构数组

    • 声明结构数组
    • 读取结构数组
  • 嵌套结构

    • 传递结构成员
    • 传递结构的地址
    • 传递结构
    struct names {
    char first[LEN];
    char last[LEN];
    };
    
    struct student {
      struct names handle;
      char food[LEN];
      char job[LEN];
      float income;
    };
    struct student fellow= {
      { "Mingyang", "Guo"},
      "Banana",
      "Student",
      114514.00
    };
    
    printf("%s's income is %f",fellow.handle.first,fellow.income);
  • 结构/结构指针的双向通信

  • 结构和结构指针的选择:传递结构指针更高效,传递结构多用于小型结构。

  • 把结构内容保存到文件中【待复习】

  • 链式结构

  • 联合

  • 枚举类型

  • typedef

    • typedef为某一类型自定义名称,由编译器解释。
    • typedef和#define的区别

(七)高级数据类型

  • 链表
    • 显示链表
    • 创建链表
    • 释放链表
Last modification:February 25, 2023
如果觉得我的文章对你有用,请随意赞赏~