文章目录
数组
存储
核心结论是:无论数组的维度是多少,计算机内存的物理结构都是一维的(线性排列的)。因此,二维数组在内存中实际上是被“压扁”成一维序列来存储的。
行优先存储
这是C、C++、Python等绝大多数编程语言默认采用的方式。
方式:从第0行开始,一行接一行地依次存储。
示例:
假设我们有一个 3行 x 4列 的二维数组 arr[3][4]:
text
| a00 | a01 | a02 | a03 | // 第0行
| a10 | a11 | a12 | a13 | // 第1行
| a20 | a21 | a22 | a23 | // 第2行
在内存中的排列顺序为:
a00 -> a01 -> a02 -> a03 -> a10 -> a11 -> a12 -> a13 -> a20 -> a21 -> a22 -> a23
地址计算:
对于一个 M行 x N列 的数组 arr[M][N],要访问元素 arr[i][j],其在一维内存序列中的位置(假设基地址为 base,每个元素占 L 字节)可以通过以下公式计算:
地址(arr[i][j]) = base + (i * N + j) * L
-
i * N:跳过前面i整行的元素。 -
+ j:在第i行中,再跳过j个元素。 -
* L:乘以每个元素所占的字节数(如int通常是4字节)。
高效遍历
为什么这很重要?
-
性能优化(缓存友好性):
- 在行优先存储的语言(如C++)中,按行遍历(外层循环为行,内层循环为列)的效率远高于按列遍历。因为按行访问时,相邻的数据在内存中也是相邻的,CPU缓存可以高效地加载一整块数据(缓存行)。
- 反之,如果按列遍历,每次访问都可能需要从内存的不同位置加载数据,导致大量的缓存未命中,性能会急剧下降。
C++示例(行优先语言):
cpp
// 高效:按行遍历 for (int i = 0; i < M; i++) { for (int j = 0; j < N; j++) { arr[i][j] = ...; // 内存访问是连续的 } } // 低效:按列遍历 for (int j = 0; j < N; j++) { for (int i = 0; i < M; i++) { arr[i][j] = ...; // 每次访问都跳过了N个元素,不连续 } }
初始化
一、一维数组初始化
1. 基本初始化方式
c
// 方式1:声明时直接初始化
int arr1[5] = {1, 2, 3, 4, 5};
// 方式2:不指定大小,编译器自动计算
int arr2[] = {1, 2, 3, 4, 5}; // 自动确定为5个元素
// 方式3:部分初始化,其余自动补0
int arr3[5] = {1, 2}; // 实际为:{1, 2, 0, 0, 0}
// 方式4:全部初始化为0
int arr4[5] = {0}; // {0, 0, 0, 0, 0}
2. 字符数组初始化
c
// 字符串初始化
char str1[] = "hello"; // 自动包含'\0',共6个字符
char str2[6] = "hello"; // 同上
char str3[] = {'h', 'e', 'l', 'l', 'o', '\0'}; // 等价写法
// 字符初始化
char chars[5] = {'a', 'b', 'c'}; // {'a', 'b', 'c', 0, 0}
二、多维数组初始化
1. 二维数组
c
// 完全初始化
int matrix1[2][3] = {
{1, 2, 3},
{4, 5, 6}
};
// 部分初始化
int matrix2[2][3] = {
{1, 2}, // {1, 2, 0}
{4} // {4, 0, 0}
};
// 连续初始化
int matrix3[2][3] = {1, 2, 3, 4, 5, 6}; // 按行优先存储
// 省略第一维大小
int matrix4[][3] = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
}; // 自动推断为3行
2. 三维数组
c
int cube[2][2][3] = {
{
{1, 2, 3},
{4, 5, 6}
},
{
{7, 8, 9},
{10, 11, 12}
}
};
下面考虑这个赋值过程:
维度解析
cube[2][2][3] 分解:
- 第一维:2个"面"
- 第二维:每个面有2个"行"
- 第三维:每行有3个"列"
内存布局与二维数组类似
面0:
行0: [1, 2, 3]
行1: [4, 5, 6]
面1:
行0: [7, 8, 9]
行1: [10, 11, 12]
三、特殊初始化技巧
1. 使用memset函数
c
#include <string.h>
int arr[10];
memset(arr, 0, sizeof(arr)); // 全部初始化为0
memset(arr, -1, sizeof(arr)); // 全部初始化为-1
2. 指定初始化器(C99标准)
c
// 指定特定位置初始化
int arr[10] = {[0] = 1, [5] = 2, [9] = 3};
// 结果:{1, 0, 0, 0, 0, 2, 0, 0, 0, 3}
// 范围初始化(GCC扩展)
int arr2[10] = {[0 ... 4] = 1, [5 ... 9] = 2};
// 结果:{1, 1, 1, 1, 1, 2, 2, 2, 2, 2}
四、完整示例程序
c
#include <stdio.h>
#include <string.h>
int main() {
// 一维数组示例
int scores[5] = {95, 87, 92, 78, 85};
printf("一维数组:");
for(int i = 0; i < 5; i++) {
printf("%d ", scores[i]);
}
printf("\n");
// 二维数组示例
int classroom[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("%d ", classroom[i][j]);
}
printf("\n");
}
// 字符数组示例
char greeting[] = "Hello, World!";
printf("字符串:%s\n", greeting);
return 0;
}
五、注意事项
- 数组越界:初始化值不能超过数组大小
- 自动补零:未初始化的部分会自动设为0
- 字符串结束符:字符数组用字符串初始化时会自动添加’\0’
- const数组:如果数组内容不变,建议加const修饰符
操作
输入
1. getchar() 用法
函数原型:int getchar(void)
功能:从标准输入读取一个字符
返回值:读取的字符(转为int类型),遇到错误或文件结束返回EOF
#include<stdio.h>
int main() {
char ch;
// 基本用法
ch = getchar();
// 循环读取直到换行
while((ch = getchar()) != '\n') {
// 处理每个字符
printf("%c", ch);
}
return 0;
}
2. fgets() 用法
函数原型:char *fgets(char *str, int n, FILE *stream)
功能:从指定流读取一行字符串
参数:
str:存储读取字符串的缓冲区n:最大读取字符数(包括结尾的’\0’)stream:输入流(stdin表示标准输入)
#include<stdio.h>
int main() {
char str[100];
// 读取一行,最多99个字符
fgets(str, 100, stdin);
printf("输入的内容:%s", str);
return 0;
}

15万+

被折叠的 条评论
为什么被折叠?



