C语言的那点事第六篇:数据的“集体宿舍”数组,数据的“导航仪”指针与灵活的租房服务动态内存分配

1. 数组:数据的“集体宿舍”

数组是一种数据结构,用来存储一组相同类型的数据。想象一下,数组就像是一排排整齐的宿舍房间,每个房间都有一个编号(索引),而里面住着的数据就是“室友”。

类型 描述 示例代码 输出
一维数组 单层宿舍,存储一组相同类型的数据,索引从0开始。 int dorm[5] = {1, 2, 3, 4, 5}; dorm[0] = 1, dorm[1] = 2, ..., dorm[4] = 5
多维数组 多层宿舍,需要多个索引访问元素。二维数组是最常见形式。 int building[2][3] = { {1, 2, 3}, {4, 5, 6}}; building[0][0] = 1, building[1][2] = 6
字符数组与字符串 字符数组以\0结尾表示字符串,用于存储文本数据。 char name[] = "Hello"; name = "Hello"
1.1 一维数组:单层宿舍

一维数组是最简单的数组形式,就像是一排单层的宿舍。每个房间(元素)都有一个编号,编号从0开始。

int dorm[5] = {1, 2, 3, 4, 5};

这里,dorm 是一个一维数组,可以存储5个整数。dorm[0] 是第一个房间,住着数字1;dorm[4] 是最后一个房间,住着数字5。

示例代码:

#include 

int main() {
    int dorm[5] = {1, 2, 3, 4, 5};  // 定义一个一维数组
    printf("一维数组的内容:\n");
    for (int i = 0; i < 5; i++) {
        printf("dorm[%d] = %d\n", i, dorm[i]);
    }
    return 0;
}

输出:

一维数组的内容:
dorm[0] = 1
dorm[1] = 2
dorm[2] = 3
dorm[3] = 4
dorm[4] = 5
1.2 多维数组:多层宿舍

多维数组就像是多层的宿舍楼。二维数组是最常见的多维数组,它有两个维度:行和列。每个房间的地址需要两个编号来确定。

int building[2][3] = {
    {1, 2, 3},  // 第一层
    {4, 5, 6}   // 第二层
};

这里,building 是一个二维数组,第一维表示楼层,第二维表示房间。building[0][0] 是第一层的第一个房间,住着数字1;building[1][2] 是第二层的第三个房间,住着数字6。

示例代码:

#include 

int main() {
    int building[2][3] = {
        {1, 2, 3},  // 第一层
        {4, 5, 6}   // 第二层
    };

    printf("多维数组的内容:\n");
    for (int i = 0; i < 2; i++) {  // 遍历楼层
        for (int j = 0; j < 3; j++) {  // 遍历房间
            printf("building[%d][%d] = %d\n", i, j, building[i][j]);
        }
    }
    return 0;
}

输出:

多维数组的内容:
building[0][0] = 1
building[0][1] = 2
building[0][2] = 3
building[1][0] = 4
building[1][1] = 5
building[1][2] = 6
1.3 字符数组与字符串:宿舍里的名字牌

字符数组是用来存储字符的数组,而字符串是字符数组的一种特殊形式,以\0(空字符)结尾。字符串就像是宿舍门口的名字牌,用来标识每个房间的主人。

char name[6] = {'H', 'e', 'l', 'l', 'o', '\0'};

或者直接写成:

char name[] = "Hello";

这里的name是一个字符串,存储了“Hello”这个单词,最后一个\0是字符串的结束标志。

示例代码:

#include 

int main() {
    char name[6] = {'H', 'e', 'l', 'l', 'o', '\0'};  // 字符数组
    char greeting[] = "Hello";  // 字符串

    printf("字符数组的内容:%s\n", name);
    printf("字符串的内容:%s\n", greeting);

    return 0;
}

输出:

字符数组的内容:Hello
字符串的内容:Hello

2. 指针:数据的“导航仪”

指针是一种特殊的变量,用来存储另一个变量的地址。想象一下,指针就像是一个导航仪,它不直接存储数据,而是告诉你数据在哪里。

