如果我在 main 中有一个数组作为参数
int main(int argc, char* argv[])
为什么会
sizeof(argv)/sizeof(argv[0])
总是可靠地给我数组的长度?
它没有。
写这个答案时,我可能有点忘乎所以;对于一个相当简单的问题,这太过分了语言律师的方法。我将添加这个足以回答问题的快速摘要。答案的迂腐和过于冗长的版本位于水平线下方。
鉴于:
int main(int argc, char* argv[])
argv
根本不是一个数组;这是一个指针。(C 和 C++ 不允许数组类型的参数;看起来像数组参数的东西实际上是指针参数。)argv
确实(在运行时)指向数组的第一个元素,但声明中没有关于那个数组有多大。这就是为什么我们需要argc
参数来提供该信息。
所以sizeof(argv)/sizeof(argv[0])
不会给你数组中元素的数量;它只是将指针的大小除以指针的大小,这可能会给你1
. (为什么只“可能”?这是下面过于迂腐的答案的一部分。)
现在,如果您将某些内容明确定义为数组对象:
int an_array[42];
您可以使用该习语来计算数组中的元素数:
sizeof an_array / sizeof an_array[0]
这产生42
, 数组中的元素数。它的工作方式(我认为)相当简单:它是整个数组的字节大小除以其元素之一的字节大小。
但它仅适用于实际数组,不适用于argv
看起来像数组但实际上是指针的东西。
数组和指针之间的关系可能会令人困惑。comp.lang.c FAQ的第 6 节很好地解释了它,并且大部分或全部适用于 C 和 C++。
现在是冗长的迂腐解释,题外话太多:
argv
是一个指针,特别是指向 的指针的指针char
。作为参数声明(并且仅在该上下文中),char *argv[]
等效于char **argv
.
sizeof (argv)
是char**
指针中的字节数。
sizeof (argv[0])
是char*
指针中的字节数。
sizeof (argv) / sizeof (argv[0])
很可能是 1(假设它char*
具有char**
相同的大小,它们在大多数实现中都是这样做的)。
现在对于被定义为数组对象的东西:
some_type an_array[COUNT];
该表达确实有效;这个:
sizeof an_array / sizeof an_array[0]
确实为您提供了 中的元素数量an_array
,即COUNT
. 我认为,其原因应该是相当明显的。数组中的字节数是数组的一个元素中的字节数乘以元素数。所以:
sizeof an_array == sizeof an_array[0] * COUNT
并且,重新排列条款:
sizeof an_array / sizeof an_array[0] == COUNT
顺便说一句,由于索引运算符在 C 中的定义方式,sizeof an_array[0]
也可以写为。sizeof *an_array
(请注意sizeof
,如果该参数是诸如对象名称之类的表达式,则运算符不需要在其参数周围加上括号。操作数sizeof
是表达式或带括号的类型名称。但是,如果您更喜欢始终将括号与 一起使用sizeof
,则可以这样做。)
这是计算数组中元素数量的常用习语——但只有当您拥有数组本身的名称而不是指向其第一个元素的指针时,它才有效。
[以下适用于 C。我相信它也适用于 C++(我最初没有注意到问题被标记为 C++,而不是 C)。]
在回答评论中提出的问题时,否,char*
并且char**
不需要具有相同的大小。C 标准对指针表示的要求是:
指向void的指针应具有与指向字符类型的指针相同的表示和对齐要求。类似地,指向兼容类型的合格或不合格版本的指针应具有相同的表示和对齐要求。所有指向结构类型的指针都应具有彼此相同的表示和对齐要求。所有指向联合类型的指针都应具有彼此相同的表示和对齐要求。指向其他类型的指针不需要具有相同的表示或对齐要求。
参考:N1570,6.2.5p28。
C++ 标准至少有一些这样的;N3485 草案的第 3.9.2 [basic.compound] 节说:
cv 类型的对象应具有与cv
void*
相同的表示和对齐要求。char*
我还没有找到我从 C 标准中引用的其余内容的相应文本。
在字寻址的机器上,char*
指针可能需要更多的信息来指定一个字和该字中的一个字节,而不是指针所需的信息char**
,而指针只需要指定一个对齐的字。