【C语言篇】C 语言总复习(中):点亮编程思维,穿越代码的浩瀚星河


我的个人主页
我的专栏:C语言,希望能帮助到大家!!!点赞❤ 收藏❤

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


一、数组

(一)一维数组

  1. 数组的定义与声明
    • 数组是一组相同类型元素的集合,在 C 语言中,一维数组的定义形式为:数据类型 数组名[数组大小]; 例如:int arr[10]; 声明了一个名为 arr,包含 10 个整型元素的数组。
    • 数组的下标从 0 开始,所以对于上述数组,有效下标范围是 0 到 9。

数组的定义、初始化与元素访问

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;
}
  1. 数组的初始化
    • 可以在定义数组时进行初始化,如:int arr[5] = {1, 2, 3, 4, 5}; 按照顺序为数组元素赋值。
    • 如果初始化时提供的初值个数少于数组大小,未初始化的元素将被自动初始化为 0(对于全局数组)或不确定值(对于局部数组)。例如:int arr[10] = {1, 2};arr[0]=1arr[1]=2,其余元素为 0(全局数组)或不确定(局部数组)。
    • 也可以省略数组大小,让编译器根据初始化列表中的元素个数自动确定数组大小,如:int arr[] = {1, 2, 3}; 此时数组 arr 的大小为 3。
  2. 数组元素的访问
    • 通过下标来访问数组元素,形式为:数组名[下标]。例如:arr[3] 表示访问数组 arr 的第 4 个元素(下标为 3)。
    • 可以对数组元素进行赋值、读取等操作,如:arr[2] = 10; 将数组 arr 的第 3 个元素赋值为 10。
  3. 数组作为函数参数
    • 数组名作为函数参数时,实际上传递的是数组的首地址,函数接收的是一个指针。例如:void printArray(int arr[], int size); 这里的 arr 相当于 int *arr
    • 在函数内部对数组元素的修改会影响到原始数组,因为它们共享同一段内存空间。
  1. 数组作为函数参数传递
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]);
    }
}

(二)二维数组

  1. 二维数组的概念与声明
    • 二维数组可以看作是一个矩阵,由多个一维数组组成。其声明形式为:数据类型 数组名[行数][列数]; 例如:int matrix[3][4]; 声明了一个 3 行 4 列的二维整型数组。
  2. 二维数组的初始化
    • 可以按行初始化,如: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。
  1. 二维数组的定义、初始化与元素访问
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;
}
  1. 二维数组元素的访问
    • 通过两个下标来访问元素,形式为:数组名[行下标][列下标]。例如:matrix[1][2] 表示访问二维数组 matrix 第 2 行第 3 列的元素。
    • 可以对二维数组元素进行各种操作,如:matrix[0][1] = 10; 对指定元素赋值。
  1. 遍历二维数组
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;
}

(三)字符数组与字符串

  1. 字符数组的定义与初始化
    • 字符数组用于存储字符序列,定义形式与普通数组类似,如:char str[10];
    • 可以逐个字符初始化,如:char str[5] = {'H', 'e', 'l', 'l', 'o'};
    • 也可以用字符串常量初始化,如:char str[6] = "Hello"; 注意字符串常量会自动在末尾添加 '\0' 作为字符串结束标志,所以数组大小要足够容纳字符串和结束标志。
  2. 常用字符串处理函数
    • 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 语言代码示例,帮助你更好地理解和复习:
  1. 字符数组的定义、初始化及字符串结束标志
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;
}
  1. 使用字符串处理函数
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;
}

二、函数

(一)函数的定义与声明

  1. 函数定义的一般形式为:返回值类型 函数名(参数列表) {函数体}。例如:
c 复制代码
int add(int a, int b) {
    return a + b;
}

这里定义了一个名为 add 的函数,它接受两个整型参数 ab,并返回它们的和,返回值类型为 int

  1. 函数声明的作用是告诉编译器函数的名称、返回值类型和参数类型等信息,以便在函数调用之前进行检查。函数声明的形式为:返回值类型 函数名(参数类型列表); 例如:
c 复制代码
int add(int, int);

这是上面 add 函数的声明。函数声明通常放在头文件中,以便多个源文件共享函数的定义信息。

(二)函数的调用

  1. 函数调用时,需要提供与函数定义中参数列表匹配的实参。例如:
c 复制代码
int result = add(3, 5);

这里调用 add 函数,并传入实参 35,函数返回值被赋值给 result 变量。

  1. 函数调用的过程包括将实参的值传递给形参(值传递情况),然后执行函数体中的代码,最后返回结果(如果有返回值)。

(三)函数的参数传递

  1. 值传递:在值传递方式下,函数形参获得实参的副本,函数内部对形参的修改不会影响到实参。例如:
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 变量的值。

  1. 地址传递:通过传递变量的地址,可以在函数内部修改函数外部变量的值。例如:
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 的值。

(四)函数的嵌套调用与递归调用

  1. 嵌套调用:一个函数可以在另一个函数内部被调用。例如:
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 函数来计算乘积,然后进行进一步的处理。

  1. 递归调用:函数直接或间接地调用自身。例如,计算阶乘的递归函数:
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 的阶乘。

