【问题标题】:Assigning array to an int in C将数组分配给C中的int
【发布时间】:2021-12-14 07:53:01
【问题描述】:

我用 C 编写了以下代码

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

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

【问题讨论】:

  • Build with warnings,编译不会这样。
  • 您应该查看本教程:C-Array 在数组中,如果您想访问其他单元格,您必须指定索引,例如 a[0] = 1 or a[1] = 2 数组总是从 0 开始
  • 您必须使用有效的 C 编译器编译代码。 What compiler options are recommended for beginners learning C?
  • 这样的代码的目的是什么?
  • Re“第一个数组元素的值”:{1, 2, 3} 不是数组。它是大括号内的值列表。该列表可用于初始化数组,但它不是数组。没有书也没有老师告诉你这是一个数组。

标签: arrays c initialization scalar braced-init-list


【解决方案1】:

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

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

C17 6.7.9/2:

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

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

为避免浪费您的时间来解决诸如此类的无效 C 代码问题,请学习 What compiler options are recommended for beginners learning 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;
}

虽然这是一个非标准的编译器扩展,但没有保证或可移植的行为,理解这一点很重要。

【讨论】:

  • 在提供的链接中有一段你说“有一个选项 -pedantic 会针对无效 C 发出警告。-pedantic-errors 是相同的,但会给出错误并阻止代码编译” - 我很久以前通过阅读-pedantic and -pedantic-errors 试图了解是否是这种情况 - 但后来放弃了,从那以后就一直使用它们。 :-) 如果你确定这一点,从现在开始我会放弃-pedantic
  • @TedLyngmo -pedantic 给出 警告-pedantic-errors 给出错误。如果您使用-pedantic-errors,则无需使用-pedantic。他们寻找完全相同的东西。 -pedantic-errors 的优点是您可以使用它并且只会因违反 C 语言而出错。如果你愿意 -pedantic -Werror 你会将 all 警告变成错误,这对 C 初学者有好处,但不适合中级及更高版本,他们可能只对完全错误的东西出错感兴趣。跨度>
  • 谢谢!我只是做了以前没有想到的事情。我检查了 gcc 源。 pedantic-errors Common Var(flag_pedantic_errors) Like -pedantic but issue them as errors. :-)
  • (类似地,在 -std=gnu17 下编写 glibc 或 Linux 内核代码之类的东西的人可能想使用 -pedantic 来提醒自己,因为他们可能故意做非标准的事情。 )
【解决方案2】:

此声明

 int a = {1, 2, 3};

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

来自 C 标准(6.7.9 初始化)

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

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

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

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

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

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

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

【讨论】:

  • 它是否语法无效?严肃的问题。我不确定。
  • 语法没问题。唯一的问题是初始化标量对象时元素过多。
  • @0___________ 这就是我的想法,直到我看到上面的内容,我很确定 Vlad 会为此挖掘一个标准条款 :-) 谢谢 Vlad!
  • @TedLyngmo 不,语法上没问题。语法为 (6.7.9) initializer: { initializer-list }。弗拉德的话有点无关紧要。这是 C17 6.7.9/2 的约束违反“任何初始化程序都不应尝试为未包含在正在初始化的实体中的对象提供值。”
  • @Vlad 随时用我在上面评论中的参考来更新答案,因为从迂腐的角度来看,这不是语法错误,并且您引用了错误的段落。
【解决方案3】:
int main(void)
{
  int a[3] = {1, 2, 3};
  printf("%d\n", a[0]);
  return(0);
}
Output : 1

【讨论】:

  • 请用更多的信息和解释扩展你的答案,所以它不仅仅是一个无法解释的代码块。
  • 我在之前的cmets中已经给出了解释
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-05-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-07-04
  • 2018-02-03
相关资源
最近更新 更多