
数组的基本概念、地址与指针关系、赋值引用、多维数组、字符数组及语法解析等方面,补充数组越界、函数传参等实用内容
C 语言基础数组核心知识点总结
一、数组的基本概念
1. 定义与本质
- 概念:数组是相同类型变量的集合,所有元素存储在连续的内存空间中,通过下标统一管理。
- 语法:类型 数组名[元素个数];(如int buf[5];表示定义一个包含 5 个 int 型元素的数组buf)。
- 核心特征:
- 元素类型一致(如 int、char 等)。
- 内存连续(元素地址依次递增,间隔为元素类型的字节数)。
- 大小固定(定义时需指定元素个数,后续不可修改)。
2. 初始化
- 定义时赋值:在声明数组的同时为元素赋值(程序运行前完成)。
|
int buf[5] = {100, 200, 300, 400, 500}; // 完全初始化 int buf[5] = {100, 200}; // 部分初始化,未赋值元素默认为0 int buf[] = {100, 200, 300}; // 省略元素个数,编译器自动推断为3 |
- 注意:初始化时元素个数不能超过定义的大小(如int buf[3] = {1,2,3,4};会报错)。
二、数组的地址与指针关系
1. 关键地址表示及区别
数组名、首元素地址、整个数组地址的数值相同,但作用范围(步长)不同,是数组操作的核心易错点。
|
地址表示 |
含义 |
作用范围(+1 后的地址偏移) |
示例(int buf [5]) |
|
buf |
数组首元素的地址 |
1 个元素的大小(如 int 为 4 字节) |
buf+1指向 buf [1] |
|
&buf[0] |
数组首元素的地址(与buf等价) |
1 个元素的大小 |
&buf[0]+1指向 buf [1] |
|
&buf |
整个数组的地址 |
整个数组的大小(如 5 个 int 为 20 字节) |
&buf+1指向数组末尾后 1 个位置 |
- 示例验证:
|
int buf[5] = {100, 200, 300, 400, 500}; printf("buf: %p, buf+1: %p\n", buf, buf+1); // 地址差4字节 printf("&buf[0]: %p, &buf[0]+1: %p\n", &buf[0], &buf[0]+1); // 地址差4字节 printf("&buf: %p, &buf+1: %p\n", &buf, &buf+1); // 地址差20字节 |
2. 元素访问的两种方式
- 下标法(推荐,直观):buf[i]表示访问第 i 个元素(下标从 0 开始)。
- 指针法(底层实现):*(buf+i)与buf[i]等价,通过地址偏移访问元素。
|
int buf[5] = {100, 200, 300, 400, 500}; printf("buf[2] = %d\n", buf[2]); // 下标法,输出300 printf("*(buf+2) = %d\n", *(buf+2)); // 指针法,输出300 |
三、数组的赋值与引用
1. 单个元素操作
- 赋值:通过下标为单个元素赋值(数组定义后不可整体赋值,如buf = {1,2,3};错误)。
|
int buf[5]; buf[0] = 100; // 为第1个元素赋值 buf[1] = 200; // 为第2个元素赋值 |
- 引用:通过下标或指针访问元素值,常用于读取或修改。
2. 循环批量操作
- 遍历赋值 / 读取:使用 for 循环结合数组长度遍历所有元素(项目开发中常用)。
|
int buf[5]; // 循环赋值 for (int i = 0; i < 5; i++) { buf[i] = 100 * (i + 1); // buf[0]=100, buf[1]=200, ..., buf[4]=500 } // 循环读取 for (int i = 0; i < 5; i++) { printf("buf[%d] = %d\n", i, buf[i]); } |
3. 数组长度计算
- 宏定义实现:通过sizeof计算数组总大小与单个元素大小的比值,得到元素个数。
|
#define CAL_ARRAY_NUM(A) (sizeof(A) / sizeof(A[0])) // 通用宏定义 int buf[5] = {100, 200, 300, 400, 500}; int len = CAL_ARRAY_NUM(buf); // len = 20(总大小) / 4(int大小) = 5 |
- 注意:该方法仅适用于数组名直接作为参数,若数组退化为指针(如函数参数),则失效。
四、多维数组
1. 基本概念与语法
- 定义:元素为数组的数组(如二维数组是 “数组的数组”,三维数组是 “数组的数组的数组”)。
- 语法:类型 数组名[行数][列数];(以二维数组为例)。
|
int buf[2][3] = {{1, 2, 3}, {4, 5, 6}}; // 2行3列的二维数组 |
- 内存布局:多维数组在内存中仍为连续存储,按行优先排列(先存第 1 行所有元素,再存第 2 行,以此类推)。
2. 地址与元素访问
- 地址表示:与一维数组类似,需区分不同层级的地址范围:
- buf:二维数组首行(第 1 个一维数组)的地址,作用范围为 1 行的大小(如 3 个 int 为 12 字节)。
- *buf/buf[0]:首行首元素的地址,作用范围为 1 个元素的大小(4 字节)。
- &buf:整个二维数组的地址,作用范围为数组总大小(如 2 行 3 列 int 为 24 字节)。
- 元素访问:
|
int buf[2][3] = {{1,2,3}, {4,5,6}}; printf("buf[1][2] = %d\n", buf[1][2]); // 下标法,输出6 printf("*(*(buf+1)+2) = %d\n", *(*(buf+1)+2)); // 指针法,输出6 |
-
- 下标法:buf[i][j](第 i 行第 j 列元素)。
- 指针法:*(*(buf+i)+j)(与下标法等价)。
五、字符数组与字符串
1. 字符数组的特殊性
- 定义:元素类型为 char 的数组,可存储字符序列,若以'\0'(空字符)结尾则称为字符串。
- 初始化方式:
|
char s1[5] = {'a', 'b', 'c', 'd', 'e'}; // 字符数组(非字符串,无'\0') char s2[6] = {'a', 'b', 'c', 'd', 'e', '\0'}; // 字符串(有'\0') char s3[6] = "abcde"; // 字符串(编译器自动添加'\0') char s4[] = "hello"; // 省略大小,编译器自动包含'\0'(总大小6) |
2. 字符串操作注意事项
- 字符串结束标志:'\0'是字符串的终止符,printf("%s")、strlen等函数依赖它判断结束。
- 示例:char s[5] = "hello";会因数组大小不足丢失'\0',导致打印时越界。
- 字符数组赋值:
- 不可直接用=整体赋值(如s = "abc";错误)。
- 需用字符串函数strcpy(需包含<string.h>):
|
char s[20]; strcpy(s, "hello world"); // 正确:将字符串复制到字符数组 |
- 字符数组 vs 字符指针:
- 字符数组(char s[20]):存储在栈区,内容可修改。
- 字符指针(char *p = "hello"):指向常量区,内容不可修改(修改会导致段错误),但指针指向可改变(如p = "new")。
六、数组语法解析
1. 数组定义的通用结构
任何数组定义均由两部分组成:
- 元素类型:可以是基本类型(int、char)、指针类型(int*)、函数指针类型(int (*)(int))等。
- 数组标识:数组名[元素个数],表示数组的名称和规模。
- 复杂数组示例:
|
int a[4]; // 元素类型为int,4个元素的数组 int b[3][4]; // 元素类型为int[4](一维数组),3个元素的二维数组 int *d[6]; // 元素类型为int*(指针),6个元素的指针数组 int (*e[7])(int, char); // 元素类型为int(*)(int,char)(函数指针),7个元素的函数指针数组 |
2. 解析技巧
- 先确定数组名,再根据优先级判断元素类型:
- int *d[6]:d先与[6]结合(数组),再与*结合(指针)→ 指针数组。
- int (*e[7])(int, char):e先与[7]结合(数组),再与*结合(指针),最后与函数类型结合 → 函数指针数组。
七、注意事项与常见错误
1. 数组越界
- 危害:C 语言不检查数组下标合法性,越界访问会修改未知内存,导致程序崩溃或数据错误。
- 避免:循环遍历数组时,以下标< 数组长度为边界(如for (int i=0; i < len; i++))。
2. 数组作为函数参数
- 退化现象:数组作为函数参数时,会退化为首元素指针,丢失长度信息。
- 示例:
|
void print_arr(int arr[]) { // arr实际为int*类型 printf("sizeof(arr) = %lu\n", sizeof(arr)); // 输出8(64位系统指针大小),而非数组总大小 } |
- 解决:需额外传递数组长度作为参数:
|
void print_arr(int arr[], int len) { for (int i=0; i < len; i++) { printf("%d ", arr[i]); } } |
3. 数组不可直接赋值或比较
- 数组无法用=整体赋值(如int a[5], b[5]; a = b;错误),需逐个元素赋值或用memcpy。
- 数组无法直接用==比较(比较的是地址而非内容),需遍历元素逐个比较或用memcmp。
八、总结
数组是 C 语言中管理连续内存的核心工具,其核心在于连续存储和下标访问。掌握数组与指针的关系(地址表示及步长)、多维数组的内存布局、字符数组与字符串的区别,以及数组作为函数参数的退化特性,是正确使用数组的关键。实际开发中需特别注意数组越界问题,通过宏定义计算长度、传递长度参数等方式提高代码安全性。
3608

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



