我的个人主页
我的专栏:C语言,希望能帮助到大家!!!点赞❤ 收藏❤
在计算机科学的广袤宇宙中,C语言犹如一颗璀璨的恒星,散发着持久而耀眼的光芒。它作为一种基础且强大的编程语言,承载着无数程序员的梦想与创造力,是开启编程世界大门的关键钥匙。当我们踏上 C 语言总复习的征程时,就如同踏上了穿越代码浩瀚星河的奇妙之旅,每一个知识点都是一颗独特的星辰,等待我们去探索、去领悟,从而点亮我们的编程思维,指引我们在编程的宇宙中自由翱翔。
一、数组

(一)一维数组
- 数组的定义与声明
- 数组是一组相同类型元素的集合,在 C 语言中,一维数组的定义形式为:数据类型 数组名[数组大小]; 例如:
int arr[10];
声明了一个名为arr
,包含 10 个整型元素的数组。 - 数组的下标从 0 开始,所以对于上述数组,有效下标范围是 0 到 9。
- 数组是一组相同类型元素的集合,在 C 语言中,一维数组的定义形式为:数据类型 数组名[数组大小]; 例如:
数组的定义、初始化与元素访问
c
#include <stdio.h>
int main() {
// 定义并初始化一个一维数组
int arr[5] = {10, 20, 30, 40, 50};
// 访问数组元素并输出
printf("数组的第一个元素:%d\n", arr[0]);
printf("数组的第三个元素:%d\n", arr[2]);
// 修改数组元素的值
arr[3] = 45;
printf("修改后数组的第四个元素:%d\n", arr[3]);
return 0;
}
- 数组的初始化
- 可以在定义数组时进行初始化,如:
int arr[5] = {1, 2, 3, 4, 5};
按照顺序为数组元素赋值。 - 如果初始化时提供的初值个数少于数组大小,未初始化的元素将被自动初始化为 0(对于全局数组)或不确定值(对于局部数组)。例如:
int arr[10] = {1, 2};
则arr[0]=1
,arr[1]=2
,其余元素为 0(全局数组)或不确定(局部数组)。 - 也可以省略数组大小,让编译器根据初始化列表中的元素个数自动确定数组大小,如:
int arr[] = {1, 2, 3};
此时数组arr
的大小为 3。
- 可以在定义数组时进行初始化,如:
- 数组元素的访问
- 通过下标来访问数组元素,形式为:数组名[下标]。例如:
arr[3]
表示访问数组arr
的第 4 个元素(下标为 3)。 - 可以对数组元素进行赋值、读取等操作,如:
arr[2] = 10;
将数组arr
的第 3 个元素赋值为 10。
- 通过下标来访问数组元素,形式为:数组名[下标]。例如:
- 数组作为函数参数
- 数组名作为函数参数时,实际上传递的是数组的首地址,函数接收的是一个指针。例如:
void printArray(int arr[], int size);
这里的arr
相当于int *arr
。 - 在函数内部对数组元素的修改会影响到原始数组,因为它们共享同一段内存空间。
- 数组名作为函数参数时,实际上传递的是数组的首地址,函数接收的是一个指针。例如:
- 数组作为函数参数传递
c
#include <stdio.h>
// 函数声明,用于打印数组元素
void printArray(int arr[], int size);
int main() {
int myArray[6] = {5, 10, 15, 20, 25, 30};
// 调用函数传递数组和数组大小
printArray(myArray, 6);
return 0;
}
// 定义函数,实现打印数组元素的功能
void printArray(int arr[], int size) {
for (int i = 0; i < size; i++) {
printf("数组元素 arr[%d] = %d\n", i, arr[i]);
}
}
(二)二维数组
- 二维数组的概念与声明
- 二维数组可以看作是一个矩阵,由多个一维数组组成。其声明形式为:数据类型 数组名[行数][列数]; 例如:
int matrix[3][4];
声明了一个 3 行 4 列的二维整型数组。
- 二维数组可以看作是一个矩阵,由多个一维数组组成。其声明形式为:数据类型 数组名[行数][列数]; 例如:
- 二维数组的初始化
- 可以按行初始化,如:
int matrix[2][3] = {``{1, 2, 3}, {4, 5, 6}};
为 2 行 3 列的二维数组赋值。 - 也可以不按行初始化,如:
int matrix[2][3] = {1, 2, 3, 4, 5, 6};
编译器会按照顺序依次为数组元素赋值。 - 同样可以省略第一维的大小,由编译器根据初始化列表确定,如:
int matrix[][3] = {1, 2, 3, 4, 5, 6};
这里编译器会确定第一维大小为 2。
- 可以按行初始化,如:
- 二维数组的定义、初始化与元素访问
c
#include <stdio.h>
int main() {
// 定义并初始化一个二维数组(按行初始化)
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
// 访问二维数组元素并输出
printf("二维数组 matrix[0][1] 的元素值:%d\n", matrix[0][1]);
printf("二维数组 matrix[1][2] 的元素值:%d\n", matrix[1][2]);
// 修改二维数组元素的值
matrix[1][1] = 8;
printf("修改后二维数组 matrix[1][1] 的元素值:%d\n", matrix[1][1]);
return 0;
}
- 二维数组元素的访问
- 通过两个下标来访问元素,形式为:数组名[行下标][列下标]。例如:
matrix[1][2]
表示访问二维数组matrix
第 2 行第 3 列的元素。 - 可以对二维数组元素进行各种操作,如:
matrix[0][1] = 10;
对指定元素赋值。
- 通过两个下标来访问元素,形式为:数组名[行下标][列下标]。例如:
- 遍历二维数组
c
#include <stdio.h>
int main() {
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 使用嵌套循环遍历二维数组并输出元素
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 4; j++) {
printf("%d ", matrix[i][j]);
}
printf("\n");
}
return 0;
}
(三)字符数组与字符串
- 字符数组的定义与初始化
- 字符数组用于存储字符序列,定义形式与普通数组类似,如:
char str[10];
- 可以逐个字符初始化,如:
char str[5] = {'H', 'e', 'l', 'l', 'o'};
- 也可以用字符串常量初始化,如:
char str[6] = "Hello";
注意字符串常量会自动在末尾添加 '\0' 作为字符串结束标志,所以数组大小要足够容纳字符串和结束标志。
- 字符数组用于存储字符序列,定义形式与普通数组类似,如:
- 常用字符串处理函数
strlen
函数:用于计算字符串的长度(不包括 '\0'),例如:int len = strlen("Hello");
返回值为 5。strcpy
函数:用于将一个字符串复制到另一个字符数组中,如:char dest[10]; strcpy(dest, "World");
将 "World" 复制到dest
数组中。strcat
函数:用于将一个字符串连接到另一个字符串的末尾,如:char str1[20] = "Hello"; char str2[] = " World"; strcat(str1, str2);
结果str1
变为 "Hello World"。strcmp
函数:用于比较两个字符串的大小,根据字典序比较,如果相等返回 0,如果str1
小于str2
返回负数,如果str1
大于str2
返回正数,例如:int result = strcmp("apple", "banana");
返回值小于 0。- 以下是一些与上述数组部分知识点相关的 C 语言代码示例,帮助你更好地理解和复习:
- 字符数组的定义、初始化及字符串结束标志
c
#include <stdio.h>
int main() {
// 逐个字符初始化字符数组
char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
// 用字符串常量初始化字符数组
char str2[] = "World";
// 输出字符数组内容(以字符串形式输出,依赖 '\0' 作为结束标志)
printf("str1: %s\n", str1);
printf("str2: %s\n", str2);
return 0;
}
- 使用字符串处理函数
c
#include <stdio.h>
#include <string.h>
int main() {
char str1[20] = "Hello";
char str2[10] = "World";
// 使用 strlen 函数计算字符串长度
int len1 = strlen(str1);
int len2 = strlen(str2);
printf("str1 的长度:%d\n", len1);
printf("str2 的长度:%d\n", len2);
// 使用 strcpy 函数复制字符串
char dest[20];
strcpy(dest, str1);
printf("复制后的字符串:%s\n", dest);
// 使用 strcat 函数连接字符串
strcat(dest, str2);
printf("连接后的字符串:%s\n", dest);
// 使用 strcmp 函数比较字符串
int result = strcmp(str1, str2);
if (result < 0) {
printf("str1 小于 str2\n");
} else if (result > 0) {
printf("str1 大于 str2\n");
} else {
printf("str1 等于 str2\n");
}
return 0;
}
二、函数

