【问题标题】:Why can i use an array but not a structure as a scalar in C?为什么我可以使用数组而不是结构作为 C 中的标量?
【发布时间】:2014-06-09 14:26:07
【问题描述】:

在 C 中,数组变量可以用作标量。例如:

char A[5];
if(A) printf("True\n");

将打印 True。鉴于:

struct S { int i; } s;
if(s) printf("True\n");

实际上会编译失败,报错:

错误:使用了需要标量的结构类型值

这是为什么?

为什么数组变量可以用作指针而不是结构体 变量?

谢谢。

编辑:也许这是改写问题的更好方法 为什么 C 赋予 A(标量)含义而不赋予 S?

编辑:我的问题本质上更具哲学性。我试图理解为什么 C 是这样设计的。为什么数组会像指针一样起作用,而结构却不会。它们都代表连续的内存块。那个记忆的结构是不同的,但为什么这很重要。

我对编译失败的原因不感兴趣,我理解(我证明它不是我)。

【问题讨论】:

  • if(S) 在您的示例中没有任何意义,这是语法错误。过去的任何讨论都是毫无意义的。
  • @self 但是为什么它没有任何意义。为什么 C 赋予 A 意义而不赋予 S 意义?
  • @captaincurrie 出于同样的原因,ch ar 没有任何意义,而 char 有。
  • @chux S 不是结构,struct S 是。我在示例中没有看到任何 typedef。
  • @captaincurrie 询问if(s) printf("True\n");(小写s)不是更有意义吗?

标签: c arrays pointers structure


【解决方案1】:

为什么数组变量可以用作指针而不是结构变量?

数组名称转换为指向其第一个元素的指针。

C11:6.3,2.1:

...类型为“类型数组”的表达式被转换为类型为“类型指针”的表达式,该类型指向数组对象的初始元素,而不是左值。

结构不是这样的。

【讨论】:

  • @chux;好吧,它是非标准的,但被广泛用于解决这个问题。
  • 您的回答完全改写了我所展示的内容。我想知道为什么语言是这样的。
  • @captaincurrie;我不知道。询问语言设计师:)
【解决方案2】:

还有一件事:struct 是一种聚合类型。指向数组第一个元素的指针是标量类型。

编辑
我认为这可能是重点:

结构是一种聚合数据类型,类似于数组。

然而,数组有一定的规律性,而结构体没有 有。数组是一些东西的数组,都是一样的。 例如。 100 个整数的数组。每个数组成员都是一个 int。你可以索引 您想用任意引用的 100 个整数中的哪一个 表达。这是唯一可能的,因为所有元素都是 大小相同且井然有序。这种观察类似于 声明数组索引是指针运算。
另一方面,结构是一个集合,其中的元素可以 都不同。它们被称为名称,并且没有 索引的非常量表达式的类似物。



来自http://www.cdf.toronto.edu/~ajr/209/notes/struct.html

【讨论】:

  • C 中没有“类”类型。
  • @macfil 感谢您的编辑。但我觉得结构是聚合这一事实并不能回答为什么结构变量不像指针那样工作的问题。
  • @captaincurrie 请重新阅读我帖子中链接中的文章。基本上,数组存储了一堆相同类型的变量。因此,您可以使用指针算法从数组中引用任何变量。事实上A == A[0] == *(A+0)。结构可以保留不同类型的变量,并且“没有类似索引的非常量表达式”。
  • @macfij 好文章!我想也许这就是原因。如果你不能用它做任何事情,为什么还要麻烦将结构变量表示为指针。不过,为什么不呢。允许 struct S *p = s 而不是 struct S *p = &s 你会失去什么?
  • 好吧,如果你有一个指向 struct 的指针,你可以将它作为参数传递给函数并在函数内更改 struct 的内容。附言。如果您认为我帮助了您,您可以将我的回答标记为已接受 :)
【解决方案3】:

如果问题是为什么数组表达式会衰减成指针,而struct类型不会,答案基本如下:

Dennis Ritchie 的 C 设计基于一种称为 B 的早期语言(见图)。在 B 中,当你分配一个包含 N 个元素的数组时,如

auto arr[N];

