飞翔飞翔
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
主页
  • 计算机基础

    • TCP/IP协议
    • Linux命令
  • 数据库

    • SQL教程
  • 编程语言

    • C语言
    • Python2
    • Python3
  • 数据格式

    • JSON教程
  • 工具

    • Markdown指南
  • Git

    • GitFlow
  • Quartz

    • Quartz教程
  • Java

    • Java设计模式
  • 缓存

    • Redis教程
联系
阿里云
  • 学习路径
  • 第1章 编程基础概念

    • 冯·诺依曼体系结构
    • 数据在计算机中的表示
    • 编程语言的层次
    • C语言的起源与发展
    • C99标准的主要改进
    • 开发环境搭建
    • 第一个C程序
    • 编译与运行流程
    • 可移植性风险的三级体系
  • 第2章 数据类型与运算

    • 字符集与标识符
    • 关键字
    • 注释
    • char 类型
    • short 与 int
    • long 与 long long
    • 有符号与无符号
    • 取值范围与 limits.h
    • float 与 double
    • long double
    • _Bool 类型
    • 变量声明与定义
    • 常量
    • 转义序列
    • 算术运算符
    • 赋值运算符
    • 自增自减运算符
    • 关系与判等运算符
    • 逻辑运算符
    • 位运算符
    • 条件运算符
    • 逗号运算符
    • 运算符优先级
    • 隐式类型转换
    • 显式类型转换
  • 第3章 控制流

    • 表达式语句与空语句
    • 复合语句
    • if 语句
    • switch 语句
    • while 循环
    • do-while 循环
    • for 循环
    • break 与 continue
    • goto 语句
    • return 语句
  • 第4章 函数与模块化编程

    • 函数定义
    • 函数声明与原型
    • main 函数
    • 函数调用机制
    • 传值调用
    • 数组参数
    • 作用域
    • 存储期
    • 链接属性
    • static 与 extern
    • 递归
    • 头文件与源文件
    • 头文件保护
    • include 规则
  • 第5章 数组与字符串

    • 一维数组声明与初始化
    • 数组的存储模型
    • 数组访问与越界
    • 数组操作
    • 二维数组
    • 变长数组 VLA
    • 字符串基础
    • 字符串输入输出
    • 字符串处理函数
    • 字符串与数字转换
  • 第6章 指针

    • 指针的概念
    • 指针的声明与使用
    • 指针运算
    • const 与指针
    • 数组名与指针
    • 指针遍历数组
    • 指针与多维数组
    • 指针作为函数参数
    • 函数返回指针
    • 函数指针
    • 二级指针
    • 复杂声明解析
  • 第7章 结构体、联合体与枚举

    • 结构体定义与声明
    • 结构体初始化
    • 结构体成员访问
    • 结构体嵌套
    • 结构体指针
    • 结构体与函数
    • 联合体
    • 联合体与类型双关
    • 枚举类型
    • 位域
    • 内存对齐与填充
  • 第8章 动态内存管理

    • malloc 与 free
    • calloc 与 realloc
    • 内存泄漏
    • 悬垂指针
    • 内存分配策略
    • 自定义内存池
    • Valgrind 与内存检测
    • 内存碎片
    • 内存对齐分配
    • 常见内存错误
  • 第9章 文件输入输出

    • 文件打开与关闭
    • 文本读写
    • 格式化输入输出
    • 二进制读写
    • 文件定位
    • 错误处理
    • 标准流
    • 临时文件
    • 文件操作示例
  • 第10章 预处理器

    • 预处理器基础
    • 宏定义
    • 带参数的宏
    • 条件编译
    • 头文件包含
    • 预定义宏
    • 宏的高级技巧
    • 预处理器陷阱
    • 编译器特定扩展
  • 第11章 标准库概览

    • 标准库概述
    • assert.h
    • ctype.h
    • errno.h
    • float.h
    • limits.h
    • locale.h
    • math.h
    • setjmp.h
    • signal.h
    • stdarg.h
    • stddef.h
    • stdlib.h
  • 第12章 进阶主题

    • 内联函数
    • 变长数组 VLA
    • 复数类型
    • 布尔类型
    • stdint 与 inttypes
    • 灵活数组成员
    • 匿名结构体与联合体
    • 静态断言
    • 线程支持
    • 原子操作

数组操作

数组是 C 语言中最基础的数据结构,掌握常见的数组操作——遍历、查找、排序、反转、复制——是编写高效程序的基础。这些操作也是面试和算法竞赛中的常客,理解它们的实现原理有助于选择合适的数据结构和算法。

遍历

