在if语句内分配不同类型数组的C语言最佳实践
C语言中按条件分配不同类型数组的最佳实践
背景与问题
最近对CUDA产生了兴趣,感觉C程序转CUDA比Fortran更自然(除核心问题外,这个观点是否属实?),学习C时发现if语句的作用域规则和Fortran差异很大。
比如下面的Fortran代码可以正常运行并输出预期结果:
program test real(kind=selected_real_kind(15)), allocatable :: arr_double(:) integer , allocatable :: arr_int(:) logical :: flag_int, flag_double flag_int = .false. flag_double = .true. if (flag_int) then allocate(arr_int(10)) arr_int = 1 print *, arr_int else if (flag_double) then allocate(arr_double(10)) arr_double = 1. print *, arr_double end if if (flag_int) then deallocate(arr_int) else if (flag_double) then deallocate(arr_double) end if end program
输出结果:
1.00000000000000 1.00000000000000 1.00000000000000 1.00000000000000 1.00000000000000 1.00000000000000 1.00000000000000 1.00000000000000 1.00000000000000 1.00000000000000
但对应的C代码(icc编译)会直接报错:
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> int main() { size_t dim1; bool flag_int, flag_double; flag_int = false; flag_double = true; if (flag_int) { int (*arr1d) = calloc(dim1, sizeof(*arr1d)); }else if(flag_double) { double (*arr1d) = calloc(dim1, sizeof(*arr1d)); } free(arr1d); }
编译失败的原因是C语言中if语句块内声明的变量仅具有局部作用域,无法在块外访问。那么如果要根据if条件分配不同类型(int或double)的数组,C语言的最佳实践是什么?
注:该问题仅为学习C时的练习,不会用于实际工作。
关于C转CUDA更自然的补充
这个观点确实有合理性:CUDA本身是基于C/C的扩展,语法和C高度兼容,指针操作、线程索引计算等CUDA核心操作和C的编程习惯契合度很高;而Fortran转CUDA需要适配Fortran的数组模型和CUDA的C风格接口,虽然有CUDA Fortran支持,但生态和资料量远不如C/C版本的CUDA,从学习和上手角度来看,C转CUDA确实更顺畅。
解决方案
方法1:使用void指针+类型标记
利用C中void*可以指向任意类型的特性,用通用指针存储数组地址,同时用一个标记变量记录数组类型,后续操作时通过类型转换访问对应数据。
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> int main() { size_t dim1 = 10; // 必须先初始化,避免未定义行为 bool flag_int = false; bool flag_double = true; void* arr1d = NULL; enum { TYPE_INT, TYPE_DOUBLE } arr_type; if (flag_int) { arr1d = calloc(dim1, sizeof(int)); arr_type = TYPE_INT; // 赋值并打印 int* int_arr = (int*)arr1d; for (size_t i = 0; i < dim1; i++) { int_arr[i] = 1; } for (size_t i = 0; i < dim1; i++) { printf("%d ", int_arr[i]); } printf("\n"); } else if (flag_double) { arr1d = calloc(dim1, sizeof(double)); arr_type = TYPE_DOUBLE; // 赋值并打印 double* double_arr = (double*)arr1d; for (size_t i = 0; i < dim1; i++) { double_arr[i] = 1.0; } for (size_t i = 0; i < dim1; i++) { printf("%.15lf ", double_arr[i]); } printf("\n"); } // 释放内存,free不关心指针类型 if (arr1d != NULL) { free(arr1d); } return 0; }
方法2:提前声明所有可能的指针
和你提供的Fortran代码逻辑最接近,提前声明不同类型的指针,在对应分支中分配内存,释放时根据相同条件判断要释放的指针。
#include <stdio.h> #include <stdlib.h> #include <stdbool.h> int main() { size_t dim1 = 10; bool flag_int = false; bool flag_double = true; int* arr_int = NULL; double* arr_double = NULL; if (flag_int) { arr_int = calloc(dim1, sizeof(int)); for (size_t i = 0; i < dim1; i++) { arr_int[i] = 1; } for (size_t i = 0; i < dim1; i++) { printf("%d ", arr_int[i]); } printf("\n"); } else if (flag_double) { arr_double = calloc(dim1, sizeof(double)); for (size_t i = 0; i < dim1; i++) { arr_double[i] = 1.0; } for (size_t i = 0; i < dim1; i++) { printf("%.15lf ", arr_double[i]); } printf("\n"); } // 根据条件释放对应指针 if (flag_int) { free(arr_int); } else if (flag_double) { free(arr_double); } return 0; }
额外注意事项
- 原C代码中的
dim1未初始化,会导致calloc的大小不确定,属于未定义行为,实际编写时必须先给变量赋值。 - C的块级作用域是语言核心特性:变量在
{}内声明时,仅能在该块及嵌套块中访问,这和Fortran的作用域规则完全不同(Fortran中程序单元内声明的变量,整个单元都可访问)。
内容的提问来源于stack exchange,提问作者Sangjun Lee
相关产品推荐
相关产品推荐

