发布于 

记录 C 语言填坑过程

结构体

  1. 在定义一个结构体变量之时,其已经被分配了应有的空间。(实际上,C 语言任何变量在定义时都会自动分配空间)
  2. 结构体在定义时可以进行初始化,使用{},但除了在初始化时可以使用{}赋值外,其余任何时候都不用{}赋值。(实际上,数组也是这样)
  3. 结构体数组同样,当进行初始化时,会自动分配整个数组的空间,也可以通过{}嵌套赋值。(也就是说,C 语言数组定义时要么要通过数字显式给出数组长度,要么通过{}进行初始化隐式给出长度,否则会语法错误)
  4. 结构体当中如果有数组,那么在定义结构体类型时,是否需要给出数组成员的长度?需要
  5. 结构体能否直接通过 A = B 来将B结构体的值赋给A?可以

文件

  1. 文件定义方式 FILE * fp; fopen(path, mode)/fclose(FILE *) mode 包含 r/w/a/b
  2. 文件读取方法
    1. 按字符 fgetc(fp) 返回字符,结尾 EOF
    2. 按行 fgets(buffer, buffer_l, fp),读入 buffer,通过 feof(fp)来检测文件尾,每行 buffer 从头写。fgets 当文件尾和出错时都会返回 NULL。固定写法: while(!feof(fp)){if(fgets(…) != NULL){}}
    3. 按格式 fscanf(fp, “format”, aim)返回读取项数
  3. 文件写入方法
    1. 按字符 fputc(char, fp) 
    2. 按行 fputs(string, fp)
    3. 按格式 fprintf(fp, format, src)

数组

  1. 任何类型的数组,只有在定义时才能通过{}进行赋值(初始化)。
  2. 任何类型的数组,在定义时必须显式或隐式的给出数组长度,不可以不给出。
  3. 任何类型的数组在定义时便会分配好空间,后续无需再次分配。
  4. 数组名,本质上是一种指针常量,其不是普通的指针,不可以被赋值。切记不要把值赋给一个数组名,尤其是字符数组构成的字符串特别容易写错。
  5. 对于数组名 A,A[i]是值,A 是地址,可以有 A+1 来指向下一个值,但 A 本身的值无法更改,不可以 A++。

字符串

  1. C 语言中的字符串本质上就是一个字符数组。
  2. 字符串的输入输出
    1. 虽然本质上是一个数组,但是可以通过数组名与 printf 和 scanf 直接进行输入输出
      1. 无论在 printf 还是 scanf 其中 %s 接收的类型都是 char *,在 printf 中无需取值。
    2. 此外,fgets 也可进行输入,不过输入流是 stdin
  3. 赋值方式:
    1. 定义时(初始化时),可以直接赋值。char A[] = "This is a String"; char B[] = {'H', 'I', '\0'}; 
    2. 除了定义时,其余任何时候,都不能通过数组名来直接赋值。
    3. 那么如何赋值?通常使用 strcpy(dest, str);
  4. C 语言字符串的最后必须包含一个隐藏的 '\0' 作为字符串的末尾标识。在通过{}定义字符数组时如果没加,则在使用字符串相关函数时会可能溢出数组。
  5. 为什么字符串本质上作为一种字符数组可以在printf中直接通过传入数组名来输出呢?其他类型数组不行。因为 %s 要接收的类型就是 char *,而字符数组名在传入函数时会退化为指针,因此可以传入。
  6. 千万别忘 C 语言任何字符串的最后都有一个 '\0',在定义字符数组长度时千万要注意!例如名字最多有 4 个字,但是字符数组必须长度为 5,例如学号固定长度 10 位,字符数组必须长度为 11。strlen()函数不会算入 '\0'。
    1
    char s[3] = "abc" // 该语句无法执行。直接通过字符串来初始化时,会自动在其末尾添加 '\0' 
  7. 注意,C 语言中,char 类型只占 1 字节,这根本不够存储中文字符,在 UTF-8 下每个字符长达 3 字节。因此,在 C 语言中,中文字符不能用单个 char 来存储。同理,在字符数组构成的字符串中,如果该字符串要存储中文,切记不要把长度设定为要存储的中文长度,否则会超界。应该设为长度*3+1。
  8. 使用字符串相关函数如:strlen/strcat/strcpy/strcmp 时,记得加头文件 <string.h>

