Home Categories Archives Friends GitHub

C语言复习提纲

C C

数据类型

变量名只能由字母、数字和 _ 组成,且不能以字母开头。此外,变量名不可以为关键词。下面是 C 语言中常见的一些关键词。

autobreakcasecharconstcontinuedefaultdodoubleelseenumexternfloatforgotoifinlineintlongregisterrestrictreturnshortsignedsizeofstaticstructswitchtypedefunionunsignedvoidvolatilewhile

基础数据类型

类型 大小 范围
char 1 -128 到 127 或 0 到 255(取决于是否带符号)
unsigned char 1 0 到 255
short 2 -32,768 到 32,767
unsigned short 2 0 到 65,535
int 4 -2,147,483,648 到 2,147,483,647
unsigned int 4 0 到 4,294,967,295
long 4 或 8 -2,147,483,648 到 2,147,483,647 或 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807(取决于系统)
unsigned long 4 或 8 0 到 4,294,967,295 或 0 到 18,446,744,073,709,551,615(取决于系统)
long long 8 -9,223,372,036,854,775,808 到 9,223,372,036,854,775,807
unsigned long long 8 0 到 18,446,744,073,709,551,615
float 4 大约 6~9 位精度,范围为 1.2e-38 到 3.4e38
double 8 大约 15 位精度,范围为 2.3e-308 到 1.7e308
long double 16 或更多 大约 19 位精度,范围为 3.4e-4932 到 1.1e4932(取决于系统)

ASCII 码表

