C语言数组核心原理与高效应用实践

1. C语言数组的本质与核心价值

数组是C语言中最基础却最强大的数据结构之一,它本质上是一块连续的内存空间,用于存储相同类型的元素集合。这种连续存储特性带来了两个关键优势:一是可以通过下标直接计算出元素的内存地址(地址=基地址+下标×元素大小),实现O(1)时间复杂度的随机访问;二是由于局部性原理,数组遍历时CPU缓存命中率极高。

在实际开发中,数组的应用场景远超初学者想象。从最简单的成绩统计、传感器数据采集,到图像处理中的像素矩阵、游戏开发中的地图网格,再到算法中的哈希表、堆、栈等高级数据结构的底层实现,数组都扮演着核心角色。特别是在嵌入式系统和实时系统中,由于内存受限且对性能要求严苛,数组因其确定的内存占用和高效的访问特性成为首选。

关键理解:数组的"连续内存"特性既是优势也是约束。优势在于访问高效,约束在于大小固定。这也是为什么后续发展出了动态数组、链表等变体结构。

2. 数组的声明与初始化实战技巧

2.1 基础声明方式解析

C语言中数组的标准声明语法为:

数据类型 数组名[元素个数];

例如声明一个包含10个整数的数组:

int scores[10];

但实际工程中,我们更推荐使用宏定义或常量来指定数组大小,避免魔法数字:

#define MAX_STUDENTS 50 int studentScores[MAX_STUDENTS];

2.2 初始化的高级用法

数组初始化有多种形式,每种都有其适用场景:

  1. 完全初始化
int primes[5] = {2, 3, 5, 7, 11};
  1. 部分初始化(剩余元素自动补0)
int arr[10] = {1, 2}; // 后8个元素为0
  1. 自动计算大小
int days[] = {31,28,31,30,31}; // 编译器自动计算为5
  1. 字符数组的特殊性
char str1[] = {'H','e','l','l','o'}; // 长度5 char str2[] = "Hello"; // 长度6(包含'\0')

2.3 多维数组的内存布局

以二维数组为例:

int matrix[3][4] = { {1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12} };

在内存中实际是按行优先顺序连续存储的:

1 2 3 4 5 6 7 8 9 10 11 12

理解这一点对性能优化至关重要。访问数组元素时,应该尽量利用局部性原理,按内存顺序访问(即外层循环行,内层循环列)。

3. 数组与指针的深度关联

3.1 数组名的双重身份