int arr[] = {1, 2, 3, 4, 5};
int n = sizeof(arr) / sizeof(arr[0]);

/* 下标遍历 */
for (int i = 0; i < n; i++)
    printf("%d ", arr[i]);

/* 指针遍历 */
for (int *p = arr; p < arr + n; p++)
    printf("%d ", *p);

查找

线性查找:

int linear_search(const int arr[], int n, int target)
{
    for (int i = 0; i < n; i++)
        if (arr[i] == target)
            return i;           /* 找到,返回索引 */
    return -1;                  /* 未找到 */
}

时间复杂度 O(n)。

二分查找(要求数组有序):

int binary_search(const int arr[], int n, int target)
{
    int left = 0, right = n - 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;
}

时间复杂度 O(log n)。

排序

冒泡排序:

void bubble_sort(int arr[], int n)
{
    for (int i = 0; i < n - 1; i++) {
        for (int j = 0; j < n - 1 - i; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

时间复杂度 O(n²)。

快速排序(使用标准库):

#include <stdlib.h>

/* 比较函数 */
int compare_int(const void *a, const void *b)
{
    return (*(int *)a - *(int *)b);
}

int arr[] = {3, 1, 4, 1, 5, 9, 2, 6};
qsort(arr, sizeof(arr) / sizeof(arr[0]), sizeof(int), compare_int);

qsort 是 C 标准库提供的快速排序实现。

反转

void reverse(int arr[], int n)
{
    for (int i = 0; i < n / 2; i++) {
        int temp = arr[i];
        arr[i] = arr[n - 1 - i];
        arr[n - 1 - i] = temp;
    }
}

复制

#include <string.h>

/* 方法 1:循环 */
for (int i = 0; i < n; i++)
    dest[i] = src[i];

/* 方法 2:memcpy */
memcpy(dest, src, n * sizeof(int));

/* 方法 3:memmove(源和目的重叠时安全) */
memmove(dest, src, n * sizeof(int));

memcpy 在源和目的重叠时行为未定义,memmove 可以安全处理重叠。

插入和删除

数组的插入和删除需要移动元素,效率较低:

/* 在位置 pos 插入 value */
void insert(int arr[], int *n, int capacity, int pos, int value)
{
    if (*n >= capacity || pos < 0 || pos > *n)
        return;                 /* 错误处理 */
    
    for (int i = *n; i > pos; i--)
        arr[i] = arr[i - 1];    /* 后移元素 */
    
    arr[pos] = value;
    (*n)++;
}

/* 删除位置 pos 的元素 */
void delete(int arr[], int *n, int pos)
{
    if (pos < 0 || pos >= *n)
        return;
    
    for (int i = pos; i < *n - 1; i++)
        arr[i] = arr[i + 1];    /* 前移元素 */
    
    (*n)--;
}

时间复杂度 O(n)。频繁插入删除的场景应考虑链表。

最大值和最小值

int find_max(const int arr[], int n)
{
    if (n <= 0) return 0;       /* 错误处理 */
    
    int max = arr[0];
    for (int i = 1; i < n; i++)
        if (arr[i] > max)
            max = arr[i];
    return max;
}

int find_min(const int arr[], int n)
{
    if (n <= 0) return 0;
    
    int min = arr[0];
    for (int i = 1; i < n; i++)
        if (arr[i] < min)
            min = arr[i];
    return min;
}

常见错误

数组大小计算错误:

void func(int arr[])
{
    int n = sizeof(arr) / sizeof(arr[0]);   /* 错误! */
}

排序比较函数错误:

/* 错误:返回差值可能溢出 */
int compare(const void *a, const void *b)
{
    return *(int *)a - *(int *)b;           /* 溢出! */
}

/* 正确 */
int compare(const void *a, const void *b)
{
    int x = *(int *)a;
    int y = *(int *)b;
    if (x < y) return -1;
    if (x > y) return 1;
    return 0;
}

memcpy 重叠:

int arr[] = {1, 2, 3, 4, 5};
memcpy(arr + 2, arr, 3 * sizeof(int));      /* 未定义行为:重叠! */

/* 正确 */
memmove(arr + 2, arr, 3 * sizeof(int));     /* 安全 */

最佳实践

  • 数组操作始终知道数组大小
  • 排序使用 qsort,不要自己实现简单排序(除非学习目的)
  • 复制用 memcpy(不重叠)或 memmove(可能重叠)
  • 频繁插入删除考虑链表或动态数组
  • 查找有序数组用二分查找
  • 始终处理空数组和越界情况
上一页
数组访问与越界
下一页
二维数组