GCC 11.2.0与Apple Clang 13.0.0对VLA参数const修饰的判定谁正确?
GCC与Clang对const修饰变长数组(VLA)参数的兼容性判定差异
测试代码
#include <stdio.h> static void print_matrix(size_t rows, size_t cols, const int data[rows][cols]) { for (size_t r = 0; r < rows; r++) { const char *pad = ""; for (size_t c = 0; c < cols; c++) { printf("%s%3d", pad, data[r][c]); pad = " "; } putchar('\n'); } } int main(void) { /* Created by: gen_matrix -r 3 -c 5 -L 0 -H 999 -n matrix -E -w 3 -S 0x08C777A9 -i */ /* Random seed: 0x08C777A9 */ int matrix[3][5] = { { 984, 843, 464, 599, 17, }, { 876, 173, 647, 61, 387, }, { 138, 245, 718, 981, 629, }, }; enum { MATRIX_ROWS = 3, MATRIX_COLS = 5 }; print_matrix(MATRIX_ROWS, MATRIX_COLS, matrix); return 0; }
编译用Makefile
# Makefile to demonstrate inconsistency between GCC 11.2.0 and Apple Clang 13.0.0. CC = gcc OFLAGS = -O3 GFLAGS = -g WFLAG1 = -Werror WFLAG2 = -Wall WFLAG3 = -Wextra WFLAG4 = -pedantic WFLAG5 = -pedantic-errors UFLAGS = # Set on command line WFLAGS = ${WFLAG1} ${WFLAG2} ${WFLAG3} ${WFLAG4} ${WFLAG5} CFLAGS = ${OFLAGS} ${GFLAGS} ${WFLAGS} ${UFLAGS} PROG1 = gcc23 FILE.c = ${PROG1}.c PROGRAMS = ${PROG1} all: ${PROGRAMS} ${PROG1}: ${CC} ${CFLAGS} ${FILE.c} -o $@
环境中的编译器版本
$ gcc --version gcc (GCC) 11.2.0 Copyright (C) 2021 Free Software Foundation, Inc. This is free software; see the source for copying conditions. There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. $ clang --version Apple clang version 13.0.0 (clang-1300.0.29.30) Target: x86_64-apple-darwin20.6.0 Thread model: posix InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin $
编译结果
$ rm -f gcc23 $ make CC=gcc gcc -O3 -g -Werror -Wall -Wextra -pedantic -pedantic-errors gcc23.c -o gcc23 gcc23.c: In function ‘main’: gcc23.c:29:44: error: pointers to arrays with different qualifiers are incompatible in ISO C [-Wpedantic] 29 | print_matrix(MATRIX_ROWS, MATRIX_COLS, matrix); | ^~~~~~ make: *** [gcc23] Error 1 $ rm -f gcc23 $ make CC=clang clang -O3 -g -Werror -Wall -Wextra -pedantic -pedantic-errors gcc23.c -o gcc23 $ rm -f gcc23 $ make CC=clang UFLAGS=-Weverything clang -O3 -g -Werror -Wall -Wextra -pedantic -pedantic-errors -Weverything gcc23.c -o gcc23 error: include location '/usr/local/include' is unsafe for cross-compilation [-Werror,-Wpoison-system-directories] gcc23.c:3:73: error: variable length array used [-Werror,-Wvla] static void print_matrix(size_t rows, size_t cols, const int data[rows][cols]) ^~~~ gcc23.c:3:67: error: variable length array used [-Werror,-Wvla] static void print_matrix(size_t rows, size_t cols, const int data[rows][cols]) ^~~~ 3 errors generated. make: *** [gcc23] Error 1 $
添加-std=c99、-std=c11或-std=c18选项不改变编译错误情况;Clang启用-Weverything时的错误与const修饰无关,添加-Wno-vla后可正常编译。
核心问题与解答
哪个编译器的判定符合C语言标准?
Clang的行为符合C语言标准,GCC的报错属于过度严格的判定。
原因
- 参数类型调整规则:C标准规定,函数参数中的数组类型会被调整为指向数组首元素的指针。对于
const int data[rows][cols](VLA参数),会被调整为const int (*data)[cols]——即指向cols个const int元素的数组的指针。 - 实参类型退化:主函数中的
matrix是int[3][5]类型,传递给函数时会退化为int (*)[5]——指向5个int元素的数组的指针。 - 限定转换规则:C标准允许将非限定类型的指针转换为指向底层类型兼容的const限定类型的指针。这里
int (*)[5]和const int (*)[5]的底层类型兼容,只是后者指向的数组元素被添加了const限定,属于合法的限定转换(qualification conversion),完全符合标准要求。
GCC的错误在于,它错误地将这种元素层面的const限定判定为指针指向的数组类型的限定不匹配,但实际上标准明确允许这种为元素添加const限定的转换操作。
内容的提问来源于stack exchange,提问作者Jonathan Leffler
相关产品推荐
相关产品推荐

