在声明以外的时间初始化 C 数组



我知道在C中我可以执行以下操作。

int test[5] = {1, 2, 3, 4, 5};

现在,这仅在声明数组时才合法。但是我想知道为什么以后这样做是不合法的?但是在程序的后面,执行以下操作是不合法的。

test[5] = {10, 20, 30, 40, 50}; 

或类似的东西。这是为什么呢?我知道这是不合法的,我不是在抱怨,但有人可以给我一个更技术性的解释,为什么我不能这样做吗?(即不要只是说 C 规范不允许它或类似的东西(

我假设它必须与在堆栈上为数组分配内存的时间做一些事情,所以此时 C 可以自动填充我的值,但为什么以后不能这样做呢?

谢谢大家

它不仅仅是数组,除了定义之外,您不能为任何内容提供初始值设定项。人们有时将类似int i; i = 0;的第二条语句称为"初始化i"。事实上,它正在分配给i,它以前持有一个不确定的值,因为它没有被初始化。将其称为"初始化"很少令人困惑,但就语言而言,那里没有初始值设定项。

赋值和初始化对语言来说是分开的,即使它们都使用=字符。数组不可分配。

数组不可分配的原因在其他地方有介绍,例如为什么C++支持结构中数组的成员分配,但一般不支持?简短的回答是,"历史原因"。我认为没有任何杀手级的技术原因可以不更改语言以允许数组分配。

还有一个次要问题,语法上{1, 2, 3, 4, 5}是一个初始值设定项,而不是数组文字,因此即使数组是可分配的,也不能在赋值中使用。我不确定为什么 C89 没有数组文字,可能只是没有人需要它们。C99 引入了一般的"复合文字"语法,特别是数组文字的语法:(int[]) {1, 2, 3, 4, 5} 。您仍然无法从中分配给数组。

关键是使用 int array[]={ } 声明和初始化您创建的对象

声明数组,您实际上可以为数组赋值

int array[5];
array[0] = 1, array[1] = 2, ...

您所做的是为单个数组条目分配多个值:

array[5] = {1,2,3,4,5}; // array[5] can only contain one value

这将是合法的:

array[5] = 6;

希望这是有道理的。只是一个语法问题。

请注意,C99 复合文字允许您"将数组传递给函数":

int your_func(int *test)
{
    ...use test as an array...
}
void other_func(void)
{
    int x = rand();
    if (your_func((int[]){ 0, 1, 2, x, 3, 4 }) > 0 ||
        your_func((int[]){ 9, x, 8, 7, 6, 5 }) > 0)
        ...whatever...
}

这与使用不同值重新初始化数组不同,但它可能足够接近,适合您。

这是解决方案

//Declaration
int a[5];
int a_temp[5] = {1, 2, 3, 4, 5 };
memcpy(a, a_temp, sizeof(a));
//Now array a have values 1, 2, 3, 4, 5

表面上的原因是数组几乎无处不在地转换为指向第一个元素的指针。如果有两个数组

double A[5];
double B[5];

在表达式中,例如

A = B;

两者都已转换为指针。特别是左边的A不是"左值",因此您无法分配给它。

这听起来像是一个蹩脚的借口,但我的猜测是,历史上它就是这样发生的。C(以及与之C++(被困在早期的语法选择中,如果你想与遗留代码保持兼容,可能没有太多的出路。

您希望将数组的定义和初始化分开test 。如果test未定义为const,则可以这样做:您可以为每个数组元素显式赋值:

int test[5];  // definition
...
test[0] = 10;
test[1] = 20;
test[2] = 30;
test[3] = 40;
test[4] = 50;

由于多种原因,您的语法test[5] = {10, 20, 30, 40, 50};不起作用:

  • test[5]是指数组末尾之外的元素
  • {10, 20, 30, 40, 50}只能用作对象定义中的初始值设定项。在其他上下文中,它被解析为块,不能显示为赋值运算符=的右操作数,并且内容无论如何对块无效。

test重新定义为int test[5] = { 10, 20, 30, 40, 50 };也行不通,因为这将定义一个单独的对象test,这在同一作用域中是不允许的(但在内部作用域中是允许的,尽管在实践中令人困惑且容易出错(。

从 C99 开始,您可以定义复合对象并将其用于您的目的:

    memcpy(test, ((int[]){ 10, 20, 30, 40, 50 }), sizeof(test));

(int[]){ 10, 20, 30, 40, 50 } 定义了一个 5 个整数的初始化数组。数组不是左值,因此不能使用赋值运算符 = ,但可以使用 memcpy 。需要额外的()memcpy因为在某些系统上恰好是一个宏,并且与括号()不同,大括号{}不会隐藏宏参数扫描的逗号。

下面是一个示例,其中包含 test 的定义,以及稍后使用 C99 复合文本在单个调用中分配一组不同的值:

#include <stdio.h>
#include <string.h>
void print_array(const char *msg, int *a, size_t len) {
    if (msg)
        printf("%s:", msg);
    for (size_t i = 0; i < len; i++)
        printf(" %d", a[i]);
    printf("n");
}
int main() {
    int test[5] = { 1, 2, 3, 4, 5 };
    print_array("initial values", test, sizeof(test) / sizeof(*test));
    memcpy(test, ((int[]){ 10, 20, 30, 40, 50 }), sizeof(test));
    print_array("new values", test, sizeof(test) / sizeof(*test));
    return 0;
}

输出:

initial values: 1 2 3 4 5
new values: 10 20 30 40 50

这里有几件不同的事情。

一个是,严格来说,初始化赋值有很大不同。 它们看起来非常相似,但它们在"引擎盖下"几乎完全不同(可以这么说(。

然后初始化和赋值之间的一个很大区别是,在初始化中你可以做一些特殊的事情。 最大的一个是你要问的那个:值列表语法{1, 2, 3, 4, 5}(我们将介绍一个特殊的例外(仅在初始值设定项中有效。

还有一个问题是您根本无法分配给数组。 在你做之后

int test[5];

int test[5] = whatever;

您以后无法完成作业

test = anything_else;                                   /* WRONG */

完全。 您不能在该赋值运算符的右侧符号上放置任何内容(即代替" anything_else "(,该运算符将一举为数组的所有元素分配新值test。 数组在 C 语言中是"二等公民",二等状态的一部分是数组无法分配。

特别是,即使你试图做

int test[] = {1, 2, 3, 4, 5};
int newvals[] = {6, 7, 8, 9, 10};
test = newvals;                                         /* WRONG */

这是行不通的。 我刚才尝试过的一个编译器说"数组类型'int [5]'不可分配"。

如果要将值从一个数组复制到另一个数组,可以使用标准库函数 memcpy 来完成。 所以这将起作用:

memcpy(test, newvals, 5 * sizeof(int));

这基本上只是将 5 int 的数据从 newvals 按位复制到 test .

因此,您可能想知道,有没有办法跳过显式newvals数组并执行以下操作

memcpy(test, {6, 7, 8, 9, 10}, 5 * sizeof(int));        /* WRONG */

曾几何时,答案是否定的。 曾几何时,没有办法拥有像{6, 7, 8, 9, 10}这样的"临时数组值"。 正如我之前所说,唯一可以放置值列表语法{6, 7, 8, 9, 10}的地方是在初始化中。 这是因为,在初始化中,您不是在创建"临时数组值",而是为实际数组值提供初始值。

但是,最新版本的 C 确实有一种方法可以做到这一点。 (而且它不再是真正的"最近";我想到现在至少已经有十年了。 它看起来像这样:

memcpy(test, (int [5]){6, 7, 8, 9, 10}, 5 * sizeof(int));

请注意,你不能只说{6, 7, 8, 9, 10};你必须把那个(int [5])的东西放在它前面,看起来像一个强制转换,告诉编译器你打算用大括号{之间的东西来表示什么样的临时结构......} .

您可以在此答案中阅读有关数组的"第二类"状态以及无法分配它们的更多信息。

最新更新