(一)函数的定义与声明
- 函数定义的一般形式为:返回值类型 函数名(参数列表) {函数体}。例如:
c
int add(int a, int b) {
return a + b;
}
这里定义了一个名为 add
的函数,它接受两个整型参数 a
和 b
,并返回它们的和,返回值类型为 int
。
- 函数声明的作用是告诉编译器函数的名称、返回值类型和参数类型等信息,以便在函数调用之前进行检查。函数声明的形式为:返回值类型 函数名(参数类型列表); 例如:
c
int add(int, int);
这是上面 add
函数的声明。函数声明通常放在头文件中,以便多个源文件共享函数的定义信息。
(二)函数的调用
- 函数调用时,需要提供与函数定义中参数列表匹配的实参。例如:
c
int result = add(3, 5);
这里调用 add
函数,并传入实参 3
和 5
,函数返回值被赋值给 result
变量。
- 函数调用的过程包括将实参的值传递给形参(值传递情况),然后执行函数体中的代码,最后返回结果(如果有返回值)。
(三)函数的参数传递
- 值传递:在值传递方式下,函数形参获得实参的副本,函数内部对形参的修改不会影响到实参。例如:
c
#include <stdio.h>
void modify(int x) {
x = 10;
}
int main() {
int num = 5;
modify(num);
printf("num 的值仍然是:%d\n", num);
return 0;
}
在这个例子中,modify
函数中对形参 x
的修改不会改变 main
函数中的 num
变量的值。
- 地址传递:通过传递变量的地址,可以在函数内部修改函数外部变量的值。例如:
c
#include <stdio.h>
void modifyByAddress(int *x) {
*x = 10;
}
int main() {
int num = 5;
modifyByAddress(&num);
printf("num 的值被修改为:%d\n", num);
return 0;
}
这里将 num
的地址传递给 modifyByAddress
函数,函数通过解引用指针修改了 num
的值。
(四)函数的嵌套调用与递归调用
- 嵌套调用:一个函数可以在另一个函数内部被调用。例如:
c
#include <stdio.h>
int multiply(int a, int b) {
return a * b;
}
int calculate(int x, int y) {
int product = multiply(x, y);
return product + 10;
}
int main() {
int result = calculate(3, 4);
printf("最终结果:%d\n", result);
return 0;
}
在 calculate
函数中调用了 multiply
函数来计算乘积,然后进行进一步的处理。
- 递归调用:函数直接或间接地调用自身。例如,计算阶乘的递归函数:
c
#include <stdio.h>
int factorial(int n) {
if (n == 0 || n == 1) {
return 1;
} else {
return n * factorial(n - 1);
}
}
int main() {
int num = 5;
int fact = factorial(num);
printf("%d 的阶乘是:%d\n", num, fact);
return 0;
}
在 factorial
函数中,通过递归调用 factorial(n - 1)
来计算 n
的阶乘。
(五)变量的作用域与存储类别
- 局部变量:在函数内部定义的变量称为局部变量,其作用域仅限于该函数内部。例如:
c
#include <stdio.h>
void function() {
int localVariable = 5;
printf("局部变量 localVariable 的值:%d\n", localVariable);
}
int main() {
function();
// 这里无法访问 localVariable 变量
return 0;
}
- 全局变量:在函数外部定义的变量称为全局变量,其作用域从定义位置开始到整个源文件结束。例如:
c
#include <stdio.h>
int globalVariable = 10;
void function() {
printf("全局变量 globalVariable 的值:%d\n", globalVariable);
}
int main() {
function();
return 0;
}
- 自动变量(auto):函数内的局部变量默认是自动变量,其生存期在函数执行期间。例如:
c
#include <stdio.h>
void function() {
auto int autoVariable = 3;
printf("自动变量 autoVariable 的值:%d\n", autoVariable);
}
int main() {
function();
return 0;
}
- 静态变量(static):静态局部变量在函数调用结束后仍然保留其值。例如:
c
#include <stdio.h>
void function() {
static int staticVariable = 0;
staticVariable++;
printf("静态变量 staticVariable 的值:%d\n", staticVariable);
}
int main() {
function();
function();
return 0;
}
在第一次调用 function
函数后,staticVariable
的值变为 1,第二次调用时,其值在前一次的基础上继续递增,而不是重新初始化为 0。
- 寄存器变量(register):提示编译器将变量存储在寄存器中以提高访问速度,但编译器可能会忽略该提示。例如:
c
#include <stdio.h>
void function() {
register int registerVariable = 5;
printf("寄存器变量 registerVariable 的值:%d\n", registerVariable);
}
int main() {
function();
return 0;
}
- 外部变量(extern):用于在一个源文件中引用另一个源文件中定义的全局变量。例如,在
file1.c
中定义全局变量:
c
int globalVariable = 20;
在 file2.c
中引用该变量:
c
#include <stdio.h>
extern int globalVariable;
int main() {
printf("外部变量 globalVariable 的值:%d\n", globalVariable);
return 0;
}
需要注意的是,在使用
extern
声明变量时,变量必须在其他源文件中已经定义。
三、指针