概念 描述 示例代码 输出
指针的概念 指针存储变量的地址,通过指针可以间接访问和修改变量。 c
int value = 10;
int *ptr = &value;
printf("%d", *ptr);
10
指针与数组 数组名是数组首元素的地址,指针可以用来遍历数组。 c
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;
printf("%d", *(ptr + 2));
3
指针运算 指针可以进行加减运算,用于访问相邻元素。 c
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;
ptr++;
printf("%d", *ptr);
2
指针与函数 指针可以作为函数参数或返回值,用于修改变量或返回地址。 c
void increment(int *p) { (*p)++; }
int value = 5;
increment(&value);
printf("%d", value);
6
指针与字符串 字符串是字符数组,可以通过指针逐字符访问。 c
char *str = "Hello";
printf("%c", *(str + 1));
e
字符串操作函数 提供字符串复制、比较等功能。 c
char src[] = "Hello";
char dest[20];
strcpy(dest, src);
printf("%s", dest);
Hello
2.1 指针的概念

指针是一个变量,用来存储另一个变量的地址。通过指针,我们可以间接访问和修改变量的值。

int value = 10;
int *ptr = &value;  // ptr 是一个指针,存储了 value 的地址

&value 是取value的地址,ptr 是一个指向int类型的指针。

示例代码:

#include 

int main() {
    int value = 10;  // 定义一个整数变量
    int *ptr = &value;  // 定义一个指针,存储 value 的地址

    printf("value 的值:%d\n", value);
    printf("value 的地址:%p\n", &value);
    printf("通过指针访问 value:%d\n", *ptr);  // 通过指针访问 value 的值
    return 0;
}

输出:

value 的值:10
value 的地址:0x7ffccf4f7a4c
通过指针访问 value:10
2.2 指针与数组的关系

数组的名字本质上就是一个指向数组首元素的指针。通过指针,我们可以访问数组的元素。

int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;  // arr 是数组名,也是首元素的地址

ptrarr 都指向数组的第一个元素。通过指针运算,可以访问数组的其他元素。

示例代码:

#include 

int main() {
    int arr[5] = {1, 2, 3, 4, 5};  // 定义一个数组
    int *ptr = arr;  // 指针指向数组的首元素

    printf("通过数组名访问元素:\n");
    for (int i = 0; i < 5; i++) {
        printf("arr[%d] = %d\n", i, arr[i]);
    }

    printf("通过指针访问元素:\n");
    for (int i = 0; i < 5; i++) {
        printf("*(ptr + %d) = %d\n", i, *(ptr + i));
    }

    return 0;
}

输出:

通过数组名访问元素:
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
arr[4] = 5
通过指针访问元素:
*(ptr + 0) = 1
*(ptr + 1) = 2
*(ptr + 2) = 3
*(ptr + 3) = 4
*(ptr + 4) = 5
2.3 指针运算

指针可以进行加减运算,但不能进行乘除运算。通过指针运算,我们可以遍历数组或访问相邻的元素。

int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;

ptr++;  // 指针指向下一个元素
printf("%d\n", *ptr);  // 输出 2

示例代码:

#include 

int main() {
    int arr[5] = {1, 2, 3, 4, 5};  // 定义一个数组
    int *ptr = arr;  // 指针指向数组的首元素

    printf("指针初始位置:%p\n", ptr);
    printf("指针指向的值:%d\n", *ptr);

    ptr++;  // 指针指向下一个元素
    printf("指针移动后的位置:%p\n", ptr);
    printf("指针移动后指向的值:%d\n", *ptr);

    return 0;
}

输出:

指针初始位置:0x7ffccf4f7a40
指针指向的值:1
指针移动后的位置:0x7ffccf4f7a44
指针移动后指向的值:2
2.4 指针与函数

指针可以作为函数的参数,也可以作为函数的返回值。

  • 指针作为参数:可以修改传入的变量。

void increment(int *p) {
    (*p)++;
}
  • 返回指针:函数可以返回一个指针。

int *getPointer() {
    int value = 10;
    return &value;  // 注意:返回局部变量的地址是危险的!
}

示例代码:

#include 

void increment(int *p) {
    (*p)++;  // 通过指针修改变量的值
}

