我正在寻找其他方法来获取提供给流程的命令行参数argc
和argv
,而无法直接访问传递到main()
的变量。
我想创建一个独立于main()
的类,以便argc
和argv
不必显式传递给使用它们的代码。
编辑:有些澄清似乎是有道理的。我有这门课。
class Application
{
int const argc_;
char const** const argv_;
public:
explicit Application(int, char const*[]);
};
Application::Application(int const argc, char const* argv[]) :
argc_(argc),
argv_(argv)
{
}
但我想要一个默认构造函数Application::Application()
,其中包含一些(很可能)C代码,可以从某处提取argc
和argv
。
答案 0 :(得分:31)
在Linux上,您可以从进程的proc文件系统获取此信息,即/proc/$$/cmdline
:
int pid = getpid();
char fname[PATH_MAX];
char cmdline[ARG_MAX];
snprintf(fname, sizeof fname, "/proc/%d/cmdline", pid);
FILE *fp = fopen(fname);
fgets(cmdline, sizeof cmdline, fp);
// the arguments are in cmdline
答案 1 :(得分:29)
main
的参数由C运行时定义,是获取命令行参数的唯一标准/可移植方式。不要打架系统。 :)
如果您只想使用自己的API提供对程序其他部分中命令行参数的访问,则有很多方法可以执行此操作。只需使用argv/argc
中的main
初始化自定义类,从那时起,您可以忽略它们并使用您自己的API。单身模式非常适合这类事情。
为了说明最流行的C ++框架之一,Qt使用了这种机制:
int main(int argc, char* argv[])
{
QCoreApplication app(argc, argv);
std::cout << app.arguments().at(0) << std::endl;
return app.exec();
}
应用程序捕获参数并将其复制到QStringList
。有关详细信息,请参阅QCoreApplication::arguments()。
类似地,Mac上的Cocoa有一个特殊的函数,它捕获命令行参数并使它们可用于框架:
#import <Cocoa/Cocoa.h>
int main(int argc, char *argv[])
{
return NSApplicationMain(argc, (const char **)argv);
}
然后使用NSProcessInfo.arguments属性在应用程序的任何位置提供参数。
我在您的更新问题中注意到您的班级直接在其实例中存储了argc/argv
逐字的副本:
int const argc_;
char const** const argv_;
虽然这应该是安全的(argv
指针的生命周期应该在整个生命周期内有效),但它不是很像C ++。考虑创建一个字符串向量(std::vector<std::string>
)作为容器并复制字符串。然后它们甚至可以安全地变更(如果你想要的话!)。
我想创建一个独立于main()的类,这样就不必将argc和argv显式传递给使用它们的代码。
目前尚不清楚为什么从main
传递此信息在某种程度上是一件应该避免的坏事。这就是主要框架如何做到的。
我建议您查看使用单例来确保只有一个Application
类的实例。参数可以通过main
传递,但没有其他代码需要知道或关心它们来自何处。
如果确实想要隐藏main
的参数传递给Application
构造函数的事实,您可以使用宏隐藏它们。
答案 2 :(得分:15)
要回答有关Windows的部分问题,可以获取命令行作为GetCommandLine
函数的返回,该函数记录为here,无需显式访问{{main
的参数1}}功能。
答案 3 :(得分:13)
我完全赞同@gavinb和其他人。你真的应该使用clang
中的参数并存储它们或将它们传递给你需要的地方。这是唯一可移植的方式。
但是,仅出于教育目的,以下内容适用于OS X上的gcc
和Linux上的#include <stdio.h>
__attribute__((constructor)) void stuff(int argc, char **argv)
{
for (int i=0; i<argc; i++) {
printf("%s: argv[%d] = '%s'\n", __FUNCTION__, i, argv[i]);
}
}
int main(int argc, char **argv)
{
for (int i=0; i<argc; i++) {
printf("%s: argv[%d] = '%s'\n", __FUNCTION__, i, argv[i]);
}
return 0;
}
:
$ gcc -std=c99 -o test test.c && ./test this will also get you the arguments
stuff: argv[0] = './test'
stuff: argv[1] = 'this'
stuff: argv[2] = 'will'
stuff: argv[3] = 'also'
stuff: argv[4] = 'get'
stuff: argv[5] = 'you'
stuff: argv[6] = 'the'
stuff: argv[7] = 'arguments'
main: argv[0] = './test'
main: argv[1] = 'this'
main: argv[2] = 'will'
main: argv[3] = 'also'
main: argv[4] = 'get'
main: argv[5] = 'you'
main: argv[6] = 'the'
main: argv[7] = 'arguments'
将输出:
stuff
原因是因为__attribute__((constructor))
函数被标记为main
,它将在动态链接器加载当前库时运行它。这意味着在主程序中它甚至会在$('DOMELEMENT').on('input', function() {
$(this).checkvalue --> disable/enable.
});
之前运行并具有类似的环境。因此,您可以获得参数。
但请允许我重申:这仅用于教育目的,不应在任何生产代码中使用。它不会是便携式的,可能会在没有任何警告的情况下随时中断。
答案 4 :(得分:5)
在Windows中,如果您需要将参数设为wchar_t *
,则可以使用CommandLineToArgvW()
:
int main()
{
LPWSTR *sz_arglist;
int n_args;
int result;
sz_arglist = CommandLineToArgvW(GetCommandLineW(), &n_args);
if (sz_arglist == NULL)
{
fprintf(stderr, _("CommandLineToArgvW() failed.\n"));
return 1;
}
else
{
result = wmain(n_args, sz_arglist);
}
LocalFree(sz_arglist);
return result;
}
使用MinGW非常方便,因为gcc
无法将int _wmain(int, wchar_t *)
识别为有效的main
原型。
答案 5 :(得分:5)
传递值不构成创建依赖关系。您的课程并不关心那些argc
或argv
值的来源 - 它只是希望它们通过。您可能希望在某处复制值 - 不能保证它们不会被更改(这同样适用于GetCommandLine
等替代方法。)
恰恰相反,事实上 - 当您使用GetCommandLine
之类的东西时,您正在创建一个隐藏的依赖项。突然间,你没有一个简单的“传递价值”语义,而是“神奇地从其他地方接受他们的输入” - 结合前面提到的“价值随时可能发生变化”,这使你的代码变得更加脆弱,更不用说了无法测试。解析命令行参数绝对是自动化测试非常有益的情况之一。如果你愿意的话,它是一个全局变量而不是方法参数方法。
答案 6 :(得分:3)
在C / C ++中,如果main()
没有导出它们,那么就没有直接的方式来访问它们;然而,这并不意味着没有间接的方式。许多类似Posix的系统使用elf格式,它在栈上传递argc
,argv
和envp
,以便由_start()
初始化并传递到main()
通过正常的呼叫约定。这通常在汇编中完成(因为仍然没有可移植的方式来获取堆栈指针)并放入“启动文件”,通常使用名称crt.o的一些变体。
如果您无权访问main()
以便只能导出符号,则可能无法访问_start()
。那么为什么呢,我甚至会提到它吗?因为第3个参数envp
。由于environ
是使用envp在_start()
期间设置 的标准导出变量。在许多ELF系统上,如果您使用environ
的基址并使用负数组索引向后移动,则可以推导出argc和argv参数。第一个应该是NULL,然后是最后一个argv参数,直到你到达第一个。当指向的值转换为long等于负指数的负数时,你有argc,而下一个(比负指数多一个)是argv / argv [0]。
答案 7 :(得分:2)
对于需要我知道类型为int argc, char *argv[]
的参数的函数,几乎没有常见的场景。其中一个明显的例子是GLUT,其初始化函数从main()
接管这些参数,这是一种&#34;嵌套的主&#34;场景。这可能是您想要的行为,也可能不是。如果没有,因为没有命名这些参数的约定,只要你的函数有它的参数解析器,并且你知道你正在做什么,你可以做任何你需要的,硬编码的:
int foo = 1;
char * bar[1] = {" "};
或从用户输入读取或以其他方式生成,AFAIK。
int myFunc( int foo, char *bar[]){
//argument parser
{… …}
return 0;
}
请参阅此SO post。
答案 8 :(得分:1)
听起来你想要的是一个全局变量;你应该做的只是将argc和argv作为参数传递。
答案 9 :(得分:1)
最便携的方法是使用全局变量来存储参数。你可以使用Singleton
(如问题中的类,但是由main初始化的单例)或类似的Service Locator
来使这不那么难看,它基本上是相同的:在main中创建一个对象,静态传递并存储params,并让另一个或同一个类访问它们。
非便携式方式在Windows中使用GetCommandLine,访问/proc/<pid>/cmdline
或(/proc/self/cmdline
),或使用特定于编译器的扩展程序,如__attribute__((constructor))
请注意,通过等效的GetCommandLine
is not possible来获取命令行(TLDR:Commandline不会传递给内核,但已经被调用过程解析并拆分(例如shell))