概况
做 CSAPP 的 CacheLab 的第一个门槛是学习使用 getopt()
函数。它是 Linux 下的函数,Windows 先不考虑了。
查询 getopt 用法的“官方”步骤是看 man 手册:
man 3 getopt
不过这手册看的让人头晕,还是写几个例子,分解开来逐一击破吧!
写了8个例子,每个例子都有对应注释和示例用法;也可以在 main 函数中把 argc 和 argv 传递过去,分别调用和尝试!
int main(int argc, char** argv)
{
//printf("opterr = %d\n", opterr);
//opt_example1(argc, argv);
//opt_example2(argc, argv);
//opt_example3(argc, argv);
//opt_example4(argc, argv);
//opt_example5(argc, argv);
//opt_example6(argc, argv);
//opt_example7(argc, argv);
opt_example8(argc, argv);
return 0;
}
例子1
#include <stdio.h>
#include <unistd.h>
#include <stdbool.h>
#include <stdlib.h>
// -a 选项后面一定要有参数,参数和选项之间可以有空格,也可以没空格。解析出的参数放在 optarg 变量里。
// examples:
// ./a.out -a 6
// ./a.out -a6
// ./a.out -a s
// 参数也可以用引号包裹起来,这样可以包含空格.
// ./a.out -a "s b"
// ./a.out -a"s b"
void opt_example1(int argc, char** argv)
{
const char* optstr = "a:";
char ch;
while ( (ch = getopt(argc, argv, optstr)) != -1 )
{
switch (ch) {
case 'a':
printf("have option: -a\n");
printf("the argument of -a is %s\n", optarg);
break;
}
}
}
例子2
// -b 选项后面可以有参数,也可以没有参数。但是有参数情况下,必须有空格。若没有空格,得到的参数是 null
// examples:
// ./a.out -b
// ./a.out -b hello
void opt_example2(int argc, char** argv)
{
const char* optstr = "b::";
char ch;
while ( (ch = getopt(argc, argv, optstr)) != -1 )
{
switch (ch) {
case 'b':
printf("have option: -b\n");
printf("the argument of -b is %s\n", optarg);
break;
}
}
}
例子3
// -c 选项后面不能有参数。如果有参数,会被当成别的选项. 打印 optarg 得到 null
// examples:
// ./a.out -c
void opt_example3(int argc, char** argv)
{
const char* optstr = "c";
char ch;
while ( (ch = getopt(argc, argv, optstr)) != -1 )
{
switch (ch) {
case 'c':
printf("have option: -c\n");
printf("the argument of -c is %s\n", optarg);
break;
}
}
}
例子4
// -c 选项和 -d 选项可以合并
// examples:
// ./a.out -c -d
// ./a.out -d -c
// ./a.out -cd
// ./a.out -dc
void opt_example4(int argc, char** argv)
{
const char* optstr = "cd";
char ch;
while ( (ch = getopt(argc, argv, optstr)) != -1 )
{
switch (ch) {
case 'c':
printf("have option: -c\n");
printf("the argument of -c is %s\n", optarg);
break;
case 'd':
printf("have option: -d\n");
printf("the argument of -d is %s\n", optarg);
break;
}
}
}
例子5
// argv 中不以 "-" 开头的、并且不是跟在选项后面的,不会被 getopt() 解析
// 不被解析意味着, 调用 getopt() 之后会改变 argv 的元素顺序, 被解析的 选项+参数 们 在前,没被解析的在后
// examples:
// ./a.out shenme -a hello -b world
// 新版 argv 会是 ./a.out -a hello -b world shenme
// ./a.out -a hello shenme -b world
// 新版 argv 会是 ./a.out -a hello -b world shenme
// 被解析的选项们的顺序是不会被改变的. 如果没空格,也不会被更细出空格
// ./a.out shenme -b world -a hello
// 新版 argv 会是 ./a.out -b world -a hello shenme
// ./a.out shenme -bworld -a hello
// 新版 argv 会是 ./a.out -bworld -a hello shenme
// 调用 getopt() 后, optind 会指向第一个非选项和参数的位置
void opt_example5(int argc, char** argv)
{
printf("original argv is:");
for (int i = 0; i < argc; i++)
{
printf(" %s", argv[i]);
}
printf("\n");
char ch;
const char* optstr = "a:b:c:";
while( (ch = getopt(argc, argv, optstr)) != -1 )
{
switch(ch)
{
case 'a':
break;
case 'b':
break;
}
}
printf("after getopt, argv is:");
for (int i = 0; i < argc; i++)
{
printf(" %s", argv[i]);
}
printf("\n");
printf("and now, optind = %d, argv[%d] = %s\n",
optind, optind, argv[optind]);
}
例子6
// 利用 optind 判断是否输出了指定选项和参数外的参数
// 若只输入了符合 optstr 的 argv, 则 optind 小于 argc
// 若输入了额外的参数, 则 optind 等于 argc
// examples:
// ./a.out -a hello world
// 输出:
// optind = 3, argv[3] = world
// you have entered extra arguments: world
//
// ./a.out -a 0 hello world
// 输出:
// optind = 3, argv[3] = hello
// you have entered extra arguments: hello world
void opt_example6(int argc, char** argv)
{
char ch;
const char* optstr = "a:b:c:";
while( (ch = getopt(argc, argv, optstr)) != -1 )
{
switch(ch)
{
case 'a':
break;
case 'b':
break;
}
}
printf("optind = %d, argv[%d] = %s\n",
optind, optind, argv[optind]);
if (optind < argc)
{
printf("you have entered extra arguments:");
for (int i = optind; i < argc; i++)
{
printf(" %s", argv[i]);
}
printf("\n");
}
}
例子7
// argv 可能包含 getopt() 无法解析的选项,也就是不在 optstr 中的选项
// 这样的选项,对应到 getopt() 返回 `?` , 可以设置为打印help信息
// examples:
// ./a.out -s
// 输出
// ./a.out: invalid option -- 's'
// Usage: ./a.out -a <a_arg> -b <b_arg>
void opt_example7(int argc, char** argv)
{
char ch;
const char* optstr = "a:b:c:";
while( (ch = getopt(argc, argv, optstr)) != -1 )
{
switch(ch)
{
case 'a':
break;
case 'b':
break;
case '?':
printf("Usage: %s -a <a_arg> -b <b_arg>\n", argv[0]);
}
}
}
例子8
void help_and_die(char* argv0)
{
printf("Usage: %s [-hv] -s <num> -E <num> -b <num> -t <file>\n", argv0);
printf("Options:\n");
printf(" -h Print this help message.\n");
printf(" -v Optional verbose flag.\n");
printf(" -s <num> Number of set index bits.\n");
printf(" -E <num> Number of lines per set.\n");
printf(" -b <num> Number of block offset bits.\n");
printf(" -t <file> Trace file.\n");
printf("\n");
printf("Examples:\n");
printf(" linux> %s -s 4 -E 1 -b 4 -t traces/yi.trace\n", argv0);
printf(" linux> %s -v -s 8 -E 2 -b 4 -t traces/yi.trace\n", argv0);
exit(0);
}
// CSAPP CacheLab 的例子,模拟 csim-ref 的参数解析
void opt_example8(int argc, char* argv[])
{
const char* optstr = "hvs:E:b:t:";
char ch;
bool verbose = false;
int s = -1;
int E = -1;
int b = -1;
int t = -1;
while( (ch = getopt(argc, argv, optstr)) != -1 )
{
switch (ch)
{
case 'h':
help_and_die(argv[0]);
exit(0);
break;
case 'v':
verbose = true;
break;
case 's':
s = atoi(optarg);
break;
case 'E':
E = atoi(optarg);
break;
case 'b':
b = atoi(optarg);
break;
case 't':
t = atoi(optarg);
break;
case '?':
help_and_die(argv[0]);
break;
}
}
if (optind == 1)
{
printf("%s: Missing required command line argument\n", argv[0]);
help_and_die(argv[0]);
}
}