int *getPointer() {
    int value = 10;
    return &value;  // 返回局部变量的地址(不推荐)
}

int main() {
    int value = 5;
    increment(&value);  // 通过指针修改 value
    printf("修改后的 value:%d\n", value);

    int *ptr = getPointer();  // 获取一个指针
    printf("通过指针访问的值:%d\n", *ptr);  // 注意:这个值可能已经失效!

    return 0;
}

输出:

修改后的 value:6
通过指针访问的值:10
2.5 指针与字符串

字符串本质上是一个字符数组,\0结尾。可以通过指针来操作字符串。

char *str = "Hello";
printf("%c\n", *str);  // 输出 'H'
printf("%c\n", *(str + 1));  // 输出 'e'

示例代码:

#include 

int main() {
    char *str = "Hello";  // 定义一个字符串
    printf("字符串的内容:%s\n", str);

    printf("通过指针访问字符串的每个字符:\n");
    for (int i = 0; str[i] != '\0'; i++) {
        printf("str[%d] = %c\n", i, *(str + i));
    }

    return 0;
}

输出:

字符串的内容:Hello
通过指针访问字符串的每个字符:
str[0] = H
str[1] = e
str[2] = l
str[3] = l
str[4] = o
2.6 字符串操作函数

C语言提供了很多字符串操作函数,比如strcpystrcmp等。

  • strcpy:复制字符串。

  • strcmp:比较字符串。

示例代码:

#include 
#include 

int main() {
    char src[] = "Hello";
    char dest[20];

    strcpy(dest, src);  // 复制字符串
    printf("复制后的字符串:%s\n", dest);

    int result = strcmp(src, dest);  // 比较字符串
    if (result == 0) {
        printf("两个字符串相等!\n");
    } else {
        printf("两个字符串不相等!\n");
    }

    return 0;
}

输出:

复制后的字符串:Hello
两个字符串相等!

3. 动态内存分配:灵活的租房服务

动态内存分配允许我们在程序运行时申请和释放内存。这就像一个灵活的租房服务,可以根据需要随时租用或退房。

函数 描述 示例代码 输出
malloc 分配指定大小的内存,返回指向该内存的指针。 c
int *ptr = (int *)malloc(5 * sizeof(int));
ptr[0] = 10;
printf("%d", ptr[0]);
free(ptr);
10
calloc 分配内存并初始化为0。 c
int *ptr = (int *)calloc(5, sizeof(int));
printf("%d", ptr[0]);
free(ptr);
0
realloc 调整已分配内存的大小。 c
int *ptr = (int *)malloc(5 * sizeof(int));
ptr = (int *)realloc(ptr, 10 * sizeof(int));
ptr[9] = 90;
printf("%d", ptr[9]);
free(ptr);
90
free 释放动态分配的内存,避免内存泄漏。 c
int *ptr = (int *)malloc(5 * sizeof(int));
free(ptr);
-
3.1 malloc

malloc 用于分配一块指定大小的内存,并返回一个指向这块内存的指针。

int *ptr = (int *)malloc(5 * sizeof(int));  // 分配5个整数的空间

示例代码:

#include 
#include 

int main() {
    int *ptr = (int *)malloc(5 * sizeof(int));  // 分配5个整数的空间
    if (ptr == NULL) {
        printf("内存分配失败!\n");
        return 1;
    }

    for (int i = 0; i < 5; i++) {
        ptr[i] = i * 10;  // 初始化数组
    }

    printf("动态分配的数组内容:\n");
    for (int i = 0; i < 5; i++) {
        printf("ptr[%d] = %d\n", i, ptr[i]);
    }

    free(ptr);  // 释放内存
    return 0;
}

输出:

动态分配的数组内容:
ptr[0] = 0
ptr[1] = 10
ptr[2] = 20
ptr[3] = 30
ptr[4] = 40
3.2 calloc

calloc 用于分配一块内存,并将其初始化为0

int *ptr = (int *)calloc(5, sizeof(int));  // 分配5个整数的空间,并初始化为0

示例代码:

#include 
#include 

