数组与字符串

Tutorial: 教程一 Category: C语言 Published: 2026-04-07 13:58:26 Views: 20 Likes: 0 Comments: 0
7. 数组与字符串

数组和字符串是 C 语言中用于存储和处理数据的重要数据结构。掌握数组和字符串的定义、使用以及相关操作函数,是编写高效和功能丰富的 C 程序的基础。

7.1 一维数组的定义与使用

一维数组是一组相同数据类型元素的集合,每个元素可以通过索引访问。数组在内存中是连续存储的,便于高效地访问和操作数据。

数组的定义

语法

数据类型 数组名[数组大小];
  • 数据类型:数组中元素的类型,如intfloat等。
  • 数组名:数组的名称,用于引用数组。
  • 数组大小:数组中元素的个数,必须是一个常量表达式。

示例

#include <stdio.h>

int main() {
    int numbers[5]; // 定义一个包含5个整数的数组
    return 0;
}
数组的初始化

数组可以在定义时进行初始化,赋予每个元素初始值。如果未完全初始化,未赋值的元素会被自动初始化为零。

语法

数据类型 数组名[数组大小] = {元素1, 元素2, ..., 元素n};

示例

#include <stdio.h>

int main() {
    int numbers[5] = {1, 2, 3, 4, 5}; // 完全初始化
    int scores[5] = {90, 85}; // 部分初始化,剩余元素自动为0

    // 输出数组元素
    for (int i = 0; i < 5; i++) {
        printf("scores[%d] = %d\n", i, scores[i]);
    }

    return 0;
}

输出

scores[0] = 90
scores[1] = 85
scores[2] = 0
scores[3] = 0
scores[4] = 0
访问和修改数组元素

数组元素通过索引访问,索引从0开始。可以通过索引读取或修改特定位置的元素。

示例

#include <stdio.h>

int main() {
    int numbers[5] = {10, 20, 30, 40, 50};

    // 访问数组元素
    printf("第一个元素: %d\n", numbers[0]); // 输出 10
    printf("第三个元素: %d\n", numbers[2]); // 输出 30

    // 修改数组元素
    numbers[1] = 25; // 将第二个元素修改为25
    printf("修改后的第二个元素: %d\n", numbers[1]); // 输出 25

    return 0;
}

输出

第一个元素: 10
第三个元素: 30
修改后的第二个元素: 25
数组的遍历

遍历数组意味着依次访问数组中的每个元素,通常使用for循环实现。

示例

#include <stdio.h>

int main() {
    int numbers[5] = {1, 2, 3, 4, 5};
    int sum = 0;

    // 使用for循环遍历数组
    for (int i = 0; i < 5; i++) {
        printf("numbers[%d] = %d\n", i, numbers[i]);
        sum += numbers[i]; // 累加元素值
    }

    printf("数组元素之和: %d\n", sum); // 输出 15

    return 0;
}

输出

numbers[0] = 1
numbers[1] = 2
numbers[2] = 3
numbers[3] = 4
numbers[4] = 5
数组元素之和: 15
多种数组初始化方式
  1. 部分初始化

    int numbers[5] = {1, 2}; // numbers = {1, 2, 0, 0, 0}
    
  2. 不指定大小,由初始化列表决定

    int numbers[] = {1, 2, 3, 4, 5}; // 自动推断数组大小为5
    
  3. 全部元素初始化为零

    int numbers[5] = {0}; // numbers = {0, 0, 0, 0, 0}
    

示例

#include <stdio.h>

int main() {
    int a[5] = {1, 2}; // 部分初始化
    int b[] = {3, 4, 5}; // 自动推断大小为3
    int c[5] = {0}; // 全部初始化为0

    // 输出数组a
    printf("数组a: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", a[i]);
    }
    printf("\n");

    // 输出数组b
    printf("数组b: ");
    for (int i = 0; i < 3; i++) {
        printf("%d ", b[i]);
    }
    printf("\n");

    // 输出数组c
    printf("数组c: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", c[i]);
    }
    printf("\n");

    return 0;
}

