阻止分配给数组的理由是什么?
What's the rationale for preventing assignment to arrays?
我已经尝试 google 这个并且已经阅读:
- Why can't arrays of same type and size be assigned?
- Assigning arrays
- Assign to array in struct in c
但它们都表明了一个显而易见的事实:您不能为数组赋值,因为标准是这样规定的。这很好,但我想知道 为什么 该标准不包括对分配给数组的支持。标准委员会详细讨论了事情,如果他们从未讨论过使数组可分配,我会感到惊讶。假设他们已经讨论过,他们一定有一些理由不让数组分配给。
我的意思是,我们可以将一个数组放入一个结构中并分配给该结构就好了:
struct wrapper
{
int array[2];
};
struct wrapper a = {{1, 2}};
struct wrapper b = {{3, 4}};
a = b; // legal
但是直接使用数组是被禁止的,即使它有效地完成了同样的事情:
int a[2] = {1, 2};
int b[2] = {3, 4};
a = b; // Not legal
标准委员会禁止赋值给数组的理由是什么?
原因基本上是历史性的。甚至在 ISO C89 之前就有一个 C,在 Kernighan 和 Ritchie 之后被称为 "K&R" C。该语言被设计得足够小,因此编译器将适合 64kb 的严重有限(按照今天的标准)内存。
此语言不允许分配数组。如果你想复制相同大小的数组,memcpy
可以满足你的需要。写 memcpy(a, b, sizeof a)
而不是 a = b
当然不是什么大问题。它具有可推广到不同大小的数组和数组切片的额外优势。
有趣的是,您提到的 struct
分配解决方法 在 K&R C 中也不起作用 。您必须一个接一个地分配成员,或者再次使用 memcpy
。 K&R 的C 编程语言 的第一版 提到struct
赋值是该语言未来实现的一项功能。这最终发生在 C89 上。
C 的编写方式是在计算数组表达式时计算第一个元素的地址。
引用 this answer 的摘录:
This is why you can't do something like
int a[N], b[N];
a = b;
because both a
and b
evaluate to pointer values in that context; it's equivalent to writing 3 = 4
. There's nothing in memory that actually stores the address of the first element in the array; the compiler simply computes it during the translation phase.
答案很简单:在委员会介入之前从未被允许(甚至 struct
-assignment 被认为太重),并且考虑到数组衰减,允许它会有各种 有趣后果。
让我们看看会发生什么变化:
int a[3], b[3], *c = b, *d = b;
a = b; // Currently error, would assign elements
a = c; // Currently error, might assign array of 3?
c = a; // Currently pointer assignment with array decay
c = d; // Currently pointer assignemnt
因此,允许数组赋值将使(最多)两个当前不允许的赋值有效。
但这不是问题,问题在于几乎相同的表达式会产生截然不同的结果。
如果您认为函数参数中的数组表示法目前只是指针的另一种表示法,那将变得特别刺激。
如果引入数组赋值,那将变得更加混乱。
并不是说有足够多的人没有像今天这样完全迷惑...
int g(int* x); // Function receiving pointer to int and returning int
int f(int x[3]);// Currently the same. What afterwards? Change to value-copy?
在 C 中,赋值将固定大小对象的内容复制到另一个固定大小对象。对于标量类型(整数、浮点数、指针、自 C99 以来的复杂类型),这是定义明确且实现起来相当简单的。结构的赋值几乎一样简单;较大的可能需要调用 memcpy()
或等价物,但它仍然很简单,因为大小和对齐方式在编译时已知。
数组是另一回事。大多数数组对象的大小直到 运行 时间才确定。一个很好的例子是 argv
。 运行time 环境为每个命令行参数构造一个 char
数组,以及一个包含指向参数指针的 char*
数组。这些通过 argv
、char**
以及 argv
的元素指向的动态分配的 char[]
数组提供给 main
。
C 数组本身就是对象,但它们通常不作为对象访问。相反,它们的元素是通过指针访问的,代码使用指针算法从一个元素遍历到下一个元素。
语言可以设计为将数组视为 first-class 对象,并进行赋值——但这很复杂。作为语言设计者,你必须判断一个包含 10 个整数的数组和一个包含 20 个整数的数组是否是同一类型。如果是,您必须决定当您尝试将一个分配给另一个时会发生什么。它复制较小的尺寸吗?它会导致 运行time 异常吗?是否必须添加 slice 操作才能对数组的子集进行操作?
如果 int[10]
和 int[20]
是没有隐式转换的不同类型,则数组操作不灵活(例如,参见 Pascal)。
所有这些东西都可以定义(参见 Ada),但只能通过定义比 C 中的典型结构更高级的构造。相反,C 的设计者(主要是 Dennis Ritchie)选择为数组提供低级操作.诚然,它有时会带来不便,但它是一个可用于实现任何其他语言的所有高级数组操作的框架。
了解 的目的不是 使数组表达式不可赋值;那不是目标1。相反,这种行为不符合 Ritchie 在编译器中简化数组处理的设计决策,但作为交换,创建了数组表达式 "second-class" 对象;他们在大多数情况下都失去了 "array-ness"。
阅读this paper (especially the section titled "Embryonic C") for some background; I also have a more detailed answer here。
1。除了 Perl 或 PHP2 之外,大多数明显的语言 WTF 通常是设计事故或妥协的结果;大多数语言并不是故意设计成愚蠢的。
2. 我只是在玩一点点; Perl 和 PHP 是一团糟。
也许将问题转过来问问为什么要分配数组(或结构)而不是使用指针会有所帮助?这更清晰和更容易理解(至少如果你已经吸收了 C 的禅宗),并且它的好处是不会隐藏很多工作隐藏在 "simple" 多兆字节分配下的事实数组。
我已经尝试 google 这个并且已经阅读:
- Why can't arrays of same type and size be assigned?
- Assigning arrays
- Assign to array in struct in c
但它们都表明了一个显而易见的事实:您不能为数组赋值,因为标准是这样规定的。这很好,但我想知道 为什么 该标准不包括对分配给数组的支持。标准委员会详细讨论了事情,如果他们从未讨论过使数组可分配,我会感到惊讶。假设他们已经讨论过,他们一定有一些理由不让数组分配给。
我的意思是,我们可以将一个数组放入一个结构中并分配给该结构就好了:
struct wrapper
{
int array[2];
};
struct wrapper a = {{1, 2}};
struct wrapper b = {{3, 4}};
a = b; // legal
但是直接使用数组是被禁止的,即使它有效地完成了同样的事情:
int a[2] = {1, 2};
int b[2] = {3, 4};
a = b; // Not legal
标准委员会禁止赋值给数组的理由是什么?
原因基本上是历史性的。甚至在 ISO C89 之前就有一个 C,在 Kernighan 和 Ritchie 之后被称为 "K&R" C。该语言被设计得足够小,因此编译器将适合 64kb 的严重有限(按照今天的标准)内存。
此语言不允许分配数组。如果你想复制相同大小的数组,memcpy
可以满足你的需要。写 memcpy(a, b, sizeof a)
而不是 a = b
当然不是什么大问题。它具有可推广到不同大小的数组和数组切片的额外优势。
有趣的是,您提到的 struct
分配解决方法 在 K&R C 中也不起作用 。您必须一个接一个地分配成员,或者再次使用 memcpy
。 K&R 的C 编程语言 的第一版 提到struct
赋值是该语言未来实现的一项功能。这最终发生在 C89 上。
C 的编写方式是在计算数组表达式时计算第一个元素的地址。
引用 this answer 的摘录:
This is why you can't do something like
int a[N], b[N]; a = b;
because both
a
andb
evaluate to pointer values in that context; it's equivalent to writing3 = 4
. There's nothing in memory that actually stores the address of the first element in the array; the compiler simply computes it during the translation phase.
答案很简单:在委员会介入之前从未被允许(甚至 struct
-assignment 被认为太重),并且考虑到数组衰减,允许它会有各种 有趣后果。
让我们看看会发生什么变化:
int a[3], b[3], *c = b, *d = b;
a = b; // Currently error, would assign elements
a = c; // Currently error, might assign array of 3?
c = a; // Currently pointer assignment with array decay
c = d; // Currently pointer assignemnt
因此,允许数组赋值将使(最多)两个当前不允许的赋值有效。
但这不是问题,问题在于几乎相同的表达式会产生截然不同的结果。
如果您认为函数参数中的数组表示法目前只是指针的另一种表示法,那将变得特别刺激。
如果引入数组赋值,那将变得更加混乱。
并不是说有足够多的人没有像今天这样完全迷惑...
int g(int* x); // Function receiving pointer to int and returning int
int f(int x[3]);// Currently the same. What afterwards? Change to value-copy?
在 C 中,赋值将固定大小对象的内容复制到另一个固定大小对象。对于标量类型(整数、浮点数、指针、自 C99 以来的复杂类型),这是定义明确且实现起来相当简单的。结构的赋值几乎一样简单;较大的可能需要调用 memcpy()
或等价物,但它仍然很简单,因为大小和对齐方式在编译时已知。
数组是另一回事。大多数数组对象的大小直到 运行 时间才确定。一个很好的例子是 argv
。 运行time 环境为每个命令行参数构造一个 char
数组,以及一个包含指向参数指针的 char*
数组。这些通过 argv
、char**
以及 argv
的元素指向的动态分配的 char[]
数组提供给 main
。
C 数组本身就是对象,但它们通常不作为对象访问。相反,它们的元素是通过指针访问的,代码使用指针算法从一个元素遍历到下一个元素。
语言可以设计为将数组视为 first-class 对象,并进行赋值——但这很复杂。作为语言设计者,你必须判断一个包含 10 个整数的数组和一个包含 20 个整数的数组是否是同一类型。如果是,您必须决定当您尝试将一个分配给另一个时会发生什么。它复制较小的尺寸吗?它会导致 运行time 异常吗?是否必须添加 slice 操作才能对数组的子集进行操作?
如果 int[10]
和 int[20]
是没有隐式转换的不同类型,则数组操作不灵活(例如,参见 Pascal)。
所有这些东西都可以定义(参见 Ada),但只能通过定义比 C 中的典型结构更高级的构造。相反,C 的设计者(主要是 Dennis Ritchie)选择为数组提供低级操作.诚然,它有时会带来不便,但它是一个可用于实现任何其他语言的所有高级数组操作的框架。
了解 的目的不是 使数组表达式不可赋值;那不是目标1。相反,这种行为不符合 Ritchie 在编译器中简化数组处理的设计决策,但作为交换,创建了数组表达式 "second-class" 对象;他们在大多数情况下都失去了 "array-ness"。
阅读this paper (especially the section titled "Embryonic C") for some background; I also have a more detailed answer here。
1。除了 Perl 或 PHP2 之外,大多数明显的语言 WTF 通常是设计事故或妥协的结果;大多数语言并不是故意设计成愚蠢的。
2. 我只是在玩一点点; Perl 和 PHP 是一团糟。
也许将问题转过来问问为什么要分配数组(或结构)而不是使用指针会有所帮助?这更清晰和更容易理解(至少如果你已经吸收了 C 的禅宗),并且它的好处是不会隐藏很多工作隐藏在 "simple" 多兆字节分配下的事实数组。