当前位置: 首页 > news >正文

深入详解C语言数组:承上启下——从C语言数组基础到数据结构衔接


🔥个人主页:艾莉丝努力练剑

❄专栏传送门:《C语言》、《数据结构与算法》、C语言刷题12天IO强训、LeetCode代码强化刷题、洛谷刷题、C/C++基础知识知识强化补充、C/C++干货分享&学习过程记录

🍉学习方向:C/C++方向

⭐️人生格言:为天地立心,为生民立命,为往圣继绝学,为万世开太平

前言:数组是C语言中最基础也是最最重要的数据结构,理解其内存布局和操作特性是学习更复杂数据结构的基础。从一维数组到二维数组,再到变长数组,C语言提供了不同层次的数组支持。虽然数组有局限性,但其简单高效的特性和顺序存储的特点,使其在适合的场景下仍然是首选的数据组织方式。



目录

一、一维数组:最基础的数据容器

1.1  定义与初始化

1.2  内存布局

1.3  数组与指针的关系

二、二维数组:表格型数据存储

2.1  定义与初始化

2.2  内存布局

2.3  访问方式

三、变长数组(VLA):C99引入的灵活特性

3.0  变长数组(VLA)到来前的C语言创建数组BeLike:

3.1  什么是变长数组(VLA)

3.2  变长数组(VLA)的代码演示

3.3  主要的注意事项

3.4  总结一下变长数组(VLA)

四、数组应用:二分查找算法

4.1  算法特性

4.2  代码演示

五、承上启下:衔接C语言数组与数据结构

5.1  顺序表(动态数组)

5.2  栈(数组实现,链表用的比较少)

5.3  顺序存储的二叉树

六、数组的局限性


 C++的两个参考文档:

老朋友(非官方文档):cplusplus

官方文档(同步更新):cppreference


掌握数组:一维数组、二维数组、变长数组及简单的二分查找

一、一维数组:最基础的数据容器

一维数组概念:一维数组是C语言中最简单的数据结构,是一组相同类型元素的线性集合。

1.1  定义与初始化

关键点

(1)数组长度必须是编译期常量(C99标准之前);

(2)数组下标从0开始;

(3)数组名在大多数情况下代表数组首元素的地址。

代码演示:

// 定义方式:类型 数组名[元素个数];
int scores[5]; // 声明一个包含5个整数的数组// 初始化方式
int primes[5] = {2, 3, 5, 7, 11}; // 完全初始化
int evens[10] = {2, 4, 6}; // 部分初始化,剩余元素自动初始化为0
int arr[] = {1, 2, 3}; // 省略长度,编译器自动计算

1.2  内存布局

内存布局:一维数组在内存中是连续存储的,每个元素占用相同大小的内存空间。

例如 int arr[3] 在32位系统(x86环境)中占用连续的12字节(3×4字节)。

1.3  数组与指针的关系

数组与指针关系的重要区别:

sizeof(arr) 返回整个数组的字节大小;

sizeof(p) 返回指针变量的大小(4个字节或8个字节)。

数组与指针的关系相关的代码演示:

int arr[5] = {1, 2, 3, 4, 5};
int *p = arr; // 等价于 &arr[0]// 以下表达式等价
arr[2] == *(arr + 2) == *(p + 2) == p[2]

二、二维数组:表格型数据存储

引言:二维数组可以看作是"数组的数组",常用于表示矩阵或表格数据。

2.1  定义与初始化

初始化代码演示:

// 定义方式:类型 数组名[行数][列数];
int matrix[3][4]; // 3行4列的矩阵// 初始化方式
int grid[2][3] = 
{{1, 2, 3}, // 第一行{4, 5, 6}  // 第二行
};// 也可以连续初始化
int grid2[2][3] = {1, 2, 3, 4, 5, 6};

2.2  内存布局

内存布局:二维数组在内存中仍然是线性连续存储的,按行优先顺序排列。

比如 grid[2][3] 的内存布局为:

grid[0][0], grid[0][1], grid[0][2], grid[1][0], grid[1][1], grid[1][2]

如下图所示:

2.3  访问方式

代码演示:

// 常规访问
int val = grid[1][2]; // 获取第2行第3列元素// 指针方式访问
int val = *(*(grid + 1) + 2); // 等价于grid[1][2]

三、变长数组(VLA):C99引入的灵活特性

3.0  变长数组(VLA)到来前的C语言创建数组BeLike:

        在C99标准之前,C语言在创建数组的时候,数组大小的指定只能使用常量、常量表达式,或者如果我们初始化数据的话,可以省略数组大小。

        这样的语法限制,让我们创建数组就不够灵活,有时候数组大了浪费空间,有时候数组又小了不够用的。 这样能爽吗?不爽!如此受限制,跟这群虫豸语法在一起,怎么能搞好数组呢?

3.1  什么是变长数组(VLA)

概念:变长数组(Variable Length Array)允许使用变量指定数组长度。

3.2  变长数组(VLA)的代码演示

代码演示:

int n;
printf("请输入数组大小:");
scanf("%d", &n);int vla[n]; // 变长数组

3.3  主要的注意事项

这里我们应当注意以下几点:

1、VLA(变长数组)只能是局部变量,不能是全局变量或static变量;

2、VLA(变长数组)不能初始化;

3、内存分配在栈上,大数组可能导致栈溢出;