输出

数组a: 1 2 0 0 0
数组b: 3 4 5
数组c: 0 0 0 0 0
注意事项
  • 数组越界:访问数组时,索引必须在0数组大小-1之间。越界访问会导致未定义行为,可能引发程序崩溃或数据损坏。

    示例

    #include <stdio.h>
    
    int main() {
        int numbers[3] = {1, 2, 3};
        printf("第四个元素: %d\n", numbers[3]); // 未定义行为
        return 0;
    }
    

    输出

    第四个元素: [随机值或程序崩溃]
    
  • 数组大小固定:一旦定义,数组的大小不能动态改变。如果需要动态数组,请使用指针和动态内存分配函数。

7.2 多维数组的定义与使用

多维数组是多个一维数组的集合,最常见的是二维数组。多维数组用于表示表格、矩阵等复杂数据结构。

二维数组的定义

语法

数据类型 数组名[行数][列数];
  • 行数:二维数组的行数。
  • 列数:二维数组每行的元素个数。

示例

#include <stdio.h>

int main() {
    int matrix[3][4]; // 定义一个3行4列的二维数组
    return 0;
}
二维数组的初始化

二维数组可以在定义时进行初始化,每行用一对花括号{}包围,元素用逗号分隔。

语法

数据类型 数组名[行数][列数] = {
    {元素11, 元素12, ..., 元素1n},
    {元素21, 元素22, ..., 元素2n},
    ...
    {元素m1, 元素m2, ..., 元素mn}
};

示例

#include <stdio.h>

int main() {
    // 定义并初始化一个3行3列的矩阵
    int matrix[3][3] = {
        {1, 2, 3}, // 第一行
        {4, 5, 6}, // 第二行
        {7, 8, 9}  // 第三行
    };

    // 输出二维数组元素
    for (int i = 0; i < 3; i++) { // 行循环
        for (int j = 0; j < 3; j++) { // 列循环
            printf("%d ", matrix[i][j]);
        }
        printf("\n"); // 换行
    }

    return 0;
}

输出

1 2 3
4 5 6
7 8 9
访问和修改二维数组元素

通过行索引和列索引访问特定的元素,语法为数组名[行][列]

示例

#include <stdio.h>

int main() {
    int matrix[2][3] = {
        {10, 20, 30},
        {40, 50, 60}
    };

    // 访问元素
    printf("matrix[0][1] = %d\n", matrix[0][1]); // 输出 20
    printf("matrix[1][2] = %d\n", matrix[1][2]); // 输出 60

    // 修改元素
    matrix[0][1] = 25; // 将第1行第2列的元素修改为25
    printf("修改后的 matrix[0][1] = %d\n", matrix[0][1]); // 输出 25

    return 0;
}

输出

matrix[0][1] = 20
matrix[1][2] = 60
修改后的 matrix[0][1] = 25
二维数组的遍历

遍历二维数组需要嵌套循环,外层循环遍历行,内层循环遍历列。

示例

#include <stdio.h>

int main() {
    int matrix[2][3] = {
        {1, 2, 3},
        {4, 5, 6}
    };
    int sum = 0;

    // 遍历二维数组并计算元素之和
    for (int i = 0; i < 2; i++) { // 行循环
        for (int j = 0; j < 3; j++) { // 列循环
            printf("matrix[%d][%d] = %d\n", i, j, matrix[i][j]);
            sum += matrix[i][j];
        }
    }

    printf("二维数组元素之和: %d\n", sum); // 输出 21

    return 0;
}

输出

matrix[0][0] = 1
matrix[0][1] = 2
matrix[0][2] = 3
matrix[1][0] = 4
matrix[1][1] = 5
matrix[1][2] = 6
二维数组元素之和: 21
三维数组的定义与使用(扩展)