数组名在大多数情况下会退化为指向首元素的指针,但有两个例外:

  1. 使用sizeof(arr)时,返回的是整个数组的字节大小
  2. 使用&arr时,得到的是指向整个数组的指针(类型为int(*)[N]

这种特性导致了许多初学者困惑。例如:

int arr[5]; printf("%p\n", arr); // 类型是int* printf("%p\n", &arr); // 类型是int(*)[5] // 值相同但类型不同

3.2 指针运算遍历数组

以下两种遍历方式完全等价:

// 下标法 for(int i=0; i<5; i++) { printf("%d ", arr[i]); } // 指针法 for(int *p=arr; p<arr+5; p++) { printf("%d ", *p); }

指针法的优势在于某些特定场景下更高效,特别是在处理字符串或硬件寄存器时。

3.3 数组作为函数参数

当数组传递给函数时,实际传递的是指针(首元素地址)。因此以下三种函数声明完全等价:

void func(int *arr); void func(int arr[]); void func(int arr[10]); // 这里的10会被忽略

这也解释了为什么在函数内部无法用sizeof获取数组真实大小,必须额外传递长度参数。

4. 数组的典型应用场景剖析

4.1 实现基础数据结构

栈的实现示例

#define MAX_SIZE 100 typedef struct { int data[MAX_SIZE]; int top; } Stack; void push(Stack *s, int val) { if(s->top >= MAX_SIZE-1) { printf("Stack overflow\n"); return; } s->data[++(s->top)] = val; } int pop(Stack *s) { if(s->top < 0) { printf("Stack underflow\n"); return -1; } return s->data[(s->top)--]; }

4.2 位图(Bitmap)应用

用数组实现位图是空间效率极高的方案:

#define BITSPERWORD 32 #define SHIFT 5 #define MASK 0x1F int bitmap[1 + N/BITSPERWORD]; void set(int i) { bitmap[i>>SHIFT] |= (1<<(i & MASK)); } int test(int i) { return bitmap[i>>SHIFT] & (1<<(i & MASK)); }

这种技术广泛应用于操作系统(页表管理)、数据库(布隆过滤器)、网络(路由表)等领域。

4.3 矩阵运算优化

矩阵乘法的最优实现需要考虑缓存命中率:

// 非优化版本(列优先,缓存不友好) void matmul(int **a, int **b, int **c, int n) { for(int i=0; i<n; i++) for(int j=0; j<n; j++) for(int k=0; k<n; k++) c[i][j] += a[i][k] * b[k][j]; } // 优化版本(分块处理,提高缓存命中) #define BLOCK_SIZE 32 void matmul_opt(int **a, int **b, int **c, int n) { for(int i0=0; i0<n; i0+=BLOCK_SIZE) for(int j0=0; j0<n; j0+=BLOCK_SIZE) for(int k0=0; k0<n; k0+=BLOCK_SIZE) for(int i=i0; i<i0+BLOCK_SIZE; i++) for(int j=j0; j<j0+BLOCK_SIZE; j++) for(int k=k0; k<k0+BLOCK_SIZE; k++) c[i][j] += a[i][k] * b[k][j]; }

5. 数组使用中的陷阱与优化

5.1 常见错误排查表

错误类型示例代码问题分析解决方案
数组越界int arr[5]; arr[5]=1;访问了非法内存严格检查循环条件
大小不匹配int a[3]={1,2,3,4};初始值过多检查初始化列表
未初始化int arr[10]; printf("%d",arr[0]);值不确定显式初始化
指针混淆int *p=arr; p++; arr++;数组名不是左值使用临时指针变量

5.2 性能优化技巧

  1. 循环展开:减少循环控制开销
// 常规循环 for(int i=0; i<100; i++) sum += arr[i]; // 展开4次 for(int i=0; i<100; i+=4) { sum += arr[i]; sum += arr[i+1]; sum += arr[i+2]; sum += arr[i+3]; }
  1. 预取数据:提前加载到缓存
for(int i=0; i<N; i++) { __builtin_prefetch(&arr[i+K]); // GCC内置函数 // 处理arr[i] }
  1. 对齐访问:利用SIMD指令
// 确保数组按16字节对齐 __attribute__((aligned(16))) float vec[100];

5.3 动态数组实现方案

虽然C语言原生不支持动态数组,但可以通过以下方式实现:

  1. malloc方案
int *dynArr = (int*)malloc(size * sizeof(int)); // 使用... free(dynArr);
  1. realloc扩容
dynArr = (int*)realloc(dynArr, newSize * sizeof(int));
  1. 柔性数组成员(C99)
struct dynArray { size_t length; int data[]; // 柔性成员 }; struct dynArray *arr = malloc(sizeof(struct dynArray) + length*sizeof(int));

6. 现代C语言中的数组新特性

6.1 C99变长数组(VLA)

允许使用变量定义数组大小:

void func(int n) { int arr[n]; // VLA // ... }

但需要注意:

  • 不能初始化
  • 栈空间有限,大数组可能溢出
  • 某些嵌入式环境不支持

6.2 复合字面量

直接创建匿名数组:

int *ptr = (int[]){1, 2, 3}; // 复合字面量

这在函数传参时特别有用:

printArray((int[]){1,2,3,4}, 4);

6.3 指定初始化器

C99允许指定元素初始化:

int arr[10] = { [3]=7, [7]=9 }; // 其余为0

对于结构数组尤其有用:

struct point { int x,y; } pts[5] = { [2].y=5, [3].x=8 };

7. 数组在算法竞赛中的妙用

7.1 前缀和数组

快速求解区间和:

int nums[N], prefix[N+1]; // 构建前缀和数组 prefix[0] = 0; for(int i=0; i<N; i++) prefix[i+1] = prefix[i] + nums[i]; // 查询区间[i,j]的和 int sum = prefix[j+1] - prefix[i];

7.2 差分数组

高效处理区间更新:

int diff[N+1]; // 初始全0 // 区间[i,j]增加val void add(int i, int j, int val) { diff[i] += val; if(j+1 < N) diff[j+1] -= val; } // 还原数组 for(int i=0, sum=0; i<N; i++) { sum += diff[i]; nums[i] += sum; }

7.3 树状数组(Fenwick Tree)

高效维护前缀操作:

int tree[N+1]; // 1-based int lowbit(int x) { return x & -x; } void update(int i, int val) { while(i <= N) { tree[i] += val; i += lowbit(i); } } int query(int i) { int res = 0; while(i > 0) { res += tree[i]; i -= lowbit(i); } return res; }

8. 数组与内存管理的深度思考

8.1 栈数组 vs 堆数组

特性栈数组堆数组(malloc)
生命周期所在作用域直到free
大小限制较小(约MB级)受系统内存限制
分配速度极快相对较慢
访问速度略快略慢
适用场景小型临时数组大型或动态数组

8.2 缓存友好编程实践

  1. 访问模式优化
// 差:列优先访问(对C语言不友好) for(int j=0; j<cols; j++) for(int i=0; i<rows; i++) sum += matrix[i][j]; // 好:行优先访问 for(int i=0; i<rows; i++) for(int j=0; j<cols; j++) sum += matrix[i][j];
  1. 结构体数组 vs 数组结构体
// AoS(不利于SIMD) struct { float x,y,z; } points[N]; // SoA(缓存友好) struct { float x[N], y[N], z[N]; } points;

8.3 内存对齐实战

手动对齐示例:

// 16字节对齐数组 #ifdef _MSC_VER __declspec(align(16)) float arr[100]; #else float arr[100] __attribute__((aligned(16))); #endif // 动态分配对齐内存 void *aligned_malloc(size_t size, size_t align) { void *ptr = malloc(size + align - 1 + sizeof(void*)); if(!ptr) return NULL; void *aligned = (void*)(((uintptr_t)ptr + sizeof(void*) + align -1) & ~(align-1)); *((void**)aligned - 1) = ptr; return aligned; } void aligned_free(void *aligned) { free(*((void**)aligned - 1)); }

9. 多维数组的高级应用

9.1 动态多维数组实现

方案1:指针数组

int **matrix = (int**)malloc(rows * sizeof(int*)); for(int i=0; i<rows; i++) matrix[i] = (int*)malloc(cols * sizeof(int));

方案2:连续内存(更高效)

int **matrix = (int**)malloc(rows * sizeof(int*)); matrix[0] = (int*)malloc(rows * cols * sizeof(int)); for(int i=1; i<rows; i++) matrix[i] = matrix[0] + i * cols;

9.2 锯齿数组(Jagged Array)

每行长度不同的数组:

int **jagged = (int**)malloc(rows * sizeof(int*)); for(int i=0; i<rows; i++) jagged[i] = (int*)malloc((i+1) * sizeof(int)); // 第i行有i+1个元素

9.3 数组的数组 vs 一维数组模拟

性能对比:

// 传统二维数组 int arr2d[10][20]; arr2d[i][j] = value; // 一维数组模拟 int arr1d[10*20]; arr1d[i*20 + j] = value; // 更高效但可读性差

10. 数组与其他数据结构的交互

10.1 数组与字符串

C字符串本质是字符数组:

char str1[] = "Hello"; // 自动添加'\0' char str2[10] = "World"; // 剩余补'\0' char *str3 = "Literal"; // 字符串常量(只读)

安全操作建议:

  1. 使用strncpy而非strcpy
  2. 总是检查数组边界
  3. 考虑使用snprintf格式化字符串

10.2 数组与结构体

结构体中的数组:

struct student { char name[20]; int scores[5]; };

数组中的结构体:

struct point { int x,y; }; struct point polygon[10]; // 10个点的多边形

10.3 数组与文件IO

二进制读写数组:

// 写入 float data[100]; FILE *fp = fopen("data.bin", "wb"); fwrite(data, sizeof(float), 100, fp); fclose(fp); // 读取 float newData[100]; fp = fopen("data.bin", "rb"); fread(newData, sizeof(float), 100, fp); fclose(fp);

文本格式存储:

// 写入 for(int i=0; i<100; i++) fprintf(fp, "%f\n", data[i]); // 读取 for(int i=0; i<100 && !feof(fp); i++) fscanf(fp, "%f", &newData[i]);

11. 现代硬件体系下的数组优化

11.1 SIMD指令优化

使用SSE/AVX指令集加速数组运算:

#include <immintrin.h> void add_arrays(float *a, float *b, float *c, int n) { for(int i=0; i<n; i+=8) { __m256 va = _mm256_load_ps(a+i); __m256 vb = _mm256_load_ps(b+i); __m256 vc = _mm256_add_ps(va, vb); _mm256_store_ps(c+i, vc); } }

11.2 多线程并行处理

OpenMP并行化数组处理:

#include <omp.h> void scale_array(float *arr, float factor, int n) { #pragma omp parallel for for(int i=0; i<n; i++) { arr[i] *= factor; } }

11.3 GPU加速方案

使用CUDA进行数组运算:

__global__ void addKernel(float *a, float *b, float *c, int n) { int i = blockIdx.x * blockDim.x + threadIdx.x; if(i < n) c[i] = a[i] + b[i]; } void addArrays(float *a, float *b, float *c, int n) { float *d_a, *d_b, *d_c; cudaMalloc(&d_a, n*sizeof(float)); cudaMalloc(&d_b, n*sizeof(float)); cudaMalloc(&d_c, n*sizeof(float)); cudaMemcpy(d_a, a, n*sizeof(float), cudaMemcpyHostToDevice); cudaMemcpy(d_b, b, n*sizeof(float), cudaMemcpyHostToDevice); addKernel<<<(n+255)/256, 256>>>(d_a, d_b, d_c, n); cudaMemcpy(c, d_c, n*sizeof(float), cudaMemcpyDeviceToHost); cudaFree(d_a); cudaFree(d_b); cudaFree(d_c); }

12. 安全编程与防御性设计

12.1 数组边界检查

安全访问模式:

#define ARRAY_ACCESS(arr, idx, size) \ ((idx) >= 0 && (idx) < (size) ? (arr)[(idx)] : (error_handler(),0)) int safe_access(int *arr, int idx, int size) { if(idx < 0 || idx >= size) { handle_error(); return 0; } return arr[idx]; }

12.2 缓冲区溢出防护

安全字符串处理:

// 不安全 char buf[10]; strcpy(buf, user_input); // 安全替代 strncpy(buf, user_input, sizeof(buf)-1); buf[sizeof(buf)-1] = '\0'; // 更安全的方案 snprintf(buf, sizeof(buf), "%s", user_input);

12.3 防御性编程实践

  1. 输入验证
void process_array(int *arr, int size) { assert(arr != NULL); assert(size > 0 && size <= MAX_SIZE); // ... }
  1. 资源清理
int *arr = malloc(size * sizeof(int)); if(!arr) { perror("malloc failed"); exit(EXIT_FAILURE); } // 使用... free(arr); arr = NULL; // 防止悬空指针
  1. 错误恢复
int save_data(float *data, int size) { FILE *fp = fopen("data.bin", "wb"); if(!fp) return -1; if(fwrite(data, sizeof(float), size, fp) != size) { fclose(fp); remove("data.bin"); return -2; } fclose(fp); return 0; }

13. 调试与性能分析技巧

13.1 数组调试方法

GDB调试数组示例:

gdb ./your_program (gdb) break 42 # 在数组操作处设断点 (gdb) print *arr@10 # 查看前10个元素 (gdb) watch arr[5] # 监视特定元素变化 (gdb) x/20xw arr # 以16进制查看20个字

13.2 Valgrind内存检查

检测数组越界和内存泄漏:

valgrind --tool=memcheck --leak-check=full ./your_program

13.3 性能分析工具

使用perf分析数组访问模式:

perf stat -e cache-misses,cache-references ./your_program perf record ./your_program perf report

13.4 可视化分析

生成火焰图定位热点:

perf record -g ./your_program perf script | stackcollapse-perf.pl | flamegraph.pl > flame.svg

14. 跨平台开发注意事项

14.1 字节序问题

处理网络传输的数组数据:

uint32_t normalize_endian(uint32_t value) { union { uint32_t i; char c[4]; } u = {0x01020304}; if(u.c[0] == 0x01) { // 大端 return ((value >> 24) & 0xff) | ((value >> 8) & 0xff00) | ((value << 8) & 0xff0000) | ((value << 24) & 0xff000000); } return value; // 小端无需转换 }

14.2 内存对齐差异

可移植的对齐分配:

void *aligned_alloc(size_t alignment, size_t size) { #ifdef _WIN32 return _aligned_malloc(size, alignment); #else void *ptr = NULL; posix_memalign(&ptr, alignment, size); return ptr; #endif } void aligned_free(void *ptr) { #ifdef _WIN32 _aligned_free(ptr); #else free(ptr); #endif }

14.3 编译器扩展处理

处理不同编译器的数组扩展:

#ifdef __GNUC__ #define ARRAY_SIZE(arr) (sizeof(arr)/sizeof(arr[0])) #else // 其他编译器的实现 #endif

15. 测试驱动开发实践

15.1 单元测试框架

使用Unity测试数组函数:

#include "unity.h" void test_array_sum(void) { int arr[] = {1, 2, 3, 4, 5}; TEST_ASSERT_EQUAL(15, array_sum(arr, 5)); } void test_array_reverse(void) { int arr[] = {1, 2, 3, 4, 5}; int expected[] = {5, 4, 3, 2, 1}; array_reverse(arr, 5); TEST_ASSERT_EQUAL_INT_ARRAY(expected, arr, 5); } int main() { UNITY_BEGIN(); RUN_TEST(test_array_sum); RUN_TEST(test_array_reverse); return UNITY_END(); }

15.2 边界测试案例

典型边界测试场景:

  1. 空数组
  2. 单元素数组
  3. 已排序数组
  4. 逆序数组
  5. 全相同元素数组
  6. 随机大数组

15.3 性能测试方法

基准测试框架示例:

#include <time.h> void benchmark_array_sort() { const int size = 1000000; int *arr = generate_random_array(size); clock_t start = clock(); sort_array(arr, size); clock_t end = clock(); double elapsed = (double)(end - start) / CLOCKS_PER_SEC; printf("Sorting %d elements took %.3f seconds\n", size, elapsed); free(arr); }

16. 工程实践中的数组应用

16.1 配置管理系统

使用数组存储配置参数:

#define MAX_CONFIG 100 struct config_item { char key[32]; char value[64]; } configs[MAX_CONFIG]; int load_config(const char *filename) { FILE *fp = fopen(filename, "r"); if(!fp) return -1; int count = 0; while(count < MAX_CONFIG && fscanf(fp, "%31[^=]=%63s\n", configs[count].key, configs[count].value) == 2) { count++; } fclose(fp); return count; }

16.2 环形缓冲区实现

高效循环队列:

typedef struct { int *buffer; int capacity; int head; int tail; int count; } ring_buffer; void rb_init(ring_buffer *rb, int capacity) { rb->buffer = malloc(capacity * sizeof(int)); rb->capacity = capacity; rb->head = rb->tail = rb->count = 0; } int rb_push(ring_buffer *rb, int value) { if(rb->count >= rb->capacity) return -1; rb->buffer[rb->tail] = value; rb->tail = (rb->tail + 1) % rb->capacity; rb->count++; return 0; } int rb_pop(ring_buffer *rb) { if(rb->count <= 0) return -1; int value = rb->buffer[rb->head]; rb->head = (rb->head + 1) % rb->capacity; rb->count--; return value; }

16.3 对象池模式

使用数组实现对象池:

#define POOL_SIZE 100 typedef struct { int id; // 其他成员... } object; object pool[POOL_SIZE]; int free_list[POOL_SIZE]; int free_top = 0; void pool_init() { for(int i=0; i<POOL_SIZE; i++) free_list[i] = POOL_SIZE-1 - i; free_top = POOL_SIZE-1; } object *pool_alloc() { if(free_top < 0) return NULL; int idx = free_list[free_top--]; return &pool[idx]; } void pool_free(object *obj) { int idx = obj - pool; if(idx >=0 && idx < POOL_SIZE) free_list[++free_top] = idx; }

17. 从数组到更高级数据结构

17.1 动态数组实现

类似C++ vector的实现:

typedef struct { int *data; int size; int capacity; } dynamic_array; void da_init(dynamic_array *da, int cap) { da->data = malloc(cap * sizeof(int)); da->size = 0; da->capacity = cap; } void da_push_back(dynamic_array *da, int val) { if(da->size >= da->capacity) { da->capacity *= 2; da->data = realloc(da->data, da->capacity * sizeof(int)); } da->data[da->size++] = val; } void da_free(dynamic_array *da) { free(da->data); da->data = NULL; da->size = da->capacity = 0; }

17.2 哈希表基础实现

使用数组+链表:

#define TABLE_SIZE 100 typedef struct node { char *key; int value; struct node *next; } node; node *hash_table[TABLE_SIZE]; unsigned int hash(const char *key) { unsigned int val = 0; while(*key) val = val * 31 + *key++; return val % TABLE_SIZE; } void hash_insert(const char *key, int value) { unsigned int idx = hash(key); node *n = malloc(sizeof(node)); n->key = strdup(key); n->value = value; n->next = hash_table[idx]; hash_table[idx] = n; } int hash_find(const char *key) { unsigned int idx = hash(key); for(node *n = hash_table[idx]; n; n = n->next) { if(strcmp(n->key, key) == 0) return n->value; } return -1; }

17.3 优先队列实现

基于数组的堆:

typedef struct { int *data; int size; int capacity; } priority_queue; void pq_init(priority_queue *pq, int cap) { pq->data = malloc((cap+1) * sizeof(int)); // 索引从1开始 pq->size = 0; pq->capacity = cap; } void pq_swap(priority_queue *pq, int i, int j) { int tmp = pq->data[i]; pq->data[i] = pq->data[j]; pq->data[j] = tmp; } void pq_push(priority_queue *pq, int val) { if(pq->size >= pq->capacity) return; pq->data[++pq->size] = val; for(int i = pq->size; i > 1 && pq->data[i] < pq->data[i/2]; i /= 2) pq_swap(pq, i, i/2); } int pq_pop(priority_queue *pq) { if(pq->size <= 0) return -1; int min = pq->data[1]; pq->data[1] = pq->data[pq->size--]; for(int i = 1, child; i*2 <= pq->size; i = child) { child = i*2; if(child != pq->size && pq->data[child+1] < pq->data[child]) child++; if(pq->data[child] < pq->data[i]) pq_swap(pq, i, child); else break; } return min; }

18. 嵌入式系统中的特殊考量

18.1 内存受限环境优化

  1. 使用位域压缩数据
struct { unsigned int flag1 : 1; unsigned int flag2 : 1; unsigned int value : 6; } packed_data[100];
  1. 共享内存区域
union { uint8_t bytes[64]; uint32_t words[16]; float floats[16]; } shared_mem;

18.2 寄存器映射技术

访问硬件寄存器:

#define GPIO_BASE 0x40020000 typedef struct { volatile uint32_t MODER; volatile uint32_t OTYPER; // 其他寄存器... } GPIO_TypeDef; #define GPIOA ((GPIO_TypeDef *)GPIO_BASE) void gpio_init() { GPIOA->MODER = 0xAB00; // 配置模式寄存器 GPIOA->OTYPER = 0x00; // 推挽输出 }

18.3 静态分配策略

避免动态内存分配:

// 全局静态池 #define MAX_TASKS 10 static struct task task_pool[MAX_TASKS]; static int free_tasks[MAX_TASKS]; static int free_top = MAX_TASKS-1; // 初始化时填充空闲列表 void init_task_pool() { for(int i=0; i<MAX_TASKS; i++) free_tasks[i] = MAX_TASKS-1 - i; } struct task *alloc_task() { if(free_top < 0) return NULL; return &task_pool[free_tasks[free_top--]]; } void free_task(struct task *t) { int idx = t - task_pool; if(idx >=0 && idx < MAX_TASKS) free_tasks[++free_top] = idx; }

19. 代码质量与可维护性

19.1 防御性编程实践

数组操作的健壮性检查:

int safe_array_access(int *arr, size_t size, size_t idx) { if(!arr || idx >= size) { log_error("Invalid array access"); return 0; // 或调用错误处理函数 } return arr[idx]; }

19.2 文档注释规范

Doxygen风格注释示例:

/** * @brief 在有序数组中二分查找 * @param arr 已排序的数组 * @param size 数组大小 * @param target 查找目标值 * @return 目标值索引,未找到返回-1 * @note 数组必须已按升序排序 */ int binary_search(const int *arr, size_t size, int target) { // 实现... }

19.3 单元测试覆盖

测试驱动开发示例:

void test_binary_search() { int arr[] = {1, 3, 5, 7, 9}; TEST_ASSERT_EQUAL(0, binary_search(arr, 5, 1)); TEST_ASSERT_EQUAL(2, binary_search(arr, 5, 5)); TEST_ASSERT_EQUAL(4, binary_search(arr, 5, 9)); TEST_ASSERT_EQUAL(-1, binary_search(arr, 5, 0)); TEST_ASSERT_EQUAL(-1, binary_search(arr, 5, 10)); TEST_ASSERT_EQUAL(-1, binary_search(NULL, 5, 1)); }

20. 未来发展与替代方案

20.1 C++容器对比

C++标准库提供的替代方案:

  1. std::array:固定大小数组包装器
  2. std::vector:动态数组
  3. std::valarray:数值计算专用数组

20.2 其他语言数组特性

现代语言的数组改进:

  1. Python列表:动态类型、自动扩容
  2. Java ArrayList:类型安全、丰富API
  3. Rust Vec:所有权模型保障安全

20.3 自定义智能数组

带边界检查的包装器:

typedef struct { int *data; size_t size; } safe_array; safe_array sa_create(size_t size) { safe_array sa; sa.data = malloc(size * sizeof(int)); sa.size = sa.data ? size : 0; return sa; } int sa_get(safe_array *sa, size_t idx) { if(!sa || !sa->data || idx >= sa->size) { handle_error(); return 0; } return sa->data[idx]; } void sa_free(safe_array *sa) { if(sa) { free(sa