2

我用C编写了以下代码

int main(){
    int a = {1, 2, 3};
}

似乎分配的变量(在本例中为 a)总是采用第一个数组元素的值。现在我想知道其他数组元素是否被丢弃,或者在a之后写入内存,从而导致缓冲区溢出。

4

3 回答 3

6

本声明

 int a = {1, 2, 3};

在语义上无效(它违反了下面提到的语义规则)。标量对象不能由具有多个初始化器的大括号初始化器列表初始化。

来自 C 标准(6.7.9 初始化)

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

也就是说,花括号初始化器列表中的逗号被视为初始化器的分隔符,对于标量对象,只允许使用单个表达式。

当存在多个初始化器时,编译器假定初始化对象是一个聚合对象。

要声明一个数组,您需要编写

 int a[] = {1, 2, 3};

或者

 int a[N] = {1, 2, 3};

其中N是一个等于或大于 3 的整数值。

于 2021-10-29T08:07:14.017 回答
2

int a = {1, 2, 3};不是有效的 C 代码。

这是 C 标准中所谓的约束违反,之后编译器需要发出诊断消息:

C17 6.7.9/2:

约束
任何初始化器都不应尝试为未包含在正在初始化的实体中的对象提供值。

虽然这不是语法错误,但我们实际上可能会编写奇怪的废话,例如int a = {1};使用单个大括号括起来的初始化程序。无论错误原因如何,结果都是相同的——编译器必须针对所有约束和语法违规发出诊断消息。

为了避免浪费您的时间来解决诸如此类的无效 C 代码,请研究哪些编译器选项推荐给初学者学习 C?


至于像 gcc 和 clang 这样的编译器在面对这种非标准代码时会做什么——它们似乎只是简单地丢弃了多余的初始化程序。如果我使用 gcc/clang for x86 编译此代码并忽略诊断消息:

int foo (void)
{
    int a = {1, 2, 3};
    return a;
}

生成的 x86 程序集是

mov     eax, 1
ret

当翻译回 C 时,它 100% 等同于

int foo (void)
{
    return 1;
}

重要的是要了解这是一个非标准的编译器扩展,并且没有保证或可移植的行为。

于 2021-10-29T09:00:12.167 回答
-1
int main(void)
{
  int a[3] = {1, 2, 3};
  printf("%d\n", a[0]);
  return(0);
}
Output : 1
于 2021-10-29T08:10:26.773 回答