除了二维数组,C 语言还支持多维数组,如三维数组。三维数组可用于表示立体结构的数据,如 3D 图形中的坐标点。

示例

#include <stdio.h>

int main() {
    int threeD[2][3][4] = {
        {
            {1, 2, 3, 4},
            {5, 6, 7, 8},
            {9, 10, 11, 12}
        },
        {
            {13, 14, 15, 16},
            {17, 18, 19, 20},
            {21, 22, 23, 24}
        }
    };

    // 输出三维数组元素
    for (int i = 0; i < 2; i++) { // 第一维
        for (int j = 0; j < 3; j++) { // 第二维
            for (int k = 0; k < 4; k++) { // 第三维
                printf("threeD[%d][%d][%d] = %d\n", i, j, k, threeD[i][j][k]);
            }
            printf("\n"); // 换行
        }
        printf("\n"); // 换行
    }

    return 0;
}

输出

threeD[0][0][0] = 1
threeD[0][0][1] = 2
threeD[0][0][2] = 3
threeD[0][0][3] = 4

threeD[0][1][0] = 5
threeD[0][1][1] = 6
threeD[0][1][2] = 7
threeD[0][1][3] = 8

threeD[0][2][0] = 9
threeD[0][2][1] = 10
threeD[0][2][2] = 11
threeD[0][2][3] = 12


threeD[1][0][0] = 13
threeD[1][0][1] = 14
threeD[1][0][2] = 15
threeD[1][0][3] = 16

threeD[1][1][0] = 17
threeD[1][1][1] = 18
threeD[1][1][2] = 19
threeD[1][1][3] = 20

threeD[1][2][0] = 21
threeD[1][2][1] = 22
threeD[1][2][2] = 23
threeD[1][2][3] = 24
注意事项
  • 数组索引从 0 开始:第一个元素的索引为0,最后一个元素的索引为数组大小-1
  • 内存连续:数组在内存中是连续存储的,便于快速访问,但也意味着一次性分配较大的内存可能导致内存浪费。
  • 不可变长度:数组大小在编译时必须确定,不能在运行时动态改变。如果需要动态数组,请使用指针和动态内存分配函数(如malloccalloc等)。
7.3 字符数组与字符串的区别

字符数组字符串在 C 语言中紧密相关,但它们并不完全相同。理解它们的区别有助于正确地处理文本数据。

字符数组

字符数组是一个数组,其元素类型为char,用于存储一组字符。

定义与初始化

char chars[5] = {'H', 'e', 'l', 'l', 'o'};

特点

  • 可以存储任意字符,包括不以'\0'结尾的字符序列。
  • 不具备字符串的特殊性质,不能直接作为字符串函数的参数使用,除非以'\0'结尾。

示例

#include <stdio.h>

int main() {
    char chars[5] = {'A', 'B', 'C', 'D', 'E'};

    // 输出字符数组
    for (int i = 0; i < 5; i++) {
        printf("chars[%d] = %c\n", i, chars[i]);
    }

    return 0;
}

输出

chars[0] = A
chars[1] = B
chars[2] = C
chars[3] = D
chars[4] = E
字符串

字符串在 C 语言中是一种特殊的字符数组,用于存储文本数据。字符串以空字符'\0'结尾,标志着字符串的结束。

定义与初始化

char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
char str2[] = "Hello"; // 自动添加'\0'

特点

  • '\0'结尾,表示字符串的结束。
  • 可以直接作为字符串函数(如printfstrlen等)的参数使用。
  • 字符串常量(如"Hello")实际上是一个字符数组,包含'\0'

示例

#include <stdio.h>

int main() {
    char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
    char str2[] = "World"; // 自动添加'\0'

    // 输出字符串
    printf("str1: %s\n", str1); // 使用%s格式说明符
    printf("str2: %s\n", str2);

    return 0;
}

