解释“空”的解释C数组(int a = {};)

时间:2018-03-01 15:14:07

标签: c arrays visual-studio gcc

我有一段代码片段,它定义了(我相信的)一个空数组,即一个不包含元素的数组:

int a[] = {};

我用gcc编译了代码片段没有问题

试图在MSVS下编译相同代码的同事进行了修改:

int* a = NULL;

不,他显然认为这是MSVS编译器可以接受的等效状态。

然而,稍后在代码中我检索了否。使用以下宏的数组中的元素:

#define sizearray(a)  (sizeof(a) / sizeof((a)[0]))

这样做:

sizearray({}) returns 0

这就像我所期望的那样,我认为是一个空数组的定义

sizearray(NULL) returns 1 

我认为sizeof(NULL)/sizeof((NULL)[0]))实际上是4/4 == 1

NULL == (void*)0

我的问题是:

int a[] = {}; 

是一种表达空数组的有效方式,或者它的编程实践是否很差。

此外,您是否可以在MSVS编译器中使用此类表达式,即这是某种C99兼容性问题吗?

更新:

刚刚编译过:

#include <stdio.h>

#define sizearray(a)  (sizeof(a) / sizeof((a)[0]))

int main()
{
    int a[] = {};
    int b[] = {0};
    int c[] = {0,1};

    printf("sizearray a = %lu\n", sizearray(a));
    printf("sizearray b = %lu\n", sizearray(b));
    printf("sizearray c = %lu\n", sizearray(c));

    return 0;
}

使用此Makefile:

array: array.c
    gcc -g -o array array.c

我的编译器是:

gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.9) 

编译没有任何投诉,输出如下:

bph@marvin:~/projects/scratch/c/array$ ./array
sizearray a = 0
sizearray b = 1
sizearray c = 2
很好奇吗?它可以秘密地成为C ++编译器,而不是C编译器吗?

尝试了John Bodes关于其他编译器标志的建议,并且可以确认编译确实失败了:

gcc --std=c11 --pedantic -Wall -g -o array array.c
array.c: In function ‘main’:
array.c:7:15: warning: ISO C forbids empty initializer braces [-Wpedantic]
     int a[] = {};
               ^
array.c:7:9: error: zero or negative size array ‘a’
     int a[] = {};
         ^
Makefile:2: recipe for target 'array' failed
make: *** [array] Error 1

3 个答案:

答案 0 :(得分:7)

空的初始值设定项在C中无效。所以

int a = {};

是不正确的。请参阅6.7.9 Initialization

sizearray(NULL)也无效。因为sizearray宏会扩展为:

sizeof 0 /sizeof 0[0])

如果NULL定义为0。这是无效的,因为0[0]无效,因为没有涉及指针或数组(根据指针算法的要求 - 请记住a[b]等同于*(a + b) )。

或者,它将扩展为:

(sizeof(((void *)0)) / sizeof((((void *)0))[0]))

如果NULL((void*)0)。这是无效的,因为void指针上不允许指针运算。请参阅6.5.6, 2void*是不完整的类型。无论NULL的定义在实现中是什么,都存在类似的问题(C标准对空指针常量的定义是灵活的,即NULL。参见7.19, 3)。

因此,在这两种情况下,您看到的是非标准代码的编译器特定行为。

答案 1 :(得分:5)

这不是数组,但它也不是标量:这是语法错误。

C11草案在§6.7.9.11(初始化语义学)中说:

  

标量的初始化程序应该是单个表达式,可选   用括号括起来。对象的初始值是   表达(转换后);相同的类型约束和转换   对于简单赋值应用,将标量的类型作为   其声明类型的非限定版本。

但是大括号之间必须有一些东西,它不能是空的。

所以我认为这个问题遗漏了一些,而这不是实际的代码。

答案 2 :(得分:3)

它不是数组,它是大括号初始化语法。

简短的说,你可以这样写:

int a = {1234};

它不会使用数组初始化a,它只会分配1234.如果有2个或更多值,那就是错误。

Brace初始化禁用值截断,因此:

char b = 258; // Valid, same as b = 2
char b = {258}; // Wrong, can't truncate value in braces

空括号只是零初始值,因此int a = {}相当于int a = {0}