
🔥个人主页
🔥相关专栏

C语言数组
- C语言数组学习笔记
-
- [1. 数组的概念](#1. 数组的概念)
- [2. 一维数组的创建和初始化](#2. 一维数组的创建和初始化)
- [3. 一维数组的使用](#3. 一维数组的使用)
- [4. 一维数组在内存中的存储](#4. 一维数组在内存中的存储)
- [5. sizeof计算数组元素个数](#5. sizeof计算数组元素个数)
- [6. 二维数组的创建](#6. 二维数组的创建)
- [7. 二维数组的初始化](#7. 二维数组的初始化)
-
- [7.1 完全初始化](#7.1 完全初始化)
- [7.2 按行初始化](#7.2 按行初始化)
- [7.3 初始化省略行,**不能省略列**](#7.3 初始化省略行,不能省略列)
- [8. 二维数组的使用](#8. 二维数组的使用)
- [9. 二维数组在内存中的存储](#9. 二维数组在内存中的存储)
- [10. C99变长数组(VLA)](#10. C99变长数组(VLA))
- [11. 数组练习](#11. 数组练习)
- [12. 折半查找(二分查找)](#12. 折半查找(二分查找))
-
- [12.1 折半查找完整步骤](#12.1 折半查找完整步骤)
- [12.2 示例:数组 `{1,2,3,4,5,6,7,8,9,10}`,查找目标 k=7](#12.2 示例:数组
{1,2,3,4,5,6,7,8,9,10},查找目标 k=7) - [12.3 折半查找代码](#12.3 折半查找代码)
- [12.4 关键注意点](#12.4 关键注意点)
- [12.5 重要细节](#12.5 重要细节)
- 补充小知识点
- 数组常见易错总结
C语言数组学习笔记
1. 数组的概念
数组是相同类型元素的集合 ,把多个同类型数据存放在一块连续的内存空间中。
分为一维数组、二维数组(多维数组)。
2. 一维数组的创建和初始化
创建语法
c
// 格式:元素类型 数组名[常量表达式];
int arr[10]; // 创建可以存放10个int的数组
注意:C90要求
[]内必须是编译期常量;C99支持变长数组VLA,可以写变量。
初始化
int arr1[5] = {1,2,3,4,5}; // 完全初始化
int arr2[5] = {1,2}; // 不完全初始化,剩余元素默认初始化为0
int arr3[] = {1,2,3}; // 省略大小,编译器根据初始化内容自动计算数组长度
3. 一维数组的使用
通过下标访问 ,数组下标从0开始。
int arr[5] = {10,20,30,40,50};
arr[0] = 100; // 修改第1个元素
printf("%d", arr[2]); // 读取第3个元素
⚠️不要越界访问,下标范围:
0 ~ 数组长度-1。
4. 一维数组在内存中的存储
一维数组元素在内存中连续存放 ,地址依次递增。
数组名在多数表达式中会隐式转换为数组首元素地址。
数组本身类型包含长度:
int[10]和int[12]是两种完全不同数组类型,不等同于int*指针。
5. sizeof计算数组元素个数
sizeof(数组名)得到整个数组占用总字节数
int arr[10];
int num = sizeof(arr)/sizeof(arr[0]);
// 总字节 / 单个元素字节 = 数组元素个数
⚠️数组名传参给函数后退化为指针,此时
sizeof得到的是指针大小,不能计算元素个数。
6. 二维数组的创建
// 类型 数组名[行数][列数]
int arr[3][5]; // 3行5列的二维数组
二维数组本质上是数组的数组,每一行就是一个一维数组。
7. 二维数组的初始化
7.1 完全初始化
把所有数据全部写在大括号内,按内存顺序填充
int arr3[3][5] = {1,2,3,4,5, 2,3,4,5,6, 3,4,5,6,7};
7.2 按行初始化
内层大括号代表一行,没有赋值的元素自动补0
int arr4[3][5] = {{1,2},{3,4},{5,6}};
/*
1 2 0 0 0
3 4 0 0 0
5 6 0 0 0
*/
7.3 初始化省略行,不能省略列
编译器根据初始化数据自动推导行数,列数必须写死
int arr5[][5] = {1,2,3};
// ✅合法,自动识别为1行5列,剩余元素补0
// int arr6[3][] = { ... }; ❌错误,列不能省略
8. 二维数组的使用
下标格式:数组名[行下标][列下标],行、列下标都从0开始
int arr[3][2] = {{1,2},{3,4},{5,6}};
printf("%d", arr[1][0]); // 取第2行第1列元素:3
9. 二维数组在内存中的存储
二维数组在内存也是连续存储 ,按行存放:先存完第一行全部元素,再存第二行。
可以把二维数组当作一维数组去看待。
10. C99变长数组(VLA)
C90标准不支持数组长度写变量;C99新增变长数组VLA
int n = 5;
int arr[n]; // C99 VLA变长数组,栈上分配空间
- 内存分配在栈上,函数结束自动释放,不需要
malloc sizeof(arr)运行时计算,不再是编译期常量- ⚠️风险:容易栈溢出;MSVC编译器不支持;C23标准已移除VLA
- 工程推荐优先使用
malloc堆动态内存
11. 数组练习
练习1:字符两端向中间汇聚动画
功能说明:字符从字符串两端逐步向中间填充,实现动画效果。
注意:需要头文件
<string.h>提供strlen;Sleep()是Windows平台函数,单位毫秒;Linux使用sleep()。
#include <stdio.h>
#include <string.h>
#include <windows.h>
int main()
{
char arr1[] = "welcome to bit...";
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;
}
运行现象 :一开始全是#,每隔1秒,左右两边字符逐步向中间覆盖。

还可以加上system("cls"),system函数是库函数,执行系统指令,cls是清理控制台屏幕信息,需要包含头文件include <stdlib.h>
12. 折半查找(二分查找)
前提条件:数组必须是有序数组
12.1 折半查找完整步骤
- 确定当前被查找的数据范围
- 确定查找范围的左下标left 、右下标right
- 根据左右下标,计算得到中间元素下标
mid = (left + right) / 2 - 将中间位置元素 和 要查找的目标元素进行比较
- a)相等:找到了目标元素,查找直接结束
- b)不相等:根据大小关系,缩小范围,确定新的查找区间(折半)
- 如果目标 > arrmid:目标在右半区间,更新左边界
left = mid + 1 - 如果目标 < arrmid:目标在左半区间,更新右边界
right = mid - 1
- 如果目标 > arrmid:目标在右半区间,更新左边界
- 循环重复上面步骤,直到找到元素,或者区间不存在(left > right,代表找不到)
12.2 示例:数组 {1,2,3,4,5,6,7,8,9,10},查找目标 k=7
数组下标:0 1 2 3 4 5 6 7 8 9
- 初始:
left=0,right=9``mid = (0+9)/2 = 4,arr[4]=5
目标7 > 5 → 去右边查找,更新left = mid+1 = 5 - 现在范围 left=5,right=9
mid = (5+9)/2 =7,arr[7]=8
目标7 < 8 → 去左边查找,更新right = mid-1 =6 - 现在范围 left=5,right=6
mid = (5+6)/2 =5,arr[5]=6
目标7 > 6 → 去右边查找,更新left = mid+1 =6 - 现在范围 left=6,right=6
mid=(6+6)/2 =6,arr[6]=7,和目标相等,查找成功。
12.3 折半查找代码
#include <stdio.h>
//折半查找函数
//arr:有序数组,sz数组长度,key要查找的数字
int binary_search(int arr[], int sz, int key)
{
int left = 0;
int right = sz - 1;
while(left <= right)
{
int mid = (left + right) / 2;
if(arr[mid] < key)
{
//目标在右边,左边界右移
left = mid + 1;
}
else if(arr[mid] > key)
{
//目标在左边,右边界左移
right = mid - 1;
}
else
{
//找到,返回下标
return mid;
}
}
//循环结束没找到,返回-1
return -1;
}
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,10};
int sz = sizeof(arr)/sizeof(arr[0]);
int k = 7;
int pos = binary_search(arr, sz, k);
if(pos != -1)
{
printf("找到了,下标为:%d\n", pos);
}
else
{
printf("数组中没有该数字\n");
}
return 0;
}
12.4 关键注意点
-
数组必须有序,无序数组不能直接使用折半查找。
-
循环条件:
left <= right,不能写成left < right,否则会漏掉left和right重合的那一个元素。 -
更新边界:
- key大于中间值:
left = mid + 1 - key小于中间值:
right = mid -1>
⚠️不要直接写left=mid、right=mid,会出现死循环。
- key大于中间值:
-
返回值约定:一般找到返回对应下标,找不到返回
-1(数组下标不会为负数)。
12.5 重要细节
mid = (left + right)/2存在整数溢出风险 ,当两个数值很大相加会超出int范围。- 安全写法:
mid = left + (right - left)/2,效果完全一致,不会溢出。
- 安全写法:
- 循环条件必须是
left <= right,不要漏掉等于,否则边界元素查找失败。 - 只适用于有序数组,乱序数组二分结果是错误的。
补充小知识点
int arr1[10];
// arr1完整类型为 int [10]
// arr2完整类型为 int [12]
int arr2[12];
数组长度是数组类型的一部分,
int[10]与int[12]属于不同类型,二者不能直接相互赋值。
数组常见易错总结
- 下标不要越界,越界属于未定义行为,程序可能崩溃
- 数组名大多场景转为首元素地址,但
sizeof(数组名)、&数组名代表整个数组 - 数组不能直接整体赋值,只能循环逐个元素拷贝
- 函数接收数组参数实际接收的是指针,丢失数组长度信息
谢谢观看!
