上线了建站教程,青岛手机建站公司,如何制作网站后台,大学生网站建设结题报告原理解释#xff1a; VA_LIST 是在C语言中解决变参问题的一组宏#xff0c;在stdarg.h头文件下。 VA_LIST的用法#xff1a; #xff08;1#xff09;首先在函数里定义一具VA_LIST型的变量#xff0c;这个变量是指向参数的指针 #xff08;2 VA_LIST 是在C语言中解决变参问题的一组宏在stdarg.h头文件下。 VA_LIST的用法 1首先在函数里定义一具VA_LIST型的变量这个变量是指向参数的指针 2然后用VA_START宏初始化变量刚定义的VA_LIST变量这个宏的第二个参数是第一个可变参数的前一个参数是一个固定的参数。 3然后用VA_ARG返回可变的参数VA_ARG的第二个参数是你要返回的参数的类型。 4最后用VA_END宏结束可变参数的获取。然后你就可以在函数里使用第二个参数了。如果函数有多个可变参数的依次调用VA_ARG获取各个参数。 VA_LIST在编译器中的处理 (1)在运行VA_START(ap,v)以后ap指向第一个可变参数在堆栈的地址。 2VA_ARG()取得类型t的可变参数值在这步操作中首先apt sizeof(t类型)让ap指向下一个参数的地址。然后返回ap-sizeof(t类型)的t类型*指针这正是第一个可变参数在堆栈里的地址。然后用*取得这个地址的内容。 3VA_END(),X86平台定义为ap ((char*)0)使ap不再指向堆栈而是跟NULL一样有些直接定义为((void*)0)这样编译器不会为VA_END产生代码例如gcc在Linux的X86平台就是这样定义的。 要注意的是由于参数的地址用于VA_START宏所以参数不能声明为寄存器变量或作为函数或数组类型。 使用VA_LIST应该注意的问题 1因为va_start, va_arg, va_end等定义成宏,所以它显得很愚蠢,可变参数的类型和个数完全在该函数中由程序代码控制,它并不能智能地识别不同参数的个数和类型.也就是说,你想实现智能识别可变参数的话是要通过在自己的程序里作判断来实现的. 2另外有一个问题,因为编译器对可变参数的函数的原型检查不够严格,对编程查错不利.不利于我们写出高质量的代码。 小结可变参数的函数原理其实很简单而VA系列是以宏定义来定义的实现跟堆栈相关。我们写一个可变函数的C函数时有利也有弊,所以在不必要的场合我们无需用到可变参数如果在C里我们应该利用C多态性来实现可变参数的功能尽量避免用C语言的方式来实现。 va_list ap; //声明一个变量来转换参数列表 va_start(ap,fmt); //初始化变量 va_end(ap); //结束变量列表,和va_start成对使用 可以根据va_arg(ap,type)取出参数 已经经过调试成功的输出程序 #includestdio.h #include stdarg.h #define bufsize 80 char buffer[bufsize]; int vspf(char *fmt, ...) { va_list argptr; int cnt; va_start(argptr, fmt); cnt vsnprintf(buffer,bufsize ,fmt, argptr); va_end(argptr); return(cnt); } int main(void) { int inumber 30; float fnumber 90.0; char string[4] abc; vspf(%d %f %s, inumber, fnumber, string); printf(%s\n, buffer); return 0; } 头文件: #include stdarg.h 函数声明: int vsnprintf(char*str,size_tsize,constchar*format,va_listap); 参数说明:
char *str [out],把生成的格式化的字符串存放在这里.size_t size [in], str可接受的最大字节数,防止产生数组越界.const char *format [in],指定输出格式的字符串它决定了你需要提供的可变参数的类型、个数和顺序。va_list ap [in], va_list变量. va:variable-argument:可变参数 函数功能将可变参数格式化输出到一个字符数组。 用法类似于vsprintf不过加了size的限制防止了内存溢出size为str所指的存储空间的大小。 返回值执行成功返回写入到字符数组str中的字符个数不包含终止符最大不超过size执行失败返回负值并置errno.[1] 备注: linux环境下是:vsnprintf VC6环境下是:_vsnprintf 2用法实例 #include stdio.h #include stdlib.h #include stdarg.h char *make_message(const char *fmt, ...) { /* 初始时假设我们只需要不超过100字节大小的空间 */ int n, size 100; char *p; va_list ap; if ( (p (char *) malloc(size*sizeof(char))) NULL) return NULL; while (1) { /* 尝试在申请的空间中进行打印操作 */ va_start(ap, fmt); n vsnprintf (p, size, fmt, ap); va_end(ap); /* 如果vsnprintf调用成功返回该字符串 */ if (n -1 n size) return p; /* vsnprintf调用失败(n0)或者p的空间不足够容纳size大小的字符串(nsize)尝试申请更大的空间*/ size * 2; /* 两倍原来大小的空间 */ if ((p (char *)realloc(p, size*sizeof(char))) NULL) return NULL; } } int main() { /* 调用上面的函数 */ char* str make_message(%d,%d,%d,%d,5,6,7,8); printf(%s\n,str); free(str); /* we allocate the memory in the make_message function, so we should release it by caller(main function). */ return 0; } linux 中解析命令行参数(getopt_long用法)optargoptind getopt_long支持长选项的命令行解析使用man getopt_long得到其声明如下 #include getopt.h int getopt_long(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex); int getopt_long_only(int argc, char * const argv[], const char *optstring, const struct option *longopts, int *longindex); 说明函数中的argc和argv通常直接从main()到两个参数传递而来。optsting是选项参数组成的字符串如果该字符串里任一字母后有冒号那么这个选项就要求有参数。下一个参数是指向数组的指针这个数组是 option结构数组option结构称为长选项表其声明如下 struct option { const char *name; int has_arg; int *flag; int val; }; 结构中的元素解释如下 const char *name选项名前面没有短横线。譬如help、verbose之类。 int has_arg描述长选项是否有选项参数如果有是哪种类型的参数其值见下表: 符号常量 数值 含义 no_argument 0 选项没有参数 required_argument 1 选项需要参数 optional_argument 2 选项参数是可选的 int *flag 如果该指针为NULL那么getopt_long返回val字段的值 如果该指针不为NULL那么会使得它所指向的结构填入val字段的值同时getopt_long返回0 int val 如果flag是NULL那么val通常是个字符常量如果短选项和长选项一致那么该字符就应该与optstring中出现的这个选项的参数相同 最后一个参数longindex参数一般赋为NULL即可如果没有设置为NULL那么它就指向一个变量这个变量会被赋值为寻找到的长选项在longopts中的索引值这可以用于错误诊断。 注GNU提供的getopt-long()和getopt-long-only()函数其中后者的长选项字串是以一个短横线开始的而非一对短横线。 linux 命令行约定 几乎所有的GNU/Linux程序都遵循一些命令行参数定义的约定。程序希望出现的参数可以分成两种选项options or flags、其他类型的的参数。Options修饰了程序运行的方式其他类型的参数则提供了输入例如输入文件的名称。 对于options类型参数可以有两种方式 1短选项short options:顾名思义就是短小参数。它们通常包含一个连字号和一个字母大写或小写字母。例如-s-h等。 2长选项long options长选项包含了两个连字号和一些大小写字母组成的单词。例如--size--help等。 *注一个程序通常会提供包括short options和long options两种参数形式的参数。 对于其他类型参数的说明 这种类型的参数通常跟随在options类型参数之后。例如ls –s /功能为显示root目录的大小。’/ ’这个参数告诉ls要显示目录的路径。 getopt_long()函数使用规则 1使用前准备两种数据结构 字符指针型变量 该数据结构包括了所有要定义的短选项每一个选项都只用单个字母表示。如果该选项需要参数如需要文件路径等则其后跟一个冒号。例如三个短选项分别为‘-h’‘-o’‘-v’其中-o需要参数其他两个不需要参数。那么我们可以将数据结构定义成如下形式 const char * const shor_options “ho:v” ; 如果是否有参数是可选的则在后面有两个冒号。 struct option 类型数组 该数据结构中的每个元素对应了一个长选项并且每个元素是由四个域组成。通常情况下可以按以下规则使用。 第一个元素描述长选项的名称 第二个选项代表该选项是否需要跟着参数需要参数则为1反之为0 第三个选项可以赋为NULL 第四个选项是该长选项对应的短选项名称。 另外数据结构的最后一个元素要求所有域的内容均为0即{NULL,0,NULL,0}。下面举例说明还是按照短选项为‘-h’‘-o’‘-v’的例子该数据结构可以定义成如下形式 const struct option long_options { { “help”, 0, NULL, ‘h’ }, { “output”, 1, NULL, ‘o’ }, { “verbose”, 0, NULL, ‘v’ }, { NULL, 0, NULL, 0 } }; 2调用方法 参照1准备的两个数据结构则调用方式可为 getopt_long( argc, argv, short_options, long_options, NULL); 3几种常见返回值 (a)每次调用该函数它都会分析一个选项并且返回它的短选项如果分析完毕即已经没有选项了则会返回-1。 (b)如果getopt_long()在分析选项时遇到一个没有定义过的选项则返回值为‘?’此时程序员可以打印出所定义命令行的使用信息给用户。 (c)当处理一个带参数的选项时全局变量optarg会指向它的参数 (d)当函数分析完所有参数时全局变量optindinto argv会指向第一个‘非选项’的位置 实践小例子 view plaincopy to clipboardprint? #include stdio.h #include getopt.h char *l_opt_arg; char* const short_options nbl:; struct option long_options[] { { name, 0, NULL, n }, { bf_name, 0, NULL, b }, { love, 1, NULL, l }, { 0, 0, 0, 0}, }; int main(int argc, char *argv[]) { int c; while((c getopt_long (argc, argv, short_options, long_options, NULL)) ! -1) { switch (c) { case n: printf(My name is XL.\n); break; case b: printf(His name is ST.\n); break; case l: l_opt_arg optarg; printf(Our love is %s!\n, l_opt_arg); break; } } return 0; } 编译并运行 [rootlocalhost liuxltest]# gcc -o getopt getopt.c [rootlocalhost liuxltest]# ./getopt -n -b -l forever My name is XL. His name is ST. Our love is forever! [rootlocalhost liuxltest]# [rootlocalhost liuxltest]# ./getopt -nb -l forever My name is XL. His name is ST. Our love is forever! [rootlocalhost liuxltest]# ./getopt -nbl forever My name is XL. His name is ST. Our love is forever! 刚开始接触 一些处理命令行参数的操作开始不太明白用例子测试了一下感觉比以前明了多了。 命令行参数有长参数如version, 还有短参数 如 v, 那么用这两个都可以。程序处理的时候会首先把长参数转换成对应的短参数如会把version转成v, 再进行 v 对应的操作就可以了。 命令行参数的选项有的需要参数有的不需要参数或者有的参数是可选的那么怎么区分呢 首先对这些选项如何组织起来 是以字符串的形式组织起来了。如我有一个程序有两个选项-a, -b, 我输入的时候是 ./a.out -a -b, 那么中间会处理成这种 ab这种字符串的形式这个字符串就是所有的命令行的输入选项。区别是否有参数就在于此。如果某个选项必须有参数则这一选项后有一个参数如果参数是可选的则其后面有两个冒号。如 -a 是没有参数的 -b 后面必须有参数 -c 后面是否有参数是可选的。则短的命令行选项是 ab:c:: 下面我们通过一个简单的例子看一下。 #include stdio.h #include unistd.h #include getopt.h char *l_opt_arg; const char* const short_options myl:; struct option long_options[] { { name, 0, NULL, m}, //长选项对应的短选项参数 第二个0表示选项后面无参数 1为有参数2为可选 { yourname, 0, NULL, y}, { love, 1, NULL, l}, { 0, 0, 0, 0}, }; int main(int argc, char *argv[]) { int c, i; printf(before:\n); for (i 1; i argc; i) printf(arg:%d\r\targv:%s\n, i, argv[i]); printf(\n); while((c getopt_long (argc, argv, short_options, long_options, NULL)) ! -1) { switch (c) { case m: printf(My name is A.\n); break; case y: printf(His name is B.\n); break; case l: l_opt_arg optarg; printf(Our love is %s!\n, l_opt_arg); break; } } printf(optind:%d\n, optind); printf(\nafter:\n); for (i1; iargc; i) printf(arg:%d\r\targv:%s\n, i, argv[i]); printf(................................\n); for (i optind; i argc; i) printf(arg:%d\rargv:%s\n,i,argv[i]); return 0; } 注意此程序可接收的的选项有三个 一个是m ,不带参数 y 不带参数 l 要求有参数。 那如果-m 不带参数如果我写了参数会怎么样呢下面看测试 在调用 getopt_long 以后 optind 的值随之变化 。在while循环后我们再把开始的命令行参数打印出来看一下有什么不同。 把上面的代码命名为 getopt_long.c 编译可执行文件为 a.out $ gcc getopt_long.c $ ./a.out -m -y before: arg:1 : -m arg:2 : -y My name is A. His name is B. optind:3 after: arg:1 : -m arg:2 : -y $ ./a.out -m -y -l banana before: arg:1 : -m arg:2 : -y arg:3 : -l arg:4 : banana My name is A. His name is B. Our love is banana! optind:5 after: arg:1 : -m arg:2 : -y arg:3 : -l arg:4 : banana $./a.out -m lisi -y zhangsan -l banana aaa before: arg:1 : -m arg:2 : lisi arg:3 : -y arg:4 : zhangsan arg:5 : -l arg:6 : banana arg:7 : aaa My name is A. His name is B. Our love is banana! optind:5 after: arg:1 : -m arg:2 : -y arg:3 : -l arg:4 : banana arg:5 : lisi
arg:6 : zhangsan
arg:7 : aaa 注意 argv 里面值的顺序已经和原来不一样了对命令行的参数重新组织了一下顺序也就是不认识的命令行参数都放在了argv的最后其中 optind 指向了这些没有被解释的参数的第一个。 optind有作用吧如果你想输出哪些命令行参数没有被识别可以打印出来 for (ioptind; iargc; i) printf(%s\n, argv[i]); 即可 附如果是长参数则使用 --, 如 --help, 因为 -help时(选项不需要参数的情况) 会把它当成 四个选项 -h -e -l -p. 所以使用长参数时要用两个 横线 -- #include getopt.h #include stdio.h #include stdlib.h const char* program_name; void print_usage (FILE* stream, int exit_code) { fprintf (stream, Usage: %s options [ inputfile ... ]\n, program_name); fprintf (stream, -h --help 显示这个帮助信息.\n -o --output filename 将输出定位到文件.\n -v --version 打印版本信息.\n); exit (exit_code); } int main (int argc, char* argv[]) { int next_option;//下一个要处理的参数符号 int haveargv 0;//是否有我们要的正确参数一个标识 const char* const short_options ho:v; const struct option long_options[] { { help, 0, NULL, h }, { output, 1, NULL, o }, { version, 0, NULL, v }, { NULL, 0, NULL, 0 }};//最后一个元素标识为NULL const char *output_filename NULL; int verbose 0; program_name argv[0]; do { next_option getopt_long (argc, argv, short_options, long_options, NULL); switch (next_option) { case h: haveargv 1; print_usage (stdout, 0); case o: output_filename optarg; haveargv 1; break; case v: verbose 1; haveargv 1; break; case :: break; case ?: print_usage (stderr, 1); case -1: if (!haveargv) { print_usage (stderr, 1); } break; default: print_usage (stderr, 1); break; } }while (next_option ! -1); printf(optind.................%d\n,optind); if (haveargv) { int i; for (i optind; i argc; i) printf (Argument: %s\n, argv[i]); } return 0; } getopt的用法与optarg getopt被用来解析命令行选项参数。就不用自己写东东处理argv了。 #include unistd.h extern char *optarg; //选项的参数指针 extern int optind, //下一次调用getopt的时从optind存储的位置处重新开始检查选项。 extern int opterr, //当opterr0时getopt不向stderr输出错误信息。 extern int optopt; //当命令行选项字符不包括在optstring中或者选项缺少必要的参数时该选项存储在optopt中getopt返回’、 int getopt(int argc, char * const argv[], const char *optstring); 调用一次返回一个选项。 在命令行选项参数再也检查不到optstring中包含的选项时返回1同时optind储存第一个不包含选项的命令行参数。 首先说一下什么是选项什么是参数。 字符串optstring可以下列元素 1.单个字符表示选项 2.单个字符后接一个冒号表示该选项后必须跟一个参数。参数紧跟在选项后或者以空格隔开。该参数的指针赋给optarg。 3 单个字符后跟两个冒号表示该选项后必须跟一个参数。参数必须紧跟在选项后不能以空格隔开。该参数的指针赋给optarg。这个特性是GNU的扩张。 getopt处理以-’开头的命令行参数如optstringab:c::d::,命令行为getopt.exe -a -b host -ckeke -d haha 在这个命令行参数中-a和-h就是选项元素去掉-a,b,c就是选项。host是b的参数keke是c的参数。但haha并不是d的参数因为它们中间有空格隔开。 还要注意的是默认情况下getopt会重新排列命令行参数的顺序所以到最后所有不包含选项的命令行参数都排到最后。 如getopt.exe -a ima -b host -ckeke -d haha, 都最后命令行参数的顺序是 -a -b host -ckeke -d ima haha 如果optstring中的字符串以加号开头或者环境变量POSIXLY_CORRE被设置。那么一遇到不包含选项的命令行参数getopt就会停止返回-1。 #include stdio.h #include stdlib.h #include unistd.h int main(int argc, char **argv) { int result; opterr 0; //使getopt不行stderr输出错误信息 while( (result getopt(argc, argv, ab:c::)) ! -1 ) { switch(result) { case a: printf(optiona, optopt%c, optarg%s\n, optopt, optarg); break; case b: printf(optionb, optopt%c, optarg%s\n, optopt, optarg); break; case c: printf(optionc, optopt%c, optarg%s\n, optopt, optarg); break; case ?: printf(result?, optopt%c, optarg%s\n, optopt, optarg); break; default: printf(default, result%c\n,result); break; } printf(argv[%d]%s\n, optind, argv[optind]); } printf(result-1, optind%d\n, optind); //看看最后optind的位置 for(result optind; result argc; result) printf(-----argv[%d]%s\n, result, argv[result]); //看看最后的命令行参数看顺序是否改变了哈。 for(result 1; result argc; result) printf(\nat the end-----argv[%d]%s\n, result, argv[result]); return 0; } unistd里有个 optind 变量每次getopt后这个索引指向argv里当前分析的字符串的下一个索引因此 argv[optind]就能得到下个字符串通过判断是否以 -开头就可。下面是个测试程序 #include stdio.h #include unistd.h int main(int argc, char* argv[]) { int tmp 4; while( (tmp getopt(argc, argv, abck)) ! -1 ) { printf(-%c\t, tmp); int opt optind ; while( opt argc ) { if ( argv[opt][0] ! - ) { printf(%s\t, argv[opt]); opt ; } else break; } printf(\n); } getchar(); } 函数说明 getopt()用来分析命令行参数。参数argc和argv是由main()传递的参数个数和内容。参数optstring 则代表欲处理的选项字符串。此函数会返回在argv 中下一个的选项字母此字母会对应参数optstring 中的字母。如果选项字符串里的字母后接着冒号“:”则表示还有相关的参数全域变量optarg 即会指向此额外参数。如果getopt()找不到符合的参数则会印出错信息并将全域变量optopt设为“?”字符如果不希望getopt()印出错 信息则只要将全域变量opterr设为0即可。 返回值 如果找到符合的参数则返回此参数字母如果参数不包含在参数optstring 的选项字母则返回“?”字符分析结束则返回-1。 范例 #includestdio.h #includeunistd.h int main(int argc,char **argv) { int ch; opterr 0; while((ch getopt(argc,argv,”a:bcde”))! -1) switch(ch) { case ‘a’: printf(“option a:’%s’\n”,optarg); break; case ‘b’: printf(“option b :b\n”); break; default: printf(“other option :%c\n”,ch); } printf(“optopt %c\n”,optopt); } 执行 $./getopt –b option b:b $./getopt –c other option:c $./getopt –a other option :? $./getopt –a12345 option a:’12345’