我今天与同事就他(对我来说)不寻常的“主要”功能签名进行了讨论。他喜欢这样声明:
int main(int argc, char* (*argv)[]) {
printf("at index 0: %s\n", (*argv)[0]);
}
虽然我经常写下面的代码:
int main(int argc, char** argv)
{
printf("at index 0: %s\n", argv[0]);
}
有时我写“char * argv []”,以便更清楚地表明argv是一个指向字符的指针数组。
第一个示例中的代码使用警告编译
warning: second argument of ‘main’ should be ‘char **’
但它确实有效。我的问题是为什么代码工作,因为我预期由于解除引用argv导致崩溃。编译器知道函数签名是不同的,并允许(* argv)作为一种“无操作”吗?
试着理解发生了什么,我写了下面的例子,令我惊讶的是它也没有崩溃并打印“第一”(但是编译器发出警告):
#include <stdio.h>
int my_function(int argc, char* (*argv)[])
{
printf("at index 0: %s\n", (*argv)[0]);
}
int main(void)
{
char* stringArr[] = { "frist",
NULL };
size_t stringArrSz = sizeof(stringArr)/sizeof(*stringArr);
return my_function(stringArrSz, stringArr);
}
我原本以为我需要传递“&amp; stringArr”(使用地址运算符)来运行代码。那 - 当然 - 也有效,并修复了编译器警告。
如果我的问题不清楚,请告知我们,非常感谢任何帮助!
答案 0 :(得分:1)
我的问题是为什么代码有效,因为我预计由于解除引用argv而导致崩溃。编译器是否知道函数签名是不同的,并允许(* argv)作为一种“无操作”?
“看似按预期工作”是未定义行为的可能结果之一。
仍然从运行时环境为argv
传递了一个有效的指针值,所以我不希望代码只是访问argv[0]
而崩溃,无论它是如何声明的。
当您尝试访问argv[1]
,argv[2]
等时,事情会变得有趣。虽然C语言本身并不能保证指向不同对象类型的指针具有相同的大小,但在实践中它们适用于大多数现代架构,如x86
;我,sizeof (T *)
== sizeof (T **)
== sizeof ( T *** )
。因此,p + 1
应该为该平台上的每个类型产生相同的字节偏移量。
argv
声明为char *(*)[]
而不是char *[]
或char **
可能会产生一些意外后果。