【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 所指向的动态分配内存空间。
相关推荐
isyangli_blog3 小时前
OpenDayLight (Carbon 版本) 启动与组件安装
开发语言·php
vb2008113 小时前
FastAPI APIRouter
开发语言·python
Benszen3 小时前
KVM虚拟化解决方案
开发语言·perl
会编程的土豆3 小时前
Go 语言反射(Reflection)详解
开发语言·后端·golang
東雪木3 小时前
多线程与并发编程 专属复习笔记
java·开发语言·笔记·java面试
杨充3 小时前
1.3 浮点型数据设计灵魂
开发语言·python·算法
噜噜噜阿鲁~3 小时前
python学习笔记 | 11.3、面向对象高级编程-多重继承
java·开发语言
basketball6164 小时前
Go 语言从入门到进阶:4. 数组和MAP使用方法总结
开发语言·后端·golang
春生野草4 小时前
反射、Tomcat执行
java·开发语言
雪的季节5 小时前
企业级 Qt 全功能项目
开发语言·数据库·qt