指针(极其易错 😭)

  1. 虽然数组名是一种特殊的指针常量,其包含一些数组信息,可以通过 sizeof(A)来获得整个数组的空间大小。但是,如果数组名传入函数当中,其会退化为普通指针,其不再包含数组信息,sizeof 只会获得指针本身的空间大小。(sizeof函数不会触发退化)
  2. 在 C 语言 中,普通变量(如 int a;)的内存地址是固定的,一旦分配,无法更改它的地址指向。
    1. 你可以改变 指针 的值,让它指向不同的变量,但你 不能改变变量本身的地址
    2. swap(&a, &b) 交换的是 a 和 b 存储的值,但 a 和 b 本身在内存中的位置不会改变。要想实现传地址到函数,来修改普通变量的值,必须通过指针指向来修改值,不能改变指针本身,如果改变指针本身,修改的也只是传入的局部的指针(地址)变量,不会影响原有位置的值。
    3. 不能通过取地址符号 & 获取普通变量地址之后修改普通变量的地址。普通变量的地址永远不变。&a = &b 是非法的。&a 是不可变的。而 *a 是可变的。
  3. 注意对指针进行运算时,一定要先保证已经分配好指针指向的空间。int * a 只会分配指针的空间,不会分配指针指向的值的空间(int)。当通过 * a 来赋值给 a 指向的空间时,如果该空间没有被分配,则会报错。但是可以给 a 赋值一个已经分配好空间的 int 变量地址,如 int i; int * a; a = &i; 但不可以 * a = i; int a 会分配 a 的空间,int * a 可不会分配,只会分配地址占用的空间。
  4. 再次强调:修改地址,是不会修改地址原先指向的值的,只会修改地址本身的指向,而普通变量的地址指向是没办法修改的,因此不可以通过修改普通变量的地址来实现修改普通变量的值。

运算

  1. int/int会截断小数部分。
  2. 如果想得到精确运算,将其中一个操作数进行类型转换。(float)int.
  3. 浮点数进行比较时,不可直接用 == 运算符,应该用 a - b < 1e-6 进行。
  4. scanf()读取整行会中断在空格处,而 fgets 可以读取整行,但 fgets 会读取末尾的换行符。gets 也可以读取整行,且不会读取换行符,但是过时了。
  5. ASCII 编码中,a-z、A-Z、0-9 都是按顺序编码的。因此单个字符可以通过比较来判断大小写和数字。将数字字符转为真数字,则可以通过 将字符减去 48 来实现。
  6. 在 printf 输出时,若要四舍五入输出浮点数保留到二位小数点,则 %.2f 会自动的四舍五入。
  7. scanf 处理空白字符(空格、回车、Tab)时会自动跳过,不影响读取下一个数字。因此连续读取多个值可以通过 %d %d %d 来实现。
  8. switch 的写法
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    switch(c){ // Switch 括号内可以写表达式,但 switch 只能处理整形结果(包括 char)
    case 'a':
    // 可正常写多个语句和语句块
    break; // 勿忘!
    case 'b':
    break;
    case 'c':
    case 'd':
    case 'e': // 多个 case 相同语句时的写法!!别写到一行里。
    default:
    ...
    } // switch 可以不包含 default,但尽量加上。
  9. C语言支持 -a 得到相反数。
  10. 在 OJ 评测中,C 语言若提前计算完毕要中途退出,最好用 return 0; 而不要用 exit()。
  11. 二维数组 A[i][j]中,A[0] 代表的是二维数组中第一个数组的==地址==。
  12. C 语言中的常量(如 1、2、3、'a'、"abc")是存储在只读区域,对其进行修改会导致错误。

本站由 Zhonjc 使用 Stellar 主题创建。 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

载入天数...载入时分秒...总访问 访客数