一、数组
一维数组
1. 数组定义
基本语法 正确示例:
// 数据类型 数组名[元素个数];
char arr[5]; // 字符数组
int arr[5]; // 整型数组
double arr[5]; // 双精度浮点数组
float arr[5]; // 单精度浮点数组
// 使用常量表达式定义数组大小
int a[5]; // 整型常量
int b['A']; // 字符常量(等价于 int b[65])
int c[3*4]; // 常量表达式(等价于 int c[12])
❌ 错误写法:
int[5] a; // 错误:[]必须在数组名后面
int[] b; // 错误:必须指定元素个数
int size = 5;
int d[size]; // 错误:C89标准不支持变量长度(C99支持但有限制)
重要规则:
[]必须放在数组名后面- 元素个数必须是常量或常量表达式
- 大多数情况下不能省略元素个数(函数形参和初始化时除外)
2. 数组初始化
完全初始化:
char arr[5] = {90, 91, 92, 93, 94}; // 全部元素初始化
部分初始化:
char arr[5] = {90, 91}; // 前两个元素初始化,其余自动设为0
// 等价于:{90, 91, 0, 0, 0}
自动推断大小:
int arr[] = {1, 2, 3, 4, 5}; // 编译器自动推断大小为5
❌ 错误初始化:
int a[3];
a[3] = {1, 2, 3}; // 错误:定义后不能整体赋值
a = {1, 2, 3}; // 错误:数组名是常量指针,不能赋值
注意: 数组名代表整个数组的起始地址,是一个常量指针。
3. 数组访问
读取元素:
char arr[5] = {90, 91, 92, 93, 94};
char s1 = arr[0]; // 读取第0个元素:90
char s2 = arr[1]; // 读取第1个元素:91
修改元素:
arr[1] = 89; // 修改第1个元素为89
⚠️ 重要警告:
int arr[5] = {1, 2, 3, 4, 5};
arr[5] = 6; // ❌ 数组越界!C语言不会检查边界
C语言编译器不会对数组下标进行越界检查,程序员必须自己确保访问安全。
4. 数组作为函数参数
#include <stdio.h>
// 函数声明:数组作为参数
void modifyArray(int arr[], int size) {
for(int i = 0; i < size; i++) {
arr[i] *= 2; // 修改数组元素
}
}
int main() {
int numbers[5] = {1, 2, 3, 4, 5};
printf("修改前: ");
for(int i = 0; i < 5; i++) {
printf("%d ", numbers[i]);
}
// 传递数组给函数
modifyArray(numbers, 5);
printf("\n修改后: ");
for(int i = 0; i < 5; i++) {
printf("%d ", numbers[i]); // 输出:2 4 6 8 10
}
return 0;
}
关键点: 数组名作为参数时,传递的是数组的起始地址,形参和实参指向同一内存空间。
二维数组
1. 二维数组定义与初始化
标准初始化:
// 3行4列的二维数组
int matrix[3][4] = {
{1, 2, 3, 4}, // 第0行
{5, 6, 7, 8}, // 第1行
{9, 10, 11, 12} // 第2行
};
字符二维数组示例:
char arr[5][3] = {
{91, 92, 93}, // 第0行
{81, 82, 83}, // 第1行
{71, 72, 73}, // 第2行
{61, 62, 63}, // 第3行
{51, 52, 53} // 第4行
};
简化初始化:
// 可以省略第一维的大小
int arr[][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
}; // 编译器自动推断为 int arr[3][3]
2. 二维数组访问
int matrix[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
// 访问单个元素
int element = matrix[0][0]; // 第0行第0列:1
matrix[1][2] = 99; // 修改第1行第2列的元素
// 遍历二维数组
for(int i = 0; i < 3; i++) { // 遍历行
for(int j = 0; j < 4; j++) { // 遍历列
printf("%d ", matrix[i][j]);
}
printf("\n");
}
注意: 数组下标从
0开始,matrix[m][n]表示第 m 行第 n 列。
重要总结
核心要点
- 一维数组最重要:实际开发中一维数组使用频率远高于多维数组
- 内存连续性:数组元素在内存中是连续存储的
- 下标从0开始:C语言数组下标始终从0开始计数
- 边界自检:编译器不检查数组越界,需要程序员保证安全
使用建议
// 良好的编程习惯:使用常量定义数组大小
#define MAX_SIZE 100
int main() {
int arr[MAX_SIZE];
// 循环时使用明确的边界检查
for(int i = 0; i < MAX_SIZE; i++) {
// 安全操作
}
return 0;
}
实际应用优先级
- 一维数组:★★★★★(最常用)
- 二维数组:★★☆☆☆(较少使用)
- 高维数组:★☆☆☆☆(极少使用)
掌握一维数组的使用是学习C语言数据结构的基础,二维及以上数组在实际工程中应用场景有限。
二、字符数组和字符串
字符串的本质
C语言中没有专门的字符串数据类型,而是通过 字符数组 来模拟字符串的功能。
关键概念:
- C语言中的字符串一定是
char型数组 - 但
char型数组不一定是字符串 -
字符串 = 以数字
0(或\0)结尾的字符数组
字符串的定义与区别
1. 字符 vs 字符数组 vs 字符串
#include <stdio.h>
int main() {
// 1. 单个字符
char ch = 'w';
// 2. 普通字符数组 - 不是字符串!
char arr[5] = {'h','e','l','l','o'};
// 3. 字符串 - 以 \0 结尾
char str1[] = {'h','e','l','l','o','\0'}; // 手动添加 \0
char str2[] = {'h','e','l','l','o', 0}; // 等价写法,数字0 = \0
// 4. 字符串字面量 - 编译器自动添加 \0
char *str3 = "hello"; // 字符串常量
char str4[] = "hello"; // 字符数组,自动添加 \0
return 0;
}
2. 内存布局对比
普通字符数组: | 'h' | 'e' | 'l' | 'l' | 'o' | ? | ? | ... |
字符串: | 'h' | 'e' | 'l' | 'l' | 'o' | \0 | ? | ... |
字符串的打印与遍历
打印示例
#include <stdio.h>
int main() {
char arr[5] = {'h','e','l','l','o'}; // 普通字符数组
char str[] = {'h','e','l','l','o','\0'}; // 字符串
char str2[] = "hello"; // 字符串
// ❌ 危险:arr没有\0结尾,printf会越界读取!
printf("arr: %s\n", arr); // 可能打印乱码
// ✅ 安全:str和str2以\0结尾
printf("str: %s\n", str); // 输出: hello
printf("str2: %s\n", str2); // 输出: hello
return 0;
}
遍历示例
#include <stdio.h>
int main() {
char arr[5] = {'h','e','l','l','o'};
char str[] = {'h','e','l','l','o','\0'};
char str2[] = "hello";
printf("遍历字符数组arr:\n");
for (int i = 0; i < sizeof(arr); i++) {
printf("%c", arr[i]); // 输出: hello
}
printf("\n");
printf("遍历字符串str:\n");
for (int i = 0; i < sizeof(str); i++) {
printf("%c", str[i]); // 输出: hello
}
printf("\n");
printf("遍历字符串str2:\n");
for (int i = 0; i < sizeof(str2); i++) {
printf("%c", str2[i]); // 输出: hello
}
printf("\n");
return 0;
}
字符串长度与结束标志
字符串长度规则
- 从头开始,到第一个
\0结束 -
\0之前的字符个数就是字符串长度
重要示例
#include <stdio.h>
int main() {
char str1[30] = "http://c.biancheng.net";
char str2[] = "C Language";
char str3[30] = "You are a good\0 boy!";
printf("str1: %s\n", str1); // 输出: http://c.biancheng.net
printf("str2: %s\n", str2); // 输出: C Language
printf("str3: %s\n", str3); // 输出: You are a good
return 0;
}
解释:
-
str1,str2: 编译器自动在末尾添加\0,正常输出整个字符串 -
str3: 遇到中间的\0就结束,不输出后面的 " boy!"
关键注意事项
1. 数组大小要足够
// ❌ 错误:数组太小,没有空间存放 \0
char str[5] = "hello"; // 错误!需要6个字节
// ✅ 正确:数组大小 = 字符数 + 1(给\0留位置)
char str[6] = "hello"; // |h|e|l|l|o|\0|
char str[] = "hello"; // 编译器自动计算为6
2. 必须正确终止
// ❌ 不是字符串(缺少\0)
char not_string[5] = {'h','e','l','l','o'};
// ✅ 是字符串(有\0终止)
char is_string[6] = {'h','e','l','l','o','\0'};
char is_string2[] = "hello";
3. \0 之后的内容被忽略
char msg[20] = "Hello\0World";
printf("%s", msg); // 只输出 "Hello",遇到\0就停止
实用技巧总结
安全定义字符串
// 方法1:使用字符串字面量(推荐)
char str1[] = "Hello World";
// 方法2:显式添加\0
char str2[] = {'H','e','l','l','o','\0'};
// 方法3:先声明后初始化
char str3[20];
str3[0] = 'H';
str3[1] = 'i';
str3[2] = '\0'; // 重要:手动添加结束符
验证字符串完整性
#include <stdio.h>
#include <string.h>
int main() {
char str[] = "hello";
// 检查是否以\0结尾
printf("Length: %zu\n", strlen(str)); // 输出: 5
printf("Sizeof: %zu\n", sizeof(str)); // 输出: 6 (包含\0)
printf("Last char: %d\n", str[5]); // 输出: 0 (\0的ASCII值)
return 0;
}
核心要点回顾
-
字符串本质:以
\0结尾的字符数组 -
结束标志:数字
0或\0,不是字符'0' -
数组大小:必须 ≥ 字符数 + 1(为
\0预留空间) -
打印行为:
printf遇到\0就停止输出 -
安全第一:确保所有字符串正确以
\0结尾