C语言数组

🔥个人主页

🔥相关专栏

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>提供strlenSleep()是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 折半查找完整步骤

  1. 确定当前被查找的数据范围
  2. 确定查找范围的左下标left右下标right
  3. 根据左右下标,计算得到中间元素下标 mid = (left + right) / 2
  4. 将中间位置元素 和 要查找的目标元素进行比较
    • a)相等:找到了目标元素,查找直接结束
    • b)不相等:根据大小关系,缩小范围,确定新的查找区间(折半)
      • 如果目标 > arrmid:目标在右半区间,更新左边界 left = mid + 1
      • 如果目标 < arrmid:目标在左半区间,更新右边界 right = mid - 1
  5. 循环重复上面步骤,直到找到元素,或者区间不存在(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

  1. 初始:left=0,right=9``mid = (0+9)/2 = 4arr[4]=5
    目标7 > 5 → 去右边查找,更新 left = mid+1 = 5
  2. 现在范围 left=5,right=9
    mid = (5+9)/2 =7arr[7]=8
    目标7 < 8 → 去左边查找,更新 right = mid-1 =6
  3. 现在范围 left=5,right=6
    mid = (5+6)/2 =5arr[5]=6
    目标7 > 6 → 去右边查找,更新 left = mid+1 =6
  4. 现在范围 left=6,right=6
    mid=(6+6)/2 =6arr[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 关键注意点

  1. 数组必须有序,无序数组不能直接使用折半查找。

  2. 循环条件:left <= right,不能写成left < right,否则会漏掉left和right重合的那一个元素。

  3. 更新边界:

    • key大于中间值:left = mid + 1
    • key小于中间值:right = mid -1>

    ⚠️不要直接写left=mid、right=mid,会出现死循环。

  4. 返回值约定:一般找到返回对应下标,找不到返回-1(数组下标不会为负数)。

12.5 重要细节

  1. mid = (left + right)/2 存在整数溢出风险 ,当两个数值很大相加会超出int范围。
    • 安全写法:mid = left + (right - left)/2,效果完全一致,不会溢出。
  2. 循环条件必须是 left <= right,不要漏掉等于,否则边界元素查找失败。
  3. 只适用于有序数组,乱序数组二分结果是错误的。

补充小知识点

复制代码
int arr1[10];
// arr1完整类型为 int [10]
// arr2完整类型为 int [12]
int arr2[12];

数组长度是数组类型的一部分,int[10]int[12]属于不同类型,二者不能直接相互赋值。

数组常见易错总结

  1. 下标不要越界,越界属于未定义行为,程序可能崩溃
  2. 数组名大多场景转为首元素地址,但sizeof(数组名)&数组名代表整个数组
  3. 数组不能直接整体赋值,只能循环逐个元素拷贝
  4. 函数接收数组参数实际接收的是指针,丢失数组长度信息

谢谢观看!

相关推荐
女神下凡1 小时前
芯参谋(11): 各种存储芯片电路设计
开发语言·嵌入式硬件
冬夜戏雪1 小时前
笔试的Scanner in Scanner out
java·开发语言
Zane1994121 小时前
HashSet、TreeSet、LinkedHashSet:底层都是“套壳“
java·开发语言
麻花20132 小时前
Win10、win11清理C盘的bat文件内容
开发语言
露天赏雪2 小时前
掘金小册样章
java·开发语言·人工智能·ai编程
一晌小贪欢3 小时前
python-第15天:Python 列表推导式
开发语言·python·excel·数据可视化·python办公
Java后端的Ai之路3 小时前
17、Python - 观察者模式
开发语言·人工智能·python·观察者模式·外观模式
OPEN-F3 小时前
C++并发编程:条件变量与原子操作
java·开发语言·c++
神仙别闹3 小时前
基于 C++实现(控制台)车辆信息管理系统
开发语言·c++