4、C11标准中VLA(变长数组)变为可选特性

3.4  总结一下变长数组(VLA)

四、数组应用:二分查找算法

二分查找算法:二分查找是数组的经典应用,要求数组必须有序。

4.1  算法特性

二分查找算法的算法特性——

(1)时间复杂度:O(log n)

(2)空间复杂度:O(1)

(3)仅适用于有序数组

4.2  代码演示

既然我们已经知道了二分查找算法的算法特性,就来通过一个实际的例子来感受一下二分查找吧!

int binary_search(int arr[], int size, int target) 
{int left = 0;int right = size - 1;while (left <= right) {int mid = left + (right - left) / 2; // 防止溢出if (arr[mid] == target) {return mid;}     else if (arr[mid] < target) {left = mid + 1;} else {right = mid - 1;}}return -1; // 未找到
}

五、承上启下:衔接C语言数组与数据结构

5.1  顺序表(动态数组)

顺序表是数组的扩展,可以动态增长——

typedef struct {int *data;     // 指向动态数组的指针int capacity;  // 当前容量int size;      // 当前元素数量
} SeqList;void init(SeqList *list, int cap) {list->data = (int *)malloc(cap * sizeof(int));list->capacity = cap;list->size = 0;
}void append(SeqList *list, int value) {if (list->size >= list->capacity) {// 扩容操作list->capacity *= 2;list->data = (int *)realloc(list->data, list->capacity * sizeof(int));}list->data[list->size++] = value;
}

5.2  栈(数组实现,链表用的比较少)

栈的数组实现利用数组的尾部作为栈顶:

#define MAX_SIZE 100typedef struct {int data[MAX_SIZE];int top; // 栈顶指针
} ArrayStack;void push(ArrayStack *stack, int value) {if (stack->top < MAX_SIZE - 1) {stack->data[++stack->top] = value;}
}int pop(ArrayStack *stack) {if (stack->top >= 0) {return stack->data[stack->top--];}return -1; // 栈空
}

5.3  顺序存储的二叉树

完全二叉树可以用数组顺序存储——

// 对于索引为i的节点:
// 父节点:(i-1)/2
// 左孩子:2*i + 1
// 右孩子:2*i + 2int tree[7] = {1, 2, 3, 4, 5, 6, 7}; // 表示一个完全二叉树

六、数组的局限性

数组不可能是万能的,它主要存在以下几点不足——

(1)固定大小:传统数组大小在编译时确定(VLA【变长数组】除外);

(2)插入删除效率低:平均需要移动O(n)个元素;

(3)内存浪费或不足:静态分配难以精确控制。

正是这些局限性促使我们学习更高级的一些数据结构,比如链表、树等。


往期回顾:

深入详解C语言的循环结构:while循环、do-while循环、for循环,结合实例,讲透C语言的循环结构

掌握数组:一维数组、二维数组、变长数组及简单的二分查找

结语:本文内容到这里就全部结束了,非常感谢大家的阅读!不要忘记给博主“一键四连”哦!

http://www.lryc.cn/news/620477.html

相关文章:

  • 抓取系统升级,是优化还是重构更合适?
  • CSS aspect-ratio 属性
  • RTC时钟倒计时数码管同步显示实现(STC8)
  • 【基于个人博客系统】---测试报告
  • 当GitHub宕机时,我们如何协作?
  • GO学习记录五——数据库表的增删改查
  • 手写MyBatis第16弹:泛型魔法应用:MyBatis如何破解List的运行时类型
  • C++ 应用场景全景解析:从系统级到AI的跨越式演进
  • 分布式系统架构设计模式:从微服务到云原生
  • 河南萌新联赛2025第(五)场:信息工程大学”(补题)
  • DataHub OPC Gateway:实现OPC UA与OPC DA无缝集成的高性能网关
  • iOS App TF上架全流程实战 高效内测分发与IPA包管理
  • Boost库中Pool 基础内存池(boost::pool<>)的详细用法解析和实战应用
  • Docker 核心技术:Namespace
  • 版本更新!FairGuard-Mac加固工具已上线!
  • 银河麒麟系统部署oceanbase社区版
  • 【入门级-C++程序设计:13、STL 模板:栈(stack)、队 列(queue)、 链 表(list)、 向 量(vector) 等容器】
  • 中介者模式和观察者模式的区别是什么
  • mysql——count(*)、count(1)和count(字段)谁更快?有什么区别?
  • 【React】hooks 中的闭包陷阱
  • 某处卖600的【独角仙】尾盘十分钟短线 尾盘短线思路 手机电脑通用无未来函数
  • coze小白-如何用coze上传本地文件?(对话流使用)
  • 《SeeClick: Harnessing GUI Grounding for Advanced Visual GUI Agents》论文精读笔记
  • 云原生俱乐部-k8s知识点归纳(1)
  • 同创永益 IStorM CNBR云原生业务韧性管理平台 v3.3.0重磅发布:告别备份烦恼,云原生数据保护再升级!
  • 【博客系统测试报告】---接口自动化测试
  • toRefs、storeToRefs实际应用
  • 图书商城小程序怎么做?实体书店如何在微信小程序上卖书?
  • 机器学习 - Kaggle项目实践(3)Digit Recognizer 手写数字识别
  • 20道HTML相关前端面试题及答案