序号 字符 序号 字符 序号 字符 序号 字符
0 NUL (空) 32 (空格) 64 @ 96 `
1 SOH (标题开始) 33 ! 65 A 97 a
2 STX (正文开始) 34 " 66 B 98 b
3 ETX (正文结束) 35 # 67 C 99 c
4 EOT (传送结束) 36 $ 68 D 100 d
5 ENQ (询问) 37 % 69 E 101 e
6 ACK (确认) 38 & 70 F 102 f
7 BEL (响铃) 39 ' 71 G 103 g
8 BS (退格) 40 ( 72 H 104 h
9 HT (横向制表) 41 ) 73 I 105 i
10 LF (换行) 42 * 74 J 106 j
11 VT (纵向制表) 43 + 75 K 107 k
12 FF (换页) 44 , 76 L 108 l
13 CR (回车) 45 - 77 M 109 m
14 SO (移出) 46 . 78 N 110 n
15 SI (移入) 47 / 79 O 111 o
16 DLE (退出数据链) 48 0 80 P 112 p
17 DC1 (设备控制1) 49 1 81 Q 113 q
18 DC2 (设备控制2) 50 2 82 R 114 r
19 DC3 (设备控制3) 51 3 83 S 115 s
20 DC4 (设备控制4) 52 4 84 T 116 t
21 NAK (反确认) 53 5 85 U 117 u
22 SYN (同步空闲) 54 6 86 V 118 v
23 ETB (传输块结束) 55 7 87 W 119 w
24 CAN (取消) 56 8 88 X 120 x
25 EM (媒介结束) 57 9 89 Y 121 y
26 SUB (替换) 58 : 90 Z 122 z
27 ESC (退出) 59 ; 91 [ 123 {
28 FS (文件分隔符) 60 < 92 \ 124 |
29 GS (组分隔符) 61 = 93 ] 125 }
30 RS (记录分隔符) 62 > 94 ^ 126 ~
31 US (单元分隔符) 63 ? 95 _ 127 DEL (删除)

参考:https://zh.cppreference.com/w/cpp/language/ascii

复合数据类型

结构体:一种能够存储不同类型数据的集合。

#include <stdio.h>

// 定义一个结构体
struct Person {
    char name[20];
    int age;
    float height;
};

int main() {
    // 声明一个结构体变量并初始化
    struct Person person1 = {"Alice", 25, 1.75};
    
    // 访问结构体成员并打印信息
    printf("Person's name: %s\n", person1.name);       // Person's name: Alice
    printf("Person's age: %d\n", person1.age);         // Person's age: 25
    printf("Person's height: %.2f\n", person1.height); // Person's height: 1.75
    
    return 0;
}

联合体:与结构体类似,但所有成员共享同一块内存空间。

#include <stdio.h>

// 定义一个联合
union Data {
    int i;
    float f;
    char str[20];
};

int main() {
    // 声明一个联合变量并使用
    union Data data;
    
    data.i = 10;
    printf("data.i: %d\n", data.i);   // data.i: 10
    
    data.f = 3.14; // 覆盖整型成员,为浮点型成员赋值
    printf("data.f: %.2f\n", data.f); // data.f: 3.14
    
    printf("data.i after changing data.f: %d\n", data.i);
    // data.i after changing data.f: 1078523331
    
    return 0;
}

表达式与运算符

C 语言中常见的几类运算符:

运算符优先级

运算符优先级决定了一个带有多个运算符的表达式如何进行计算。常见需要注意的是:

优先级 运算符 描述 结合性
1 ++ -- 后缀自增与自减 从左到右
() 函数调用
[] 数组下标
. 结构体与联合体成员访问
-> 结构体与联合体成员通过指针访问
2 ++ -- 前缀自增与自减 从右到左
+ - 一元加与减
! ~ 逻辑非与逐位非
(type) 转型
* 间接(解引用)
& 取址
sizeof 取大小
3 * / % 乘法、除法及余数 从左到右
4 + - 加法及减法
5 << >> 逐位左移及右移
6 < <= 分别为 < 与 ≤ 的关系运算符
> >= 分别为 > 与 ≥ 的关系运算符
7 == != 分别为 = 与 ≠ 关系
8 & 逐位与
9 ^ 逐位异或(排除或)
10 | 逐位或(包含或)
11 && 逻辑与
12 || 逻辑或
13 ?: 三元条件 从右到左
14 = 简单赋值
+= -= 以和及差赋值
*= /= %= 以积、商及余数赋值
<<= >>= 以逐位左移及右移赋值
&= ^= |= 以逐位与、异或及或赋值
15 , 逗号 从左到右

参考:https://zh.cppreference.com/w/c/language/operator_precedence

控制结构

顺序结构

按照语句的顺序执行,没有特定的控制流程。

int a = 5;
int b = 10;
int sum = a + b; // 顺序执行,计算a和b的和

选择结构

if-else 语句:根据条件选择执行不同的代码块。

int x = 15;
if (x > 10) {
  printf("x大于10\n");
} else {
  printf("x不大于10\n");
}

switch 语句:根据不同的情况选择执行不同的代码块。

int day = 3;
switch (day) {
  case 1:
    printf("星期一\n");
    break;
  case 2:
    printf("星期二\n");
    break;
  default:
    printf("其他天\n");
}

循环结构

while 循环:在条件为真时重复执行代码块。

// while循环
int i = 0;
while (i < 5) {
  printf("%d\n", i);
  i++;
}

do-while 循环:先执行一次代码块,然后在条件为真时重复执行。

int j = 0;
do {
  printf("%d\n", j);
  j++;
} while (j < 5);

for 循环:按照给定的初始化、条件和递增/递减来重复执行代码块。

for (int k = 0; k < 5; k++) {
  printf("%d\n", k);
}

跳转结构

break 语句:在循环或switch语句中,用于跳出当前循环或switch。

for (int m = 0; m < 10; m++) {
  if (m == 5) {
    break; // 当m等于5时跳出循环
  }
  printf("%d\n", m);
}

continue 语句:在循环中,用于跳过剩余的循环体并开始下一次迭代。

for (int n = 0; n < 5; n++) {
  if (n == 2) {
    continue; // 当n等于2时跳过当前迭代
  }
  printf("%d\n", n);
}

goto 语句:通过标签跳转到代码的不同位置。

int number = 0;
loop: // 标签
if (number < 5) {
  printf("%d\n", number);
  number++;
  goto loop; // 跳转到标签loop处
}

作用域与生命周期

作用域

生命周期修饰符

数组与指针

如果初始化数组但不赋值,那么数组的内容是随机的。如果使用 {} 初始化数组,那么没有显示声明的元素都会默认初始化为 0。

int fa[] = {};
int a[10];

// int a[];
// error: 不允许使用不完整的类型

int b[2] = {};

// int b[2] = {1, 2, 3};
// error: 初始值设定项太多

对于多维数组,仅允许空缺最高维。

int c[][5] = {{1, 2}, {1}};

// int c[][2] = {{1, 2, 3}};
// error: 初始值设定项太多

// int c[5][];
// error: 数组不能包含此类型的元素

数组名实际上是一个带有长度参数的指向数组首元素的静态指针。指针 + 1 实际上是 指针指向的地址 + sizeof(指针类型)

  int f[] = {10, 20};
  printf("%x %d %d\n", f, f[0], *f);
  // 52bffb08 10 10
  printf("%x %d %d\n", f + 1, f[1], *(f + 1));
  // 52bffb0c 20 20
  printf("%x %d %d\n", (void *)f + 1, f[1], *(int *)((void *)f + 1));
  // 52bffb09 20 335544320

指针传参数组时,只有最外层的数组长度可以不同(实际上是因为最外层的数组退化成了指针,丢失了长度信息)。

void fun(int p[4][2])
{
  for (int i = 0; i < 4; i++)
  {
  for (int j = 0; j < 2; j++)
    printf("%d ", p[i][j]);
  printf("\n");
  }
}

int a[3][2] = {1, 2, 3, 4, 5, 6};
int b[3][1], c[3][3];
fun(a);
// 1 2 
// 3 4
// 5 6
// -1475524368 371
// 超出的内容会直接溢出

// fun(b);
// cannot convert 'int (*)[1]' to 'int (*)[2]'

// fun(c);
// cannot convert 'int (*)[3]' to 'int (*)[2]'

基础排序算法

冒泡排序:一次比较两个元素,如果它们的顺序错误就将它们交换。

void bubbleSort(int arr[], int n) {
  for (int i = 0; i < n - 1; i++) {
    for (int j = 0; j < n - i - 1; j++) {
      if (arr[j] > arr[j + 1]) {
        int temp = arr[j];
        arr[j] = arr[j + 1];
        arr[j + 1] = temp;
      }
    }
  }
}

选择排序:每次从未排序的部分选择最小的元素,放到已排序部分的末尾。

void selectionSort(int arr[], int n) {
  int minIndex;
  for (int i = 0; i < n - 1; i++) {
    minIndex = i;
    for (int j = i + 1; j < n; j++) {
      if (arr[j] < arr[minIndex]) {
        minIndex = j;
      }
    }
    int temp = arr[i];
    arr[i] = arr[minIndex];
    arr[minIndex] = temp;
  }
}

插入排序:将未排序的元素插入到已排序部分的合适位置。

void insertionSort(int arr[], int n) {
  int key, j;
  for (int i = 1; i < n; i++) {
    key = arr[i];
    j = i - 1;
    while (j >= 0 && arr[j] > key) {
      arr[j + 1] = arr[j];
      j = j - 1;
    }
    arr[j + 1] = key;
  }
}

字符串

声明字符串的两种方式:

常用字符串库函数

其他

输入与输出

格式字符串

格式符号 含义
%d 以十进制形式输出整数
%ld 以长整型(long)十进制形式输出整数
%u 以无符号十进制形式输出整数
%lu 以无符号长整型(unsigned long)十进制形式输出整数
%x 以十六进制形式输出整数(小写字母)
%X 以十六进制形式输出整数(大写字母)
%o 以八进制形式输出整数
%c 输出字符
%s 输出字符串
%f 以十进制形式输出浮点数
%e 以指数形式输出浮点数(小写字母e)
%E 以指数形式输出浮点数(大写字母E)
%g 根据值的大小决定是用%f还是%e
%G 根据值的大小决定是用%f还是%E
%p 输出指针的地址
%% 输出百分号 %
%5d 输出宽度为5的十进制整数,右对齐
%-5d 输出宽度为5的十进制整数,左对齐
%05d 输出宽度为5的十进制整数,用0填充空白
%8.2f 输出宽度为8的浮点数,保留2位小数
%-8.2f 输出宽度为8的浮点数,保留2位小数,左对齐
%10s 输出宽度为10的字符串,右对齐
%-10s 输出宽度为10的字符串,左对齐

随机数

在 C 语言中,可以使用 rand() 函数来生成伪随机数。通常情况下,随机数的范围在 0 到 32767 之间。

#include <stdio.h>
#include <stdlib.h>
#include <time.h>

int main() {
  int i, n;
  // 设置随机数种子
  srand(time(0));

  n = rand();      // 生成随机数
  n = rand() % 10; // 生成0到10之间的随机数
  
  return 0;
}

链表

链表是一种常见的线性数据结构,它由一系列节点组成,每个节点包含两部分:数据部分和指向下一个节点的指针。链表中的节点按照线性顺序排列,但在内存中可以不是连续存储的。

#include <stdio.h>
#include <stdlib.h>

struct Node {
  int data;
  struct Node* next;
};

void append(struct Node* head , int data) {
  struct Node* p = head;
  struct Node* new_node = (struct Node*)malloc(sizeof(struct Node));
  new_node->data = data;
  new_node->next = NULL;

  while (p->next != NULL) {
    p = p->next;
  }
  p->next = new_node;
}