(五)变量的作用域与存储类别

  1. 局部变量:在函数内部定义的变量称为局部变量,其作用域仅限于该函数内部。例如:
c 复制代码
#include <stdio.h>

void function() {
    int localVariable = 5;
    printf("局部变量 localVariable 的值:%d\n", localVariable);
}

int main() {
    function();
    // 这里无法访问 localVariable 变量
    return 0;
}
  1. 全局变量:在函数外部定义的变量称为全局变量,其作用域从定义位置开始到整个源文件结束。例如:
c 复制代码
#include <stdio.h>

int globalVariable = 10;

void function() {
    printf("全局变量 globalVariable 的值:%d\n", globalVariable);
}

int main() {
    function();
    return 0;
}
  1. 自动变量(auto):函数内的局部变量默认是自动变量,其生存期在函数执行期间。例如:
c 复制代码
#include <stdio.h>

void function() {
    auto int autoVariable = 3;
    printf("自动变量 autoVariable 的值:%d\n", autoVariable);
}

int main() {
    function();
    return 0;
}
  1. 静态变量(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。

  1. 寄存器变量(register):提示编译器将变量存储在寄存器中以提高访问速度,但编译器可能会忽略该提示。例如:
c 复制代码
#include <stdio.h>

void function() {
    register int registerVariable = 5;
    printf("寄存器变量 registerVariable 的值:%d\n", registerVariable);
}

int main() {
    function();
    return 0;
}
  1. 外部变量(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 声明变量时,变量必须在其他源文件中已经定义。

三、指针

(一)指针的基本概念

  1. 指针变量的定义与初始化
    • 指针变量用于存储变量的地址。其定义形式为:数据类型 *指针变量名; 例如:int *ptr; 定义了一个指向整型数据的指针变量 ptr
    • 指针变量可以在定义时初始化,例如:int num = 10; int *ptr = &num; 这里将 ptr 初始化为变量 num 的地址。
  2. 指针的间接访问操作(* 运算符)
    • 通过 * 运算符可以访问指针所指向的变量的值。例如:
c 复制代码
#include <stdio.h>

int main() {
    int num = 20;
    int *ptr = &num;
    printf("num 的值为:%d\n", num);
    printf("通过指针访问 num 的值:%d\n", *ptr);
    return 0;
}

(二)指针与数组

  1. 指针与一维数组的关系
    • 数组名在很多情况下可以看作是指向数组首元素的指针。例如:
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;
}
  1. 指针与二维数组
    • 对于二维数组,可以将其看作是由多个一维数组组成的数组。例如,对于二维数组 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;
}

(三)指针与字符串

  1. 字符串指针的操作与应用
    • 可以使用字符指针来指向字符串常量,例如:char *str = "Hello";
    • 通过指针来遍历字符串,例如:
c 复制代码
#include <stdio.h>

int main() {
    char *str = "Hello";
    while (*str!= '\0') {
        printf("%c", *str);
        str++;
    }
    printf("\n");
    return 0;
}

(四)指针作为函数参数

  1. 传址调用在函数中的应用(修改函数外部变量的值)
    • 例如,编写一个函数来交换两个变量的值:
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;
}

(五)指针数组与指向指针的指针

  1. 指针数组的定义与使用
    • 指针数组是一个数组,其元素为指针。例如: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;
}
  1. 指向指针的指针的概念与用途
    • 指向指针的指针用于存储指针变量的地址。例如:int **pptr;
    • 可以通过指向指针的指针来间接操作指针和其所指向的数据,例如:
c 复制代码
#include <stdio.h>

int main() {
    int num = 10;
    int *ptr = &num;
    int **pptr = &ptr;
    printf("num 的值:%d\n", **pptr);
    return 0;
}

(六)动态内存分配

  1. malloccallocrealloc 函数的使用与区别
    • 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;
}
  1. free 函数释放动态分配的内存
    • 如上述代码示例中所示,使用 free 函数来释放通过 malloccallocrealloc 等函数动态分配的内存,以避免内存泄漏。例如:free(ptr); 释放指针 ptr 所指向的动态分配内存空间。
相关推荐
BanyeBirth几秒前
C++高精度算法(加、减、乘)
开发语言·c++·算法
houliabc几秒前
C语言个人笔记
c语言·数据结构·笔记·算法
Aerkui7 分钟前
Python面向对象-开闭原则(OCP)
开发语言·python·开闭原则
"_rainbow_"11 分钟前
Qt中的鼠标事件
开发语言·qt
缘来的精彩20 分钟前
kotlin 多个fragment beginTransaction容器添加使用
android·开发语言·kotlin
安小牛21 分钟前
Kotlin 学习-集合
android·开发语言·学习·kotlin
Peter_chq27 分钟前
selenium快速入门
linux·开发语言·chrome·python·selenium
双叶83632 分钟前
(51单片机)串口通讯(串口通讯教程)(串口接收发送教程)
c语言·开发语言·c++·单片机·嵌入式硬件·microsoft·51单片机
阿巴~阿巴~1 小时前
蓝桥杯 C/C++ 组历届真题合集速刷(一)
c语言·c++·算法·蓝桥杯
_x_w1 小时前
【12】数据结构之基于线性表的排序算法
开发语言·数据结构·笔记·python·算法·链表·排序算法