int main() {
    int *ptr = (int *)calloc(5, sizeof(int));  // 分配5个整数的空间,并初始化为0
    if (ptr == NULL) {
        printf("内存分配失败!\n");
        return 1;
    }

    printf("动态分配并初始化为0的数组内容:\n");
    for (int i = 0; i < 5; i++) {
        printf("ptr[%d] = %d\n", i, ptr[i]);
    }

    free(ptr);  // 释放内存
    return 0;
}

输出:

动态分配并初始化为0的数组内容:
ptr[0] = 0
ptr[1] = 0
ptr[2] = 0
ptr[3] = 0
ptr[4] = 0
3.3 realloc

realloc 用于调整已分配内存的大小

ptr = (int *)realloc(ptr, 10 * sizeof(int));  // 扩展到10个整数的空间

示例代码:

#include 
#include 

int main() {
    int *ptr = (int *)malloc(5 * sizeof(int));  // 初始分配5个整数的空间
    if (ptr == NULL) {
        printf("内存分配失败!\n");
        return 1;
    }

    for (int i = 0; i < 5; i++) {
        ptr[i] = i * 10;  // 初始化数组
    }

    printf("初始动态分配的数组内容:\n");
    for (int i = 0; i < 5; i++) {
        printf("ptr[%d] = %d\n", i, ptr[i]);
    }

    ptr = (int *)realloc(ptr, 10 * sizeof(int));  // 扩展到10个整数的空间
    if (ptr == NULL) {
        printf("内存重新分配失败!\n");
        return 1;
    }

    for (int i = 5; i < 10; i++) {
        ptr[i] = i * 10;  // 继续初始化数组
    }

    printf("扩展后的动态分配的数组内容:\n");
    for (int i = 0; i < 10; i++) {
        printf("ptr[%d] = %d\n", i, ptr[i]);
    }

    free(ptr);  // 释放内存
    return 0;
}

输出:

初始动态分配的数组内容:
ptr[0] = 0
ptr[1] = 10
ptr[2] = 20
ptr[3] = 30
ptr[4] = 40
扩展后的动态分配的数组内容:
ptr[0] = 0
ptr[1] = 10
ptr[2] = 20
ptr[3] = 30
ptr[4] = 40
ptr[5] = 50
ptr[6] = 60
ptr[7] = 70
ptr[8] = 80
ptr[9] = 90
3.4 free

free 用于释放动态分配的内存,避免内存泄漏

free(ptr);  // 释放内存

完整实例程序:动态数组和字符串操作

下面是一个综合使用数组、指针和动态内存分配的程序,实现一个动态数组的创建、字符串复制和比较。

#include 
#include 
#include 

// 动态创建一个整数数组,并初始化为指定值
int *createArray(int size, int value) {
    int *arr = (int *)malloc(size * sizeof(int));
    if (arr == NULL) {
        printf("内存分配失败!\n");
        return NULL;
    }
    for (int i = 0; i < size; i++) {
        arr[i] = value;
    }
    return arr;
}

// 打印数组
void printArray(int *arr, int size) {
    if (arr == NULL) {
        printf("数组为空!\n");
        return;
    }
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}

// 字符串操作
void stringOperations() {
    char src[] = "Hello";
    char dest[20];
    strcpy(dest, src);  // 复制字符串
    printf("复制后的字符串:%s\n", dest);

    int result = strcmp(src, dest);  // 比较字符串
    if (result == 0) {
        printf("两个字符串相等!\n");
    } else {
        printf("两个字符串不相等!\n");
    }
}

int main() {
    // 动态创建数组
    int *arr = createArray(5, 10);
    printf("动态创建的数组内容:");
    printArray(arr, 5);

    // 字符串操作
    stringOperations();

    // 释放动态分配的内存
    free(arr);

    return 0;
}

输出:

动态创建的数组内容:10 10 10 10 10
复制后的字符串:Hello
两个字符串相等!

总结

数组和指针是C语言中的重要概念,它们就像是数据的“集体宿舍”和“导航仪”。动态内存分配则让我们的程序更加灵活。通过这些知识点,我们可以实现很多强大的功能,比如动态数组和字符串操作。

你可能感兴趣的:(C语言的那点事,算法,c语言,青少年编程,开发语言,蓝桥杯)