目录
掌握数组:从一维到二维的全面指南
在编程的世界里,数组是一种基本而又强大的数据结构,它允许我们以有序的方式存储和操作同类型的数据项。无论是处理简单的列表还是复杂的矩阵,数组都是我们不可或缺的工具。本文将带你从一维数组的基础知识出发,逐步深入到多维数组的高级应用,全面掌握数组的使用。
一维数组基础
一维数组是相同类型元素的线性集合,它们在内存中连续存放。创建一维数组的基本语法如下:
type arr_name[常量值];
例如,创建一个存储20个整数的数组:
int scores[20];
数组的初始化可以在声明时进行,可以完全初始化或不完全初始化。例如:
int arr[5] = {1, 2, 3, 4, 5}; // 完全初始化
int arr2[6] = {1}; // 不完全初始化,其余元素默认为0
一维数组的使用
数组元素的访问通过下标操作符[]实现,下标从0开始。例如,访问数组的第8个元素:数组元素的访问通过下标操作符[]实现,下标从0开始。例如,访问数组的第8个元素:
printf("%d\n", arr[7]);
要遍历整个数组,可以使用循环生成所有下标:
for (int i = 0; i < 20; i++) {
printf("%d ", arr[i]);
}
数组的输入和输出也可以通过循环实现,允许用户输入数据并打印数组内容。
二维数组的创建和初始化
二维数组可以看作是由一维数组组成的数组。创建二维数组的语法如下:
type arr_name[常量值1][常量值2];
例如,创建一个3行5列的整数数组:
int matrix[3][5];
二维数组的初始化与一维数组类似,可以完全初始化或不完全初始化。例如:
int matrix[3][5] = {1, 2, 3, 4, 5, 2, 3, 4, 5, 6, 3, 4, 5, 6, 7};
也可以按行初始化:
int matrix[3][5] = {{1, 2}, {3, 4}, {5, 6}};
二维数组的使用
二维数组的元素访问同样使用下标,行和列的下标都是从0开始。例如,访问第二行第四列的元素:
printf("%d\n", matrix[2][4]);
要遍历整个二维数组,可以使用嵌套循环生成所有行和列的下标。
数组在内存中的存储
数组是编程中常用的数据结构,用于按顺序存储相同类型的多个元素。在C语言中,数组的存储方式是紧凑且连续的,这意味着数组中的每个元素都紧跟着前一个元素,没有间隙。这种存储方式不仅提高了内存的使用效率,还优化了数据的访问速度。
当我们声明一个数组,如
int arr[5] = {10, 20, 30, 40, 50};
编译器会为这五个整数分配一个连续的内存块。每个整数(假设为4字节)紧密排列,第一个整数的地址紧接着前一个整数的地址。这种连续性使得数组的遍历和元素访问变得非常快速,因为CPU可以预读取接下来的数据,减少了内存访问的延迟。
对于多维数组,如二维数组
int matrix[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
其在内存中的存储也是连续的。尽管我们通常将其视为一个行和列的网格,但在内存中,它实际上是三个连续的一维数组。每行作为一个整体连续存储,行与行之间也是连续的。这种存储方式使得二维数组的行迭代非常高效,但列迭代可能涉及更多的缓存未命中,因为同一列的元素在内存中可能不是连续的。
理解数组的内存布局对于程序员来说至关重要,它影响着程序的性能优化和内存使用。通过利用数组的连续存储特性,我们可以编写出更高效、更快速的代码。同时,这种理解也帮助我们避免内存泄漏和其他相关的内存错误。简而言之,数组的紧凑且连续的存储方式是其成为编程中一个强大工具的原因之一。
C99中的变长数组 
C99标准引入了变长数组(Variable-Length Array,VLA),这一特性为C语言的数组声明提供了更大的灵活性。变长数组允许程序员在运行时根据需要动态地指定数组的大小,这在处理不确定数量的数据时非常有用。
变长数组的定义
在C99之前,数组的大小必须在编译时确定,要么通过常量表达式指定,要么在数组初始化时省略大小。变长数组打破了这一限制,允许使用变量来定义数组的大小。例如:
int n = 10;
int arr[n];
在这个例子中,数组 arr
的大小由变量 n
的值决定,这使得数组的大小可以在运行时确定。
变长数组的使用场景
变长数组特别适合用于函数中,其中数组的大小直到运行时才已知。例如,处理用户输入的不确定数量的数据或动态数据结构时,变长数组可以提供极大的便利。
变长数组的限制
尽管变长数组提供了灵活性,但它也有一些限制:
栈内存限制:变长数组通常存储在栈上,这意味着它们的大小受到栈空间大小的限制。如果数组过大,可能会导致栈溢出。
不可初始化:与静态大小的数组不同,变长数组不能在声明时初始化,因为它们的大小在编译时未知。
可移植性问题:虽然C99标准支持变长数组,但在某些编译器(如早期的Microsoft Visual C++)中可能不受支持或支持不完全。
变长数组与动态内存分配
变长数组与动态内存分配(如使用 malloc
)不同。动态内存分配在堆上分配内存,通常提供更大的灵活性和更大的内存空间,但也意味着程序员需要手动管理内存的分配和释放。
#include <stdio.h>
int main() {
int n;
printf("Enter the size of the array: ");
scanf("%d", &n);
int arr[n];
for (int i = 0; i < n; i++) {
arr[i] = i * i;
}
for (int i = 0; i < n; i++) {
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
在这个示例中,用户可以在运行时输入数组的大小,然后程序会根据输入的大小创建数组并初始化。
注意!!!
C99中的变长数组为C语言提供了一种在运行时定义数组大小的方法,这在处理动态数据时非常有用。然而,使用时需要注意其限制,并考虑是否适合特定的应用场景。变长数组是C语言灵活性的一个体现,但也需要谨慎使用以避免潜在的问题。
数组练习
练习1:字符移动
编写代码演示字符从两端向中间汇聚的过程,例如:
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
#include <windows.h>
int main()
{
char arr1[] = "welcome to guilin...";
char arr2[] = "####################";
int left = 0;
int right = strlen(arr1) - 1;
printf("%s\n", arr2);
while (left <= right)
{
Sleep(1000);
arr2[left] = arr1[left];
arr2[right] = arr1[right];
left++;
right--;
printf("%s\n", arr2);
}
return 0;
}
代码通过逐字符替换和打印,模拟打字机效果,逐步显示隐藏在#后的文本。它利用
Sleep
函数实现延时,创建动画效果。代码中,arr1
存储目标文本,arr2
初始化为井号。通过while
循环,left
和right
指针分别从两端向中间替换字符,每次替换后打印当前状态,直到显示完整文本
练习2:⼆分查找
#define _CRT_SECURE_NO_WARNINGS 1
#include <stdio.h>
int main()
{
int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
int left = 0;
int right = sizeof(arr) / sizeof(arr[0]) - 1;
int key = 7;//要找的数字
int mid = 0;//记录中间元素的下标
int find = 0;
while (left <= right)
{
mid = (left + right) / 2;
if (arr[mid] > key)
{
right = mid - 1;
}
else if (arr[mid] < key)
{
left = mid + 1;
}
else
{
find = 1;
break;
}
}
if (1 == find)
printf("找到了,下标是%d\n", mid);
else
printf("找不到\n");
}

注意
- 代码中使用了
sizeof(arr) / sizeof(arr[0])
来计算数组arr
的长度。这是 C 语言中常用的一种计算数组长度的方法。mid
的计算可能在left
和right
很大的时候造成整数溢出,更安全的方式是使用mid = left + (right - left) / 2;
来避免这个问题。
总结
本篇文章全面介绍了数组的使用,从一维到多维数组的创建、初始化、内存存储和操作。探讨了C99的变长数组,并提供了字符移动和二分查找的编程练习,帮助读者深入理解并掌握数组的应用。