5、C 语言数组知识点总结

数组的基本概念、地址与指针关系、赋值引用、多维数组、字符数组及语法解析等方面,补充数组越界、函数传参等实用内容

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 语言中管理连续内存的核心工具,其核心在于连续存储下标访问。掌握数组与指针的关系(地址表示及步长)、多维数组的内存布局、字符数组与字符串的区别,以及数组作为函数参数的退化特性,是正确使用数组的关键。实际开发中需特别注意数组越界问题,通过宏定义计算长度、传递长度参数等方式提高代码安全性。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

長琹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值