输出

str1: Hello
str2: World
字符数组与字符串的区别
特性字符数组字符串
定义方式char arr[size]char str[size] = "text"char str[] = "text"
结束标志无(除非手动添加'\0'自动包含'\0'作为结束标志
使用场景存储单个字符或固定长度的字符序列存储文本数据,并与字符串函数配合使用
函数兼容性需要手动添加'\0'后才能作为字符串使用直接兼容字符串函数

示例比较

#include <stdio.h>

int main() {
    char charArray[5] = {'T', 'e', 's', 't', '1'};
    char string1[6] = {'T', 'e', 's', 't', '2', '\0'};
    char string2[] = "Test3";

    // 尝试使用字符串函数
    printf("charArray as string: %s\n", charArray); // 未定义行为,缺少'\0'
    printf("string1: %s\n", string1); // 正常输出
    printf("string2: %s\n", string2); // 正常输出

    return 0;
}

输出charArray as string 可能导致未定义行为):

charArray as string: Test1
string1: Test2
string2: Test3

注意

  • 当使用字符串函数(如printf%s)时,必须确保字符数组以'\0'结尾,否则可能导致内存泄漏或程序崩溃。
  • 字符数组用于存储不需要以'\0'结尾的字符序列,而字符串用于存储以'\0'结尾的文本数据。
7.4 字符串的常用操作函数

C 语言通过标准库提供了一系列函数,用于处理和操作字符串。这些函数定义在<string.h>头文件中。以下是一些常用的字符串操作函数,包括strlenstrcpystrcatstrcmp

7.4.1 strlen

功能:计算字符串的长度(不包括终止的空字符'\0')。

原型

size_t strlen(const char *str);

示例与详细说明

#include <stdio.h>
#include <string.h>

int main() {
    char str[] = "Hello, World!";
    size_t length;

    // 使用strlen函数计算字符串长度
    length = strlen(str);
    printf("字符串 \"%s\" 的长度是: %zu\n", str, length); // 输出 13

    return 0;
}

输出

字符串 "Hello, World!" 的长度是: 13

详细解释

  • strlen函数从字符串的开始位置依次计数,直到遇到'\0'为止,返回计数值。
  • size_t是无符号整数类型,适用于表示大小和长度。

更多示例

#include <stdio.h>
#include <string.h>

int main() {
    char emptyStr[] = ""; // 空字符串
    char singleChar[] = "A"; // 单字符字符串
    char sentence[] = "C语言学习笔记";

    printf("emptyStr 的长度: %zu\n", strlen(emptyStr)); // 输出 0
    printf("singleChar 的长度: %zu\n", strlen(singleChar)); // 输出 1
    printf("sentence 的长度: %zu\n", strlen(sentence)); // 输出 7

    return 0;
}

输出

emptyStr 的长度: 0
singleChar 的长度: 1
sentence 的长度: 7
7.4.2 strcpy

功能:将一个字符串复制到另一个字符串。

原型

char *strcpy(char *dest, const char *src);
  • dest:目标字符串,必须有足够的空间存放源字符串及其终止符。
  • src:源字符串。

示例与详细说明

#include <stdio.h>
#include <string.h>

int main() {
    char source[] = "Hello, C!";
    char destination[20]; // 确保有足够的空间

    // 使用strcpy函数复制字符串
    strcpy(destination, source);
    printf("源字符串: %s\n", source);
    printf("目标字符串: %s\n", destination);

    return 0;
}

输出

源字符串: Hello, C!
目标字符串: Hello, C!

详细解释

  • strcpy函数将source字符串的内容复制到destination字符串,包括终止的'\0'
  • 注意:目标数组必须有足够的空间容纳源字符串,否则会导致缓冲区溢出,产生安全漏洞。

更多示例

#include <stdio.h>
#include <string.h>

