云计算百科
云计算领域专业知识百科平台

C语言基础(六)数组相关

一、为什么需要数组?

在编程中,我们经常需要处理多个相同类型的数据。比如存储10个学生的成绩,如果不用数组,就得定义10个单独的变量(score1, score2, …),不仅麻烦,而且无法用循环统一处理。数组正是为了解决这类问题而生的——它能在内存中连续存储多个相同类型的数据,并通过下标快速访问每个元素。


二、一维数组基础

1. 数组的定义

语法格式:

类型说明符 数组名[常量表达式];

例如:

int a[10]; // 定义一个包含10个int元素的数组
char str[32]; // 定义包含32个char元素的数组
float array[5]; // 定义包含5个float元素的数组

  • 类型说明符:可以是基本数据类型(int、char、float等),也可以是构造类型,但不能是void。
  • 数组名:遵循标识符命名规则(字母、数字、下划线,数字不能开头)。
  • 常量表达式:表示数组元素个数,必须是编译时确定的常量(不能是变量)。

2. 数组元素的访问

通过 数组名[下标] 访问元素,下标从 0 开始,到 元素个数-1 结束。

a[0] = 10; // 给第一个元素赋值
a[5] = 99; // 给第六个元素赋值
printf("%d", a[0]); // 输出第一个元素

注意:下标可以是常量、变量或表达式(结果必须为整型)。访问时不要超出范围,否则会越界访问,可能导致程序崩溃或数据错乱。

3. 数组的初始化

初始化即在定义时给元素赋初值。

  • 全部初始化:

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

  • 局部初始化(未初始化的元素自动为0):

int a[5] = {1, 2, 3}; // 前三个为1,2,3,后两个为0

  • 初始化全0:

int a[5] = {0}; // 所有元素为0
int a[5] = {0,0,0,0,0};

  • 省略数组大小(必须初始化):

int a[] = {1,2,3,4,5}; // 编译器自动确定大小为5

重要:如果不初始化,数组元素的值是随机的(内存中遗留的值),所以定义时尽量初始化,哪怕只是{0}。

4. 数组的存储特性

  • 单一性:所有元素类型相同。
  • 有序性:元素按下标从0到N-1顺序存储。
  • 连续性:数组占用一块连续的内存空间。

这一点非常重要,因为连续存储使得我们可以通过指针和偏移量快速访问,也是数组效率高的原因。


三、数组的经典应用:排序

排序是数组的典型应用。这里以选择排序为例,掌握其思想。

选择排序(升序):每次从待排序位置开始,往后找到最小值,放到当前位置。

int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};
int len = sizeof(a) / sizeof(a[0]); // 计算元素个数

for (int i = 0; i < len 1; i++) {
for (int j = i + 1; j < len; j++) {
if (a[i] > a[j]) { // 升序:如果前面大于后面,交换
int tmp = a[i];
a[i] = a[j];
a[j] = tmp;
}
}
}

时间复杂度:O(n²),空间复杂度:O(1)。选择排序是不稳定的(相同元素相对顺序可能改变)。


四、字符型数组(字符串)

C语言没有专门的字符串类型,而是用字符数组来存储字符串,并以'\\0'(ASCII码0)作为结束标志。

1. 定义与初始化

char str[32];
char str[32] = {'h', 'e', 'l', 'l', 'o'}; // 不推荐,未初始化部分为0
char str[32] = {"hello"}; // 推荐
char str[32] = "hello"; // 最常用
char str[] = "hello"; // 自动确定大小为6(包含'\\0')

注意:"hello"实际占6个字节(5个字符 + 1个'\\0')。

2. 输入与输出

  • 输出:可以使用printf("%s", str)或puts(str)(自动换行)。
  • 输入:可以使用scanf("%s", str)(遇空格或Tab停止)或gets(str)(遇换行停止,但危险,可能越界)。

char str[100] = {0};
scanf("%s", str); // 输入 "hello world" 只读入 "hello"
gets(str); // 可读入带空格的字符串,但存在越界风险

3. 常用字符串操作函数(手写实现)

虽然标准库提供了strlen、strcpy、strcat等,但理解其底层实现至关重要。

① 求字符串长度 (strlen)
统计有效字符个数(不包括'\\0')。

int my_strlen(const char *s) {
int len = 0;
while (s[len] != '\\0') {
len++;
}
return len;
}

② 字符串拷贝 (strcpy)
将源字符串复制到目标数组(目标数组空间要足够)。

void my_strcpy(char *dst, const char *src) {
int i = 0;
while (src[i] != '\\0') {
dst[i] = src[i];
i++;
}
dst[i] = '\\0'; // 别忘了结束标志
}

③ 字符串拼接 (strcat)
将源字符串追加到目标字符串末尾。

void my_strcat(char *dst, const char *src) {
int i = 0, j = 0;
while (dst[i] != '\\0') i++; // 找到dst的末尾
while (src[j] != '\\0') {
dst[i] = src[j];
i++;
j++;
}
dst[i] = '\\0';
}


五、二维数组

二维数组可以理解为“数组的数组”,常用于存储矩阵或表格数据。

1. 定义与访问

int a[3][4]; // 3行4列,共12个元素

访问方式:a[行下标][列下标],行下标范围02,列下标03。

2. 初始化

  • 全部初始化(分行或不分行):

int a[3][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}};
int a[][4] = {{1,2,3,4}, {5,6,7,8}, {9,10,11,12}}; // 可省略行数
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12}; // 自动按行填充

  • 局部初始化(未初始化的补0):

int a[3][4] = {{1,2}, {3,4,5}, {6}};
// 第一行:1,2,0,0;第二行:3,4,5,0;第三行:6,0,0,0

注意:在初始化时,行数可以省略,但列数不能省略。

3. 二维字符型数组

用于存储多个字符串(如学生名单)。

char str[3][100] = {0}; // 3个字符串,每个最长99字符
char str[3][100] = {"hello", "world", "how"};

输入输出时,用gets(str[i])或scanf("%s", str[i]),循环处理每一行。


六、易错点与最佳实践

  • 越界访问:下标超出范围不会报错,但会破坏内存数据,务必确保下标在合法范围内。
  • 忘记'\\0'结束符:字符数组作为字符串使用时,必须保证有'\\0',否则输出会乱码。
  • sizeof与strlen的区别:
    • sizeof是运算符,计算数组占用的总字节数(包括'\\0')。
    • strlen是函数,计算字符串有效字符个数(不含'\\0')。
  • 数组名是地址常量:不能对数组名赋值,如str = "hello"是错误的。
  • 尽量避免使用gets:推荐fgets或scanf配合宽度限制。


  • 赞(0)
    未经允许不得转载:网硕互联帮助中心 » C语言基础(六)数组相关
    分享到: 更多 (0)

    评论 抢沙发

    评论前必须登录!