(一)指针的基本概念
- 指针变量的定义与初始化
- 指针变量用于存储变量的地址。其定义形式为:数据类型 *指针变量名; 例如:
int *ptr;
定义了一个指向整型数据的指针变量ptr
。 - 指针变量可以在定义时初始化,例如:
int num = 10; int *ptr = #
这里将ptr
初始化为变量num
的地址。
- 指针变量用于存储变量的地址。其定义形式为:数据类型 *指针变量名; 例如:
- 指针的间接访问操作(* 运算符)
- 通过
*
运算符可以访问指针所指向的变量的值。例如:
- 通过
c
#include <stdio.h>
int main() {
int num = 20;
int *ptr = #
printf("num 的值为:%d\n", num);
printf("通过指针访问 num 的值:%d\n", *ptr);
return 0;
}
(二)指针与数组
- 指针与一维数组的关系
- 数组名在很多情况下可以看作是指向数组首元素的指针。例如:
c
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr; // 等价于 int *ptr = &arr[0];
printf("数组的第一个元素:%d\n", *ptr);
ptr++; // 指针指向下一个元素
printf("数组的第二个元素:%d\n", *ptr);
return 0;
}
- 可以使用指针来遍历数组,例如:
c
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int *ptr;
for (ptr = arr; ptr < arr + 5; ptr++) {
printf("%d ", *ptr);
}
printf("\n");
return 0;
}
- 指针与二维数组
- 对于二维数组,可以将其看作是由多个一维数组组成的数组。例如,对于二维数组
int matrix[3][4];
,matrix
可以看作是一个指向包含 4 个整型元素的一维数组的指针。 - 可以使用指针来访问二维数组元素,例如:
- 对于二维数组,可以将其看作是由多个一维数组组成的数组。例如,对于二维数组
c
#include <stdio.h>
int main() {
int matrix[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
int (*ptr)[3] = matrix; // 定义一个指向包含 3 个整型元素的一维数组的指针,并指向 matrix 的首行
printf("二维数组 matrix[0][1] 的元素值:%d\n", (*ptr)[1]);
ptr++; // 指针指向下一行
printf("二维数组 matrix[1][2] 的元素值:%d\n", (*ptr)[2]);
return 0;
}
(三)指针与字符串
- 字符串指针的操作与应用
- 可以使用字符指针来指向字符串常量,例如:
char *str = "Hello";
- 通过指针来遍历字符串,例如:
- 可以使用字符指针来指向字符串常量,例如:
c
#include <stdio.h>
int main() {
char *str = "Hello";
while (*str!= '\0') {
printf("%c", *str);
str++;
}
printf("\n");
return 0;
}
(四)指针作为函数参数
- 传址调用在函数中的应用(修改函数外部变量的值)
- 例如,编写一个函数来交换两个变量的值:
c
#include <stdio.h>
void swap(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main() {
int num1 = 5, num2 = 10;
printf("交换前:num1 = %d, num2 = %d\n", num1, num2);
swap(&num1, &num2);
printf("交换后:num1 = %d, num2 = %d\n", num1, num2);
return 0;
}
(五)指针数组与指向指针的指针
- 指针数组的定义与使用
- 指针数组是一个数组,其元素为指针。例如:
int *arr[3];
定义了一个包含 3 个指向整型数据的指针的数组。 - 可以为指针数组的元素赋值并使用,例如:
- 指针数组是一个数组,其元素为指针。例如:
c
#include <stdio.h>
int main() {
int num1 = 1, num2 = 2, num3 = 3;
int *arr[3] = {&num1, &num2, &num3};
for (int i = 0; i < 3; i++) {
printf("%d ", *arr[i]);
}
printf("\n");
return 0;
}
- 指向指针的指针的概念与用途
- 指向指针的指针用于存储指针变量的地址。例如:
int **pptr;
- 可以通过指向指针的指针来间接操作指针和其所指向的数据,例如:
- 指向指针的指针用于存储指针变量的地址。例如:
c
#include <stdio.h>
int main() {
int num = 10;
int *ptr = #
int **pptr = &ptr;
printf("num 的值:%d\n", **pptr);
return 0;
}
(六)动态内存分配
malloc
、calloc
、realloc
函数的使用与区别malloc
函数用于动态分配指定字节数的内存空间,其函数原型为:void *malloc(size_t size);
例如:
c
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int *)malloc(sizeof(int));
if (ptr!= NULL) {
*ptr = 5;
printf("动态分配内存并赋值:%d\n", *ptr);
free(ptr); // 释放内存
}
return 0;
}
calloc
函数用于动态分配指定数量和类型的内存空间,并将其初始化为 0,其函数原型为:void *calloc(size_t num, size_t size);
例如:
c
#include <stdio.h>
#include <stdlib.h>
int main() {
int *arr = (int *)calloc(5, sizeof(int));
if (arr!= NULL) {
for (int i = 0; i < 5; i++) {
printf("%d ", arr[i]);
}
printf("\n");
free(arr);
}
return 0;
}
realloc
函数用于重新分配内存空间,其函数原型为:void *realloc(void *ptr, size_t size);
例如:
c
#include <stdio.h>
#include <stdlib.h>
int main() {
int *ptr = (int *)malloc(sizeof(int));
if (ptr!= NULL) {
*ptr = 10;
// 重新分配更大的内存空间
ptr = (int *)realloc(ptr, 2 * sizeof(int));
if (ptr!= NULL) {
*(ptr + 1) = 20;
printf("重新分配内存后:%d %d\n", *ptr, *(ptr + 1));
free(ptr);
}
}
return 0;
}
free
函数释放动态分配的内存- 如上述代码示例中所示,使用
free
函数来释放通过malloc
、calloc
、realloc
等函数动态分配的内存,以避免内存泄漏。例如:free(ptr);
释放指针ptr
所指向的动态分配内存空间。
- 如上述代码示例中所示,使用
