【问题标题】:Why are arrays of undefined size only allowed in main()?为什么只允许在 main() 中使用未定义大小的数组?
【发布时间】:2019-12-20 08:01:36
【问题描述】:

主方法参数使用未定义数组argv[]

int main(int argc, char *argv[])
{
 .... Do stuff....
}

为什么未定义的数组只允许在 main() 方法中使用?

【问题讨论】:

  • 未定义是什么意思?
  • 我看到的唯一数组是由 C 标准定义的,尽管可以使用任何名称。请参见C11中的5.1.2.2.1程序启动
  • @tkausl argv数组没有大小,这怎么可能?

标签: c arrays function implicit-conversion definition


【解决方案1】:

其实这个声明

int main(int argc, char *argv[])

等价于

int main(int argc, char **argv)

主机环境提供字符串数组并将指向数组中第一个字符串的指针作为参数argv 的参数传递。

来自 C 标准(5.1.2.2.1 程序启动)

——如果 argc 的值大于零,则数组成员 argv[0] 通过 argv[argc-1] 包含指向字符串的指针, 由宿主环境赋予实现定义的值 在程序启动之前。目的是为程序提供 在程序启动之前从其他地方确定的信息 托管环境。如果宿主环境不能 提供带有大写和小写字母的字符串, 实现应确保以小写形式接收字符串

至于评论

argv数组没有大小,这怎么可能?

然后将声明为 T 类型数组的函数参数调整为指向 T 的指针。

例如这些函数声明

void f( int a[100] );
void f( int a[10] );
void f( int a[] );

声明同一个函数,所有声明调整为声明

void f( int *a );

与将数组类型的参数传递给函数的方式相同,然后数组会隐式转换为指向其第一个元素的指针。

所以例如上面的函数可以这样调用

int a[100];
f( a );

int a[10];
f( a );

int *a;
f( a );

【讨论】:

  • 是否可以在函数中声明数组没有大小,因为它们会衰减为指针,这就是允许 char *argv[] 的原因吗?
  • 我花了一段时间才明白,但我真的不能感谢你
  • @druper21,不,说可以在函数中声明没有大小的数组是正确的。尽管有很好的区别,但使用数组语法声明的函数参数,无论有无边界,都是指针,而不是数组。这是它们的内在含义,不是自动转换的结果(也称为“衰减”),但它确实与调用者中的数组衰减相匹配。没有 explicit 大小的数组只能在函数内部声明,前提是它们有一个附带的初始化器来隐式地确定它们的大小,或者如果它们有链接。
  • @JohnBollinger 澄清一下“伴随的初始化程序隐式地建立它们的大小,或者它们是否有链接”是什么意思?
  • @duper21,我已经在我自己的回答中更深入地讨论了这个问题,尤其是初始化部分。链接位与在函数内(重新)声明外部变量的很少使用的替代方法有关,方法是在其声明中包含关键字extern。外部变量有时通俗地称为“全局”,但这不是 C 中它们的技术术语,它可以被认为具有实际上在 C 中不成立的含义。
【解决方案2】:

main 在这里没有什么特别之处。诸如char *argv[]int example[] 之类的数组声明具有不完整类型,这在某些情况下有效,但在其他情况下无效。但是,在函数的参数声明中,最终类型不能是数组类型;数组表示法充当指向该类型数组的第一个成员的指针的替代。 mainargv的实际类型是char **

【讨论】:

  • 所以 char *argv[] 自己声明的不能存在,但可以在函数中衰减为 **argv?
【解决方案3】:

为什么未定义的数组只允许在 main() 方法中使用?

“未定义的数组”似乎是指未指定大小的数组。有几种不同的情况需要考虑。

  1. 用数组语法声明的函数参数其实不管有没有指定边界,其实都没有数组类型。相反,这样的参数具有 pointer 类型。没有必要指定一个界限(对于第一个维度),如果指定了一个界限,那么它是无关紧要的。这与从数组到指针的自动转换(“衰减”)相匹配,如果相应的参数是一个数组,调用者会发生这种情况,但是在被调用的函数中,参数是一个指针,一个完整的指针,除了指针什么都没有.

    这适用于任何函数,而不仅仅是main()

  2. 任何变量都可以在文件或块范围内声明为没有显式绑定但带有初始化程序的数组;在这种情况下,数组边界被隐式建立为容纳由初始化程序初始化的所有元素所必需的最小值。

    例子:

    int some_primes[] = { 2, 3, 5, 7, 11 };  // dimension 5
    
  3. 文件范围(在任何函数之外)变量或具有链接的变量可以声明为没有显式边界且没有初始化器的数组隐式建立界限。这种变量的类型是“不完整的”,直到并且除非同一变量的另一个兼容声明通过提供边界来完成它,但是可以在源中它仍然不完整的点上对其执行许多数组操作。

  4. 只有数组类型且没有初始化器的局部变量必须使用显式绑定声明。 (“局部变量”是在块范围内声明且没有链接的变量。)不过,这些是相当常见的情况,所以我认为需要为它们声明大小是问题的一部分灵感。

【讨论】:

  • 如果用数组语法声明的函数参数根本没有,为什么#include <stdio.h>/void foo(char x[printf("Hello, world.\n")]) {}/int main(void) { foo(0); }会打印“Hello, world”。 ?数组类型的某些方面必须存在。 (这是使用 Apple LLVM 10.0.1 和 clang-1001.0.46.4 测试的。)
  • 所有证明,@EricPostpischil,是在调用函数时评估维度表达式。这绝不会使x 成为数组的标识符。
  • 当参数没有数组类型AT ALL时如何计算维度表达式?指针没有维度。如果存在维度表达式,则必须存在数组类型的某些方面。告诉人们参数没有数组类型AT ALL与事实相矛盾。
  • @EricPostpischil,维度表达式是参数声明的一部分。这与在该上下文中通过该语法为参数声明的类型是正交的。使用该语法表达的函数参数声明将参数声明为指针,而不是数组。
  • 没有。有多个步骤。该参数首先被声明为一个数组。随后将其调整为指针。但是,正如所展示的,数组声明并没有完全消失。短语“at all”的意思是“以任何方式”或“在任何程度上/”但很明显一种将数组声明为参数的方式具有持久效果。这意味着它“根本”没有数组类型的陈述是错误的。如果你要用“at all”之类的绝对词来限定陈述,你应该确保它们是真实的。这个不是。
【解决方案4】:

我对这个问题有点困惑。 argc 是传递给可执行文件的命令行参数的数量。 argv 是这些参数的数组。两者都未定义。

argcargv 都是简单的参数名称,您可以将它们更改为您想要的任何名称。按照惯例,它们被简单地称为argcargv

【讨论】:

  • 第二个参数argv没有大小,怎么可能?
  • 因为作为函数参数的数组的外部维度中的元素个数对于编译器来说是不需要知道的。
  • *argv[] 允许在任何函数参数列表中。在这种情况下,它与**argv 相同。
  • @duper21 您的问题暗示您认为它只允许在main 中使用。如果您还有其他意思 - 请编辑问题。
猜你喜欢
  • 2018-08-18
  • 2011-06-04
  • 2013-05-11
  • 1970-01-01
  • 2012-02-24
  • 2020-02-15
  • 2020-04-27
  • 2021-10-31
相关资源
最近更新 更多