我正在查看GNU coretils包的代码,特别是程序'yes',当我在main
函数(第78行)中看到这段代码时:
if (argc <= optind)
{
optind = argc;
argv[argc++] = bad_cast ("y");
}
如何以这种方式扩展数组argv
?显然,只是把任何代码片段脱离上下文是一个非常坏的主意,所以我看了看argv
是否事先被修改,它似乎不是除了在调用initialize_main (&argc, &argv)
,这似乎不需要一个参数为"新大小"或类似的东西(但在C中,像任何语言一样,事情并不总是他们似乎是)。
我决定写一个简单的程序来测试我是否可以在argv
上调用realloc()
char** new_argv = realloc(argv, ++argc * sizeof*argv);
它工作(与Windows 10上的VS2013)。它返回一个指向已分配内存的指针。当然,这并不意味着任何,如果它是未定义的行为。
所以,长话短说,我的问题是,argv
是如何分配的?realloc argv
是否安全?
首先,将argv[argc]
定义为NULL
。
第二,argc++
增加argc
,但返回原来的值。
因此,argv[argc++] = ...
不会调用未定义行为;它只是给先前的NULL
指针赋一个新值。
argv[argc++] = bad_cast ("y");
这不会扩展argv
数组。它只是给argv[argc]
赋值,然后增加argc
的值。这确实打破了argv[argc] == NULL
的初始保证,但只要代码不依赖于它是有效的。
标准保证:
形参
argc
和argv
以及argv数组可以被程序修改,并保留它们最后存储的值程序启动和程序终止之间的值。
没有明确保证argv
所指向的数组中的char*
指针是可修改的,但是可以合理地假设它们是可修改的。(严格来说,argv
指向数组的第一个元素,而不是数组本身,但这句话已经足够长了。)
char** new_argv = realloc(argv, ++argc * sizeof*argv);
这有未定义的行为。realloc
的第一个参数必须是空指针或指向由malloc
、calloc
、realloc
或其他类似对象分配的内存的指针。在进入main
之前,argv
所指向的内存以某种未指定的方式分配。您可以复制数组,但不能合法地释放它,这是realloc
所做的一部分。如果realloc
调用对你来说表现"正确",你只是不走运。(如果你足够幸运的话,你的程序会崩溃,这会告诉你有问题。)