int main() {
    char src1[] = "First String";
    char src2[] = "Second String";
    char dest1[20];
    char dest2[20];

    // 复制不同的源字符串到不同的目标字符串
    strcpy(dest1, src1);
    strcpy(dest2, src2);

    printf("dest1: %s\n", dest1); // 输出 "First String"
    printf("dest2: %s\n", dest2); // 输出 "Second String"

    return 0;
}

输出

dest1: First String
dest2: Second String
7.4.3 strcat

功能:将一个字符串连接到另一个字符串的末尾。

原型

char *strcat(char *dest, const char *src);
  • dest:目标字符串,必须有足够的空间存放源字符串的内容。
  • src:源字符串。

示例与详细说明

#include <stdio.h>
#include <string.h>

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

    // 使用strcat函数连接字符串
    strcat(dest, src);
    printf("连接后的字符串: %s\n", dest); // 输出 "Hello, World!"

    return 0;
}

输出

连接后的字符串: Hello, World!

详细解释

  • strcat函数将src字符串的内容追加到dest字符串的末尾,并添加终止的'\0'
  • 注意:目标数组必须有足够的空间容纳追加的字符串,否则会导致缓冲区溢出。

更多示例

#include <stdio.h>
#include <string.h>

int main() {
    char greeting[50] = "Good ";
    char timeOfDay[] = "Morning";
    char name[] = ", Alice!";

    // 连接多个字符串
    strcat(greeting, timeOfDay); // "Good Morning"
    strcat(greeting, name); // "Good Morning, Alice!"

    printf("完整的问候语: %s\n", greeting); // 输出 "Good Morning, Alice!"

    return 0;
}

输出

完整的问候语: Good Morning, Alice!
7.4.4 strcmp

功能:比较两个字符串的大小关系。

原型

int strcmp(const char *str1, const char *str2);
  • str1str2:要比较的两个字符串。

返回值

  • 0:两个字符串相等。
  • 正数str1大于str2
  • 负数str1小于str2

示例与详细说明

#include <stdio.h>
#include <string.h>

int main() {
    char str1[] = "Apple";
    char str2[] = "Banana";
    char str3[] = "Apple";

    // 比较str1和str2
    int result1 = strcmp(str1, str2);
    if (result1 < 0) {
        printf("\"%s\" 小于 \"%s\"\n", str1, str2);
    } else if (result1 > 0) {
        printf("\"%s\" 大于 \"%s\"\n", str1, str2);
    } else {
        printf("\"%s\" 等于 \"%s\"\n", str1, str2);
    }

    // 比较str1和str3
    int result2 = strcmp(str1, str3);
    if (result2 < 0) {
        printf("\"%s\" 小于 \"%s\"\n", str1, str3);
    } else if (result2 > 0) {
        printf("\"%s\" 大于 \"%s\"\n", str1, str3);
    } else {
        printf("\"%s\" 等于 \"%s\"\n", str1, str3);
    }

    return 0;
}

输出

"Apple" 小于 "Banana"
"Apple" 等于 "Apple"

详细解释

  • strcmp函数逐字符比较两个字符串,直到找到不同的字符或到达字符串末尾。
  • 比较是基于字符的 ASCII 值,字母的大小写会影响比较结果。

更多示例

#include <stdio.h>
#include <string.h>

int main() {
    char a[] = "hello";
    char b[] = "hello";
    char c[] = "world";
    char d[] = "Hello"; // 注意大写 'H'

    // 比较相同字符串
    printf("strcmp(a, b) = %d\n", strcmp(a, b)); // 输出 0

    // 比较不同字符串
    printf("strcmp(a, c) = %d\n", strcmp(a, c)); // 输出 <0 因为 'h' < 'w'

    // 比较大小写不同的字符串
    printf("strcmp(a, d) = %d\n", strcmp(a, d)); // 输出 >0 因为 'h' > 'H'

    return 0;
}

输出