编译器将分配 N+1 个单元,其中一个将绑定到标识符 arr 并包含数组第一个元素的偏移量,有点像:

           +---+
     arr:  |   | ----+
           +---+     |
            ...      |
           +---+     | 
  arr[0]:  |   | <---+
           +---+
  arr[1]:  |   |
           +---+
  arr[2]:  |   |
           +---+
            ...
           +---+
arr[N-1]:  |   |               
           +---+

Ritchie 最初保留了这些语义,但在将struct 类型引入该语言时遇到了问题。如果他定义了一个类似

的结构
struct foo {
  int inode;
  char name[14];
};

他希望struct foo 的实例有一个两字节的int,紧接着是一个14 字节的char 数组;他不希望数组指针卡在它的中间。最终,他决定完全消除数组指针,并设计了这样的语言:每当它在源代码中看到数组表达式(并且该表达式不是 sizeof 或一元 &amp; 运算符的操作数)时,它就会convert 将该表达式从“T 的 N 元素数组”类型转换为“指向T”的指针,并将其计算为第一个元素的地址。所以当你在 C 中分配一个包含 N 个元素的数组时,你会得到

           +---+      
  arr[0]:  |   | 
           +---+
  arr[1]:  |   |
           +---+
  arr[2]:  |   |
           +---+
            ...
           +---+
arr[N-1]:  |   |               
           +---+

没有单独的arr 对象用于存储第一个元素的偏移量。

这种转换操作是数组独有的,这就是为什么您可以将数组表达式用作标量(从技术上讲,作为指针)。这对于结构来说是不必要的,因为结构变量本身是该类型的一个实例,而不是struct 对象向量的偏移量。

如需了解 C 的为什么,请查看 Ritchie's 的 this essay

【讨论】:

  • 谢谢你:)。这就是我一直在寻找的。您在 B. 中提到的偏移量到底是多少?
  • @captaincurrie:与arr 变量地址的偏移量(字数)(B 是一种“无类型”语言,所有内容都存储在固定长度的字中)。与 C 中一样,B 中的数组访问被定义为 arr[i] 被评估为 *(arr+i);从存储在arr 中的值偏移i 单元格并取消引用结果。 C 保留了这些语义,但没有为arr 预留存储空间;它只是在必要时替换指针值。
【解决方案4】:

C 中,A 的值将是指向数组第一个元素的指针。

因此if(A) printf("True\n"); 表示如果此指针的值不是0,则执行某些操作。这是完全合法的C(尽管指向静态数组的指针永远不会是0)。另一方面,S 是结构的名称,if (S) 没有任何意义。

【讨论】:

  • 我知道一切意味着什么。我想知道为什么!对我来说,数组似乎与数组没有太大不同。为什么在表示上有这种差异。
  • @captaincurrie 结构是一种新类型,而数组是某种类型的实例的集合。另请注意A 是一个实际的对象(数组)。在As 之间进行公平比较是S 的一个实例。编译仍然不会成功,但那是另一回事了。
  • 也许就是这样。结构是一种新类型。看起来数组和结构基本相同。一个包含相同类型的元素,另一个包含变量类型。
  • "在 C 中,A 的值将是指向数组第一个元素的指针。"不是。A 的值是一个数组值,因为变量A 是数组类型。
【解决方案5】:

命名数组的符号是指向数组第一项的常量:A 等价于&amp;A[0]。而且这个指针不是NULL
您使用了该指针而不是 if() 所期望的 int 表达式,因此它被转换为整数常量。指针不是NULL,所以它变成了一个非零整数,因此满足if()条件并调用print

您的struct 与指针相反,没有默认转换为int,因此不能以类似方式使用。

【讨论】:

  • "...所以它被转换为整数常量。" if() 语句需要一个标量,而不是 int。如果代码传递了一个转换为UINT_MAX + 1 的指针,它仍然是真的。
猜你喜欢
  • 2010-09-28
  • 2021-02-23
  • 2021-10-17
  • 1970-01-01
  • 2020-07-31
  • 2015-05-29
  • 2020-11-17
  • 1970-01-01
  • 2016-06-10
相关资源
最近更新 更多