执行此代码后:
char **argv_p;
char *argv[20] = { NULL };
memcpy(&argv_p, &argv, sizeof(argv_p));
argv_p
最终为NULL。
这是因为&argv被分解成指向argv数组中第一个指针的指针。使用&argv[0]
产生相同的结果(如预期)。
问题是,C中是否有语法允许将char ***
传递给memcpy,而无需诉诸诸如
char **argv_p;
char *argv[20] = { NULL }, **argv_start = argv;
memcpy(&argv_p, &argv_start, sizeof(argv_p));
我已经测试并产生了正确的结果。
编辑:我知道你可以直接赋值,但这是为了解决const问题。如果有人知道为什么execve
使用char * const *
而不是char const **
作为第二个参数,则可以获得额外的互联网积分。
编辑编辑:为了澄清,const之间的区别:
char * const *
-使数组的内容不可变char const **
-使数组所指向的字符串缓冲区的内容不可变。
Const总是构成左边的东西,除非它先出现(ANSI C的家伙需要射击),在这种情况下,它构成右边的东西。虽然很多人写const char *
,但有些人认为写char const *
是最佳实践,因为这样const的应用是一致的。
你不能在不收到C编译器警告的情况下抛出const。Memcpy是唯一可以在没有警告的情况下解决这个问题的方法。
许多旧的库没有正确地将函数的参数标记为const,如果你在代码中正确地对类型应用了const,编译器将发出警告。这就是为什么使用memcpy有时是可以接受的。
不要试图通过复制指针来绕过const
。
相反,可能需要复制指针所指向的数据,然后您可以使用非const指针指向非const数据,编译器和优化器都会很好。
用别名来避免限定符是绝对不能接受的,不管它是怎么做的,也就是说,把一个指针复制到一个具有不同限定符的变量上甚至不是一个好的做法,在某些情况下甚至可能导致运行时错误或其他未定义的行为(例如,在const
限定符导致引用的存储是只读的情况下,别名(ab)被用来尝试实际修改存储)。
可能的限定符应该在"上游"移除,以避免与可能不接受带限定符的参数的系统库产生不必要的冲突(当然C并不总是允许这种可能性)。
在绝对必要的地方,并且可以证明它不会引起问题,使用__UNCONST()
宏是可以接受的。如果目标平台的编译器不提供,那么最好的解决方案是将__UNCONST()
定义为no-op,并记录可能导致可接受的潜在警告(并说明原因)。
在execve中,argv
可以声明为char *const argv[]
,向后读它是一个常量字符串数组,但该数组是可变的,可以更改以包含不同的常量字符串。在老式的Unix系统中,C main函数可以接受3个参数,第三个参数是环境数组,就像execve一样。在main()中,如果你正确地修改了argv(例如,将元素设置为""
,直到你运行到终止NULL),你可以阻止ps
命令显示已经传递给你的程序的命令行参数。通过设置argv[0]="foo"
,该命令将显示为"foo"是可执行文件的名称。例如,如果您的命令将密码作为命令行参数,那么在最新的Linux系统上,最好对ps
或/proc/PID/cmdline
隐藏它。请参阅http://netsplit.com/hiding-arguments-from-ps了解如何在新颖的Linux系统中执行相同的操作。