strcmp(a, b) = 0
strcmp(a, c) = -15
strcmp(a, d) = 32

注意事项

  • strcmp比较的是字符的 ASCII 值,大小写不同会影响结果(例如,'A' < 'a')。
  • 为了进行不区分大小写的比较,可以使用strcasecmp函数(POSIX 标准,不在 C 标准库中)。
7.5 字符数组与指针的关系(扩展)

虽然用户没有明确请求此小节,但理解字符数组与指针的关系对于深入学习字符串处理非常有帮助。

字符数组字符指针在很多情况下可以互换使用,但它们在内存分配和操作方式上存在差异。

字符数组
  • 内存分配:字符数组在栈上分配固定的内存空间。
  • 不可变长度:数组大小在定义时确定,无法动态改变。
  • 独立存储:字符数组存储自己的数据,复制或修改不影响其他数组。

示例

#include <stdio.h>

int main() {
    char arr1[] = "Hello";
    char arr2[] = "Hello";

    // 修改arr1
    arr1[0] = 'h';
    printf("arr1: %s\n", arr1); // 输出 "hello"
    printf("arr2: %s\n", arr2); // 输出 "Hello"

    return 0;
}

输出

arr1: hello
arr2: Hello
字符指针
  • 内存分配:字符指针可以指向字符串常量或动态分配的内存。
  • 可变指向:指针可以指向不同的字符串或内存位置。
  • 共享存储:多个指针可以指向同一字符串,修改一个指针指向的内容会影响所有指向该内容的指针(如果内容可修改)。

示例

#include <stdio.h>

int main() {
    char *ptr1 = "Hello";
    char *ptr2 = "Hello";

    // 修改ptr1指向的内容(未定义行为,字符串常量通常存储在只读内存)
    // ptr1[0] = 'h'; // 错误:尝试修改只读内存

    // 指针指向不同的字符串
    ptr1 = "Hi";
    printf("ptr1: %s\n", ptr1); // 输出 "Hi"
    printf("ptr2: %s\n", ptr2); // 输出 "Hello"

    return 0;
}

输出

ptr1: Hi
ptr2: Hello

注意事项

  • 只读存储:字符串常量(如"Hello")通常存储在只读内存中,尝试修改会导致未定义行为。
  • 动态分配:如果需要修改字符串内容,应使用字符数组或动态内存分配函数(如malloc)分配可写内存。

示例(使用动态内存分配):

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

int main() {
    // 分配内存并复制字符串
    char *ptr = (char *)malloc(6 * sizeof(char)); // 分配6个字节
    if (ptr == NULL) {
        printf("内存分配失败\n");
        return 1;
    }

    strcpy(ptr, "Hello"); // 复制字符串到动态分配的内存
    printf("ptr: %s\n", ptr); // 输出 "Hello"

    // 修改内容
    ptr[0] = 'h';
    printf("修改后的ptr: %s\n", ptr); // 输出 "hello"

    // 释放内存
    free(ptr);

    return 0;
}

输出

ptr: Hello
修改后的ptr: hello
7.6 总结

本节详细介绍了一维数组和多维数组的定义、初始化、访问和遍历方法,阐述了字符数组与字符串的区别,并深入讲解了字符串的常用操作函数,包括strlenstrcpystrcatstrcmp。通过多个实例和详细的注释,帮助你理解和掌握数组与字符串在 C 语言中的使用方法。这些知识对于处理数据、构建复杂的数据结构和开发功能丰富的应用程序至关重要。

  • 一维数组:用于存储同类型数据的线性集合,通过索引访问和修改元素。
  • 多维数组:扩展了一维数组的概念,适用于表示表格、矩阵等多维数据结构。
  • 字符数组与字符串:了解它们的区别和联系,有助于正确处理文本数据。
  • 字符串操作函数:掌握常用的字符串函数,提高字符串处理的效率和准确性。
Prev: 控制语句 Next: 函数