有关数组的学习
数组的概念简介数组是编程中最基础也最常用的数据结构之一理解它能帮你高效管理一组同类型的数据。1. 什么是数组核心概念同类型数组里的所有元素必须是相同的数据类型如全是int或全是float。连续内存数组在内存中是连续存放的这使得访问速度非常快。下标从 0 开始这是新手最容易犯错的地方第一个元素的下标是0最后一个元素的下标是长度 - 1。简单定义数组是一组相同数据类型的元素按固定顺序排列的集合通过索引可以快速访问任意元素。strcpy 是字符串赋值的首选代码简洁且安全。sprintf 功能更强但要注意目标数组长度足够避免溢出。memcpy 最底层适合精确控制内存但需手动保证字符串结束符\0否则可能输出乱码。函数数据类型是否自动加\0典型场景strcpyC 字符串以\0结尾✅ 自动添加字符串赋值、拼接sprintf格式化数据✅ 自动添加数字转字符串、多内容拼接memcpy任意内存块❌ 需手动处理二进制数据、固定长度数组复制2.指针和数组的区别指针和数组在 C 语言中是两个完全不同的概念。虽然它们在很多情况下可以“互换使用”比如访问元素时但它们的本质、内存分配和属性有着根本的区别。我们可以用一个比喻来理解数组就像是一排固定的连体别墅地址是写死在房产证上的你不能把整排房子搬到别处去。指针就像是一个可移动的箭头或路标它可以指向任何地方也可以随时改变指向甚至不指向任何东西空指针。 核心区别对比表特性数组 (Array)指针 (Pointer)本质一块连续内存区域的别名一个存储地址的变量可变性不可变。数组名是常量不能修改指向如a是错的可变。指针变量可以随意修改指向如p是对的内存大小sizeof(数组名) 整个数组占用的字节数sizeof(指针) 指针本身的大小32位系统4字节64位系统8字节定义位置通常在栈上分配固定大小全局数组在数据段可以在栈、堆或数据段指向任何类型的内存操作符a和a[0]值相同但类型不同p是指针变量自己的地址 代码实证为什么它们不一样让我们看一段代码来揭示真相#include stdio.h int main() { int arr[5] {1, 2, 3, 4, 5}; int *ptr arr; // ptr 指向数组的首元素 // 1. 自增操作测试 // arr; // ❌ 编译错误数组名是常量不能移动 ptr; // ✅ 合法指针向后移动了一个 int 的位置 // 2. sizeof 测试 (假设在 64 位系统) printf(sizeof(arr) %lu\n, sizeof(arr)); // 输出 20 (5个int * 4字节) printf(sizeof(ptr) %lu\n, sizeof(ptr)); // 输出 8 (指针本身的大小) // 3. 取地址测试 printf(arr %p\n, (void*)arr); // 整个数组的地址 printf(arr[0] %p\n, (void*)arr[0]); // 首元素的地址 (数值同上) printf(ptr %p\n, (void*)ptr); // 指针变量自己在内存中的地址 (完全不同) return 0; }关键点解析arr报错因为arr不是一个变量它是一个符号常量代表那块内存的起始位置你无法改变它。而ptr是个变量你可以让它指向arr[1]再指向arr[2]。sizeof不同这是最明显的区别。对数组用sizeof得到的是总容量对指针用sizeof得到的只是指针这个变量本身的大小与它指向多大的数组无关。ptr指针变量ptr自己也需要占用内存来存储地址所以ptr是存放“箭头”这个变量的地址而不是它指向的目标地址。 那为什么大家总觉得它们一样因为在表达式求值特别是下标访问时C 语言有一个特殊的规则数组名在大多数表达式中会自动“退化” (decay) 为指向其首元素的指针。当你写arr[i]时编译器实际上把它翻译成了*(arr i)。这时候arr暂时表现得像个指针所以arr[i]等价于ptr[i]*(arr i)等价于*(ptr i)但是这种“退化”只发生在运行时计算地址的时候并没有改变它们定义的本质。 经典陷阱示例// 文件 A: 定义数组 int arr[100]; // 文件 B: 错误地声明为外部指针 extern int *arr; // ❌ 错误类型不匹配如果你在另一个文件中把arr声明为extern int *arr程序链接可能通过但运行时会崩溃。因为编译器会尝试去读取arr所在内存地址里的值作为一个地址再去访问那个地址这完全是两码事。正确的声明应该是extern int arr[];。✅ 总结指针不是数组指针只是一个存地址的变量。数组也不是指针数组是一块连续的内存空间。关系数组名在计算时可以当作指针用但指针永远变不成数组指针不管理内存数组管理自己的内存。数组反转代码示例#include stdio.h int main() { // 1. 初始化数组 int arr[] {1, 2, 3, 4, 5}; int len 5; // 数组长度 printf(反转前: ); for(int i 0; i len; i) { printf(%d , arr[i]); } printf(\n\n); // 2. 核心反转逻辑双指针法 int left 0; // 左指针指向开头 int right len - 1; // 右指针指向末尾 (5-14) int temp; // 临时变量用于交换的“空杯子” // 只要左指针在右指针的左边就继续交换 while (left right) { // --- 开始交换 --- temp arr[left]; // 把左边的值存入临时变量 arr[left] arr[right];// 把右边的值赋给左边 arr[right] temp; // 把临时变量里的值原左边赋给右边 // --- 交换结束 --- // 移动指针向中间靠拢 left; // 左指针向右移 right--; // 右指针向左移 } // 3. 输出结果 printf(反转后: ); for(int i 0; i n; i) { printf(%d , arr[i]); } printf(\n); return 0; }一句话口诀数组是房子里的房间号固定不动指针是手中的地图可以随便换。 为什么 scanf 这么脆弱核心原理 scanf(%d, n) 的工作原理是 它去输入缓冲区看一眼。 如果是数字它吃掉数字存入变量返回 1成功。 关键点如果看到的是非数字比如字母 a它什么都不做 变量 n 不会被修改保持原来的垃圾值。 那个错误的字符 a 依然留在缓冲区里等着下一次读取。 它返回 0表示匹配失败没读到整数。 进阶思考如果想让用户“重试”而不是“退出”既然知道了 scanf 失败后错误字符会留在缓冲区那么想要重试必须手动把那个坏字符清理掉这是一个稍微高级一点的写法实现了“输错了可以重来”#include stdio.h int main() { int arr[10]; int i 0; int temp; // 用来丢弃非法字符 printf(请输入10个整数\n); while (i 10) { printf(请输入第 %d 个数: , i 1); // 尝试读取 if (scanf(%d, arr[i]) 1) { // 成功 i; // 只有成功了才处理下一个数 } else { // 失败 printf(❌ 输入错误那不是整数。\n); // 【关键步骤】清理缓冲区里的垃圾字符 // 一直读字符直到读到换行符 \n 或 文件结束 while ((temp getchar()) ! \n temp ! EOF); printf(请重新输入\n); } } // 后续统计逻辑... int count 0; for (i 0; i 10; i) { if (arr[i] % 2 0) count; } printf(偶数个数%d\n, count); return 0; }
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2461524.html
如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!