我知道在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])
的东西放在它前面,看起来像一个强制转换,告诉编译器你打算用大括号{
之间的东西来表示什么样的临时结构......}
.
您可以在此答案中阅读有关数组的"第二类"状态以及无法分配它们的更多信息。