C 语言疑难知识点总结

作者 Marlous 日期 2019-03-05
C 语言疑难知识点总结

一 C 语言知识结构

C 语言知识结构

二 C 语言编码规范

三 具体知识点

1 数据类型

  • 定义整型时默认是有符号,实型一定是有符号。

  • 数据类型

2 运算符与表达式

  • 运算符与表达式
  • 运算符与表达式

  • 自增、自减:
    ++n 表示先执行加一操作,后使用值;n++ 表示先使用值,后执行加一操作。

  • 强制类型转换:
    (float)m/2 表示把 m 先强制转换为 float 型。/ (float)(m/2) 把 m/2 的结果强制转换为 float 型。

3 键盘输入、屏幕输出

  • 单个字符输入输出:
    a) ch = getchar(),无参数,赋给变量 ch。/ getchar() 以回车为结束符,会将结束符留在缓冲中。
    b)putchar(ch),参数为要输出的字符变量;参数可以为变量、ASCII 数值、字符本身(记得加单引号)。

  • 字符串输入输出:
    a)gets(str),输入的字符赋给数组 str。/ gets(str) 以回车为结束符,不留在缓冲中。
    b)puts(str),参数为数组 str、字符串本身(记得加双引号)。

  • 格式化输入输出:
    a)scanf("%2d%*c%2d",&a,&b),表示连读两个数字(数字表示截取的宽度)当成一个变量值,忽略掉一个任意字符,最后再截取两个宽度的数字。/ 一般在 scanf() 函数后加一个 getchar() 函数,或下一行的 scanf() 函数中加个空格。/ scanf() 以回车、空格、Tab 为结束符,会将结束符留在缓冲中。
    b)printf("%-5.2f",num),表示变量 num 的浮点数在列宽为 5 的长度内左靠齐,小数点后保留 2 位(小数点也算一列),如果为字符串则表示左开始截取的字符数。

  • 补充:在用 %c 格式读入字符时,空格、转义、回车都会当作有效字符读入。

4 控制结构

  1. 顺序:
    略。

  2. 选择:

  • 双分支、多分支:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    if()
    {
    ...;
    }
    else if()
    {
    ...;
    }
    else
    {
    ...;
    }
  • 条件运算符:
    max = a > b ? a : b,表示前面的为非 0,取 a 值,否则取 b 值。

  • 多路选择语句:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    switch(x)
    {
    case 常量:
    ...;
    break;
    case 常量:
    ...;
    break;
    default:
    ...;
    }
  1. 循环:
  • while 循环:

    1
    2
    3
    4
    while()
    {
    ...; // 条件为真循环
    }
  • do while 循环:

    1
    2
    3
    4
    do
    {
    ...; // 条件为真循环
    }while();
  • for 循环:

    1
    2
    3
    4
    for(初始化表达式; 循环控制表达式; 增值表达式)
    {
    ...;
    }

5 函数

  • 函数的定义:

    1
    2
    3
    4
    返回值类型 函数名(类型 形参1, 类型 形参2, ...)
    {
    ...;
    }
  • 传递参数:
    按值调用:实参为变量名,形参为变量名。/ 是传一个副本给函数。
    按地址调用:实参为地址,形参为指针变量。/ 函数通过指针解引用访问原本变量。

  • 变量的作用域、存储类型:
    变量的作用域、存储类型
    变量的作用域、存储类型

6 数组

  • 定义一、二维数组:

    1
    2
    3
    4
    类型 数组名 [第一维长度]

    类型 数组名 [第一维长度][第二维长度]
    // 二维数组定义时必须知道列元素
  • 向函数传递数组:
    直接传递数组名。

  • 函数形参:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    int function(a[], n)
    {
    ... // 一维数组可以不指定长度,用参数 n 来表示
    }

    int function(a[][8])
    {
    ...
    }

7 指针

  • 指针变量就是存储地址的变量,声明时为 int *p = &a;,引用变量 a 的值需要先指针解引用 b = *p(此时用星号来引用该存储的地址的变量的值)。/ 指针类型的大小是固定的。

  • %p 为输出地址值的格式控制;& 为取地址符。输出变量地址值:

    1
    2
    3
    4
    int a = 2;
    int *pzz = &a;
    printf("address is %p",pzz);
    printf("address is %p",&a);
  • 函数指针:
    指向函数的指针,存的是函数在内存中的入口地址,函数指针如 int (*compare)(int a, int b)。实参是函数名,形参是函数指针。

  • 指针函数:
    函数返回的是一个指针,如 int *compare(int a, int b)

  • 指针小结:
    a)指针数组存放的是指针数组(若干个指针变量)变量自己的首地址(是数组、指针两个概念的集合)。/ 指针数组名 + n 来表示组中若干个指针变量;注意,数组名相当于首地址指针常量,不能加法移动改变位置。
    b)数组和指针类似,不同之处:数组名存放的是数组变量自己的首地址;指针存放的是其它变量的地址。
    c)基本类型变量存放的是一个数据值。
    d)字符串数组既类似基本变量(可以直接通过数组地址引用值,常用方法)、也是数组。

8 字符串

  • C 语言没有字符串类型,所以用数组间接实现。

  • 字符串常量用 "" 引号包含,编译器在结尾自动加一个 \0 表示是字符串。

  • 初始化:

    1
    2
    3
    4
    5
    char str[] = "hello";

    char str[] = {'h','e','l','l','o','\0'};

    char weekday[][10] = {"sunday","monday",...}; // 没填满的地方全自动加上 \0
  • 字符指针:
    每个字符串常量在内存中占用一段连续的空间,有唯一确定的首地址。字符串常量本身代表存放它位置的首地址,此时不能对其内容修改。要修改则需要用数组赋值字符串。

    1
    2
    3
    4
    char *ptr = "hello";
    等价于
    char *ptr;
    ptr = "hello";
  • 访问时注意:不能用数组名自加 str ++,因为数组名是一个地址常量;可以用指针自加,如 ptr ++ 。引用第某个字符:*(ptr + i)*(str + i)str[i]

  • 字符串的输入输出:
    scanf() 函数按 %c 一个一个字符输入输出,用循环遍历存字符串的数组单元。/ 循环输出,条件为不是结束符 \0
    scanf() 函数按 %s 存入数组名 str。
    使用字符串处理函数输入输出,如 gets(str)puts(str),str 为数组名。

  • 一些字符串处理函数:

    1
    2
    3
    4
    5
    6
    7
    8
    strlen(str); // 实际长度,不含 \0
    strcpy(str1,str2); // 将 str2 复制到 str1
    strcmp(str1,str2); // 从左到右逐一比较其 ASCII 值,直到遇见不同字符或结束。str1 大于 str2 返回值大于 0;等于、小于同理。
    strcat(str1,str2); // 将 str2 放入 str1 末尾(覆盖掉 str1 的结束符)

    strncpy(str1,str2,n); // 最多 n 个字符
    strncmp(str1,str2,n);
    strncat(str1,str2,n);
  • 向函数传递字符串:
    可用字符数组做函数参数(实参是数组名),也可用字符指针做函数参数(实参是数组名)。

  • 关于 const 类型限定符:
    关于 const 类型限定符

9 指针与数组

  • 数组作为函数形参和指针一样,因为实参数组名本质上传递的都是地址(地址调用,非值调用)。

  • 二维数组:二维数组是由若干个一维数组构成的。

  • 二维数组的指针定义、初始化:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    行指针:使用二维数组的行地址初始化。
    定义:int (*p)[4]; // 表示指向一维数组的指针,有 4 个元素;指向二维数组的指针,每行有 4 个元素。
    初始化:p = a; 或 p = &a[0];
    元素引用:a[i][j] 等价 *(a[i]+j) 等价 *(*(a+i)+j) 等价 (*(a+i))[j]

    列指针:使用二维数组的列地址初始化。
    定义:int *p;
    初始化:p = a[0] 等价 p = *a 等价 p = &a[0][0]
    元素引用:a[i][j] 等价 *(p+i*n+j) 等价 p[i*n+j]
  • 指针数组:
    指针数组的每个元素都是指针(涉及字符串操作用指针数组比用二维数组更有效)。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    e.g.
    char *pstr[N] = {"hello","world","ni","hao"};

    e.g.
    int main(void) {
    char *people[3];
    int i;
    for(i=0;i<3;i++)
    {
    people[i] = (char *)malloc(10 * sizeof(char)); // 每个元素是一个指针(存储名字的数组首地址)
    printf("enter name:\n");
    gets(people[i]); // 输入第 i 个字符串(名字)到 people[i] 指向的内存(存名字的数组)
    printf("\n");
    }
    printf("the name are:%s,%s,%s.",people[0],people[1],people[2]);
    return 0;
    }
  • 用指针数组接收命令行参数:

    1
    2
    3
    4
    5
    6
    int main(int argc,char *argv[]) // argc argv 是惯例取名
    {
    ...; // int argc 用于存放命令行中参数个数(至少为 1,函数名也是参数)
    // char *argv[] 用于接收命令行参数
    // 程序不需要命令行参数就写 void
    }
  • 动态数组:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    函数 malloc(size)
    void *malloc(unsigned int size);
    e.g.
    int *pi = NULL;
    pi = (int *)malloc(5); // 申请 5 个字节空间,强制转换成 int 型指针

    函数 calloc(n,size)
    void *malloc(unsigned int num,unsiged int size); // 若干个 n 字节空间,并初始化为 0,相当于一维数组

    函数 realloc(p,size)
    void *realloc(void *p,unsiged int size); // 指针 p 指向的空间重新分配空间,返回新分配的空间地址。与原来的地址不一定相同。
  • 补充一些函数:
    free() 函数:void free(void *p);,与 mallco() 函数成对使用。
    exit() 函数:exit(1) 异常退出,exit(0) 正常退出。

10 结构体、共用体

  • 结构体定义:

    1
    2
    3
    4
    5
    typedef struct student
    {
    ...;
    ...;
    } STUDENT; // 直接定义为 STUDENT 类型
  • 结构体变量初始化:

    1
    2
    3
    4
    5
    6
    7
    STUDENT stu1 = {10002,"ZhangSan",'M'};

    或者用圆点运算符引用结构体变量成员赋值
    (注意:
    字符串数组需要用 strcpy() 函数赋值,不能直接赋值;
    如果是数组的话,scanf() 输入时不用加 & 取地址符号,如 scanf("%s",&stu1.studentid);
    )。
  • 关于结构体指针:
    结构体变量的地址作为实参,传递到函数(形参为指针变量)。此时在函数内引用结构体变量的成员,直接写成 w->name 相当于 (*w).name(指针的解引用)。/ 圆点 . 用来引用结构体变量成员,箭头 -> 用来在结构体指针引用成员。

  • 共用体:
    使用覆盖技术,需要使几种不同类型的变量存放到同一段内存单元中,成为共同体类型的结构。
    共用体大小取决于占用空间最大的成员。每一瞬时起作用的就是最后被赋值的成员,只能对一个成员初始化。
    不能引用共用体变量,而只能引用共用体变量中的成员。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    union sample
    {
    short i;
    char ch;
    float f;
    };

    引用:
    union sample a;
    printf("%d", a.i);
  • 枚举类型:
    有限个数据组成的量,用枚举类型来表示。花括号内是枚举变量可能的取值。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    enum response {no = -1,yes = 1,none = 0}; // 可以定义时赋值,如果第一个赋值 1,后面的会递增

    enum response answer;

    e.g.
    if(answer == yes)
    {
    ...;
    }

11 文件操作