argv#
指定形参默认值#
#include <iostream>
#include <cmath>
using namespace std;
float norm(float x, float y, float z);
float norm(float x, float y, float z = 0);
float norm(float x, float y = 0, float z);
int main()
{
cout << norm(3.0f) << endl;
cout << norm(3.0f, 4.0f) << endl;
cout << norm(3.0f, 4.0f, 5.0f) << endl;
return 0;
}
float norm(float x, float y, float z)
{
return sqrt(x * x + y * y + z * z);
}
指针作为参数#
#include <iostream>
using namespace std;
int foo1(int x)
{
x += 10;
return x;
}
int foo2(int *p)
{
(*p) += 10;
return *p;
}
int main()
{
int num1 = 20;
int num2 = foo1(num1);
cout << "num1=" << num1 << endl;
cout << "num2=" << num2 << endl;
int *p = &num1;
int num3 = foo2(p);
cout << "num1=" << num1 << endl;
cout << "*p=" << *p << endl;
cout << "num3=" << num3 << endl;
return 0;
}
引用作为参数#
#include <iostream>
#include <float.h>
struct Matrix
{
int rows;
int cols;
float *pData;
};
float matrix_max(const struct Matrix &mat)
{
float max = FLT_MIN;
// find max value of mat
for (int r = 0; r < mat.rows; r++)
for (int c = 0; c < mat.cols; c++)
{
float val = mat.pData[r * mat.cols + c];
max = (max > val ? max : val);
}
return max;
}
int main()
{
using namespace std;
Matrix matA = {3, 4};
matA.pData = new float[matA.rows * matA.cols]{1.f, 2.f, 3.f};
Matrix matB = {4, 8};
matB.pData = new float[matB.rows * matB.cols]{10.f, 20.f, 30.f};
Matrix matC = {4, 2};
matC.pData = new float[matC.rows * matC.cols]{100.f, 200.f, 300.f};
// some operations on the matrices
float maxa = matrix_max(matA);
float maxb = matrix_max(matB);
float maxc = matrix_max(matC);
cout << "max(matA) = " << maxa << endl;
cout << "max(matB) = " << maxb << endl;
cout << "max(matC) = " << maxc << endl;
delete[] matA.pData;
delete[] matB.pData;
delete[] matC.pData;
return 0;
}
getopt_long
#
#include <getopt.h>
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
/**
* struct option
* {
* const char * name;
* int has_arg;
* int * flag;
* int val;
* }
*/
static struct option long_options[] = {
{"reqarg", required_argument, NULL, 'r'},
{"optarg", optional_argument, NULL, 'o'},
{"noarg", no_argument, NULL, 'n'},
{NULL, 0, NULL, 0},
};
while (1)
{
int option_index = 0, opt;
/**
* getopt_long 会遍历 argv 数组,在每次迭代中,它会返回下一个选项字符(如果有),
* 然后,这个字符与长选项列表进行比较,如果匹配,则对应的操作会被执行,
* 如果没有匹配,那么函数将返回 -1。
* 只有一个字符,不带冒号,只表示选项,如 -c
* 一个字符,后接一个冒号,表示选项后面带一个参数,如 -a 100
* 一个字符,后接两个冒号——表示选项后面带一个可选参数,
* 即参数可有可无,如果带参数,则选项与参数直接不能有空格,如 -b200
*/
opt = getopt_long(argc, argv, "a::b:c:d", long_options, &option_index);
if (opt == -1) // 选项遍历完毕,退出循环
break;
printf("opt = %c\t\t", opt);
printf("optarg = %s\t\t", optarg);
printf("optind = %d\t\t", optind);
printf("argv[%d] = %s\t\t", optind, argv[optind]);
printf("option_index = %d\n", option_index);
}
return 0;
}
这段代码是一个使用 getopt_long
函数进行命令行参数解析的例子,它演示了如何处理短选项(使用单个字符)和长选项(使用字符串)。
首先,代码定义了一个静态的 struct option
数组 long_options
,用于描述长选项的信息。每个数组元素都是一个结构体,包含以下字段:
name
:选项的名称,字符串类型。has_arg
:指定选项是否需要参数,有三个可能值:no_argument
(0)表示不需要参数,required_argument
(1)表示必须有参数,optional_argument
(2)表示参数是可选的。flag
:如果不为NULL
,则指向一个整数变量,用于存储选项的值(即val
字段),而不是返回选项字符。如果为NULL
,则getopt_long
函数将返回选项字符。val
:选项的值,通常是一个字符。
在 main
函数中,使用一个 while
循环来遍历命令行参数。在循环中,调用 getopt_long
函数来获取下一个选项。getopt_long
的参数包括:
argc
和argv
:分别是命令行参数的数量和数组。"a::b:c:d"
:短选项的字符串表示,冒号表示需要参数的选项。long_options
:长选项的数组。option_index
:用于存储当前长选项在long_options
数组中的索引。
在每次循环迭代中,打印出当前选项的相关信息,包括选项字符、选项参数、optind
的值、对应的参数值等。
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp && ./a.out --reqarg 100 --optarg=200 --noarg
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp && ./a.out –reqarg=100 --optarg=200 --noarg
g++ -std=c++20 -O2 -Wall -pedantic -pthread main.cpp && ./a.out --reqarg 100 --optarg --noarg
这条指令表示使用 C++20 标准,进行优化,启用所有警告,使用多线程,编译 main.cpp
文件,然后运行生成的可执行文件 a.out
并传递一些命令行参数。
在不同的命令行调用中,通过使用 --reqarg
、--optarg
、--noarg
等选项来测试程序的输出。程序会解析这些选项,并打印相关的信息。
va_list
和 va_arg
#
#include <cstdarg>
#include <iostream>
void log(char *fmt, ...)
{
char buf[512] = {0};
va_list ap;
va_start(ap, fmt);
(void)vsnprintf(buf, sizeof(buf) - 2, fmt, ap);
va_end(ap);
printf("%s\n", buf);
}
int main()
{
log((char *)"%s, %d, %s", "hello", 100, "world");
}
#include <cstdarg>
#include <iostream>
double sum(int num, ...)
{
va_list valist; // 创建参数列表
double ret = 0.0;
va_start(valist, num); // 初始化参数列表
for (int i = 0; i < num; i++)
{
ret += va_arg(valist, double); // 访问参数列表中的项
}
va_end(valist); // 清理参数列表占用的内存
return ret;
}
int main()
{
std::cout << "Sum of 2, 3 is " << sum(2, 2.0, 3.0) << std::endl;
std::cout << "Sum of 2, 3, 4, 5 is " << sum(4, 2.0, 3.0, 4.0, 5.0) << std::endl;
}
sum
函数接受一个整数参数num
,表示后面可变参数的数量。va_list
是用于存储可变参数的类型,它是一个指向参数列表的对象。va_start
宏用于初始化valist
,将其指向参数列表的起始位置。使用
va_arg
宏可以依次访问参数列表中的项,本例中假设参数都是double
类型。va_end
宏用于清理参数列表占用的内存。
在 main
函数中分别调用了 sum
函数两次,每次传递不同数量的参数。这展示了可变参数函数的灵活性。
当程序运行时,sum
函数通过可变参数的方式计算传递给它的数字的总和,然后 printf
语句输出这个总和。在第一个调用中,传递了两个参数(2 和 3),在第二个调用中,传递了四个参数(2、3、4、5)。
使用 C 风格的可变参数函数,需要在运行时做类型转换,可能存在类型安全的隐患。
<typename... Args>
#
在 C++ 中,你可以使用递归或者使用 C++17 引入的折叠表达式(fold expression)来访问可变参数列表中的每个参数。
#include <iostream>
// 递归终止条件
void printArgs() {
std::cout << std::endl;
}
// 递归步骤
template<typename T, typename... Args>
void printArgs(T first, Args... args) {
std::cout << first << " ";
printArgs(args...); // 递归调用
}
int main() {
printArgs(1, "Hello", 3.14, 'A');
return 0;
}
在这个例子中,printArgs
函数通过递归的方式遍历可变参数列表,打印每个参数的值。递归终止条件是一个没有参数的版本。
#include <iostream>
template<typename... Args>
void printArgs(Args... args) {
(std::cout << ... << args) << std::endl; // 折叠表达式
}
int main() {
printArgs(1, "Hello", 3.14, 'A');
return 0;
}
在这个例子中,使用了 C++17 引入的折叠表达式。(std::cout << ... << args)
表示将所有参数展开成一个表达式,然后通过 <<
运算符连接起来,最后加上 std::endl
进行换行。
这两种方法都允许你访问可变参数列表中的每个参数,具体选择取决于你的编译环境和代码的需求。折叠表达式提供了一种更简洁和直观的语法,但需要 C++17 及以上的编译器支持。
包展开(args…)和模式(args)
例 1:字面量作为实参
template <typename... Us>
void f(Us... pargs) {}
template <typename... Ts>
void g(Ts... args)
{
// &args... 是包展开
// &args 是它的模式
f(&args...);
}
int main()
{
// Ts... args 会展开成 int E1, double E2, const char* E3
// &args... 会展开成 &E1, &E2, &E3
// Us... 会展开成 int* E1, double* E2, const char** E3
g(1, 0.2, "a");
}
例 2:数组作为实参
// 接受任意数量的模板参数(用 Ts... 表示)
template <typename... Ts>
void f(Ts...) {}
// 接受一个模板参数包 Ts 和一个非类型参数包 N
// Ts (&...arr)[N] 表示 arr 是一个引用数组,数组元素的类型是 Ts
// 数组的大小是 N
template <typename... Ts, int... N>
void g(Ts (&...arr)[N]) {}
int main()
{
// Ts... 会展开成 void f(char, int)
f('a', 1);
// Ts... 会展开成 void f(double)
f(0.1);
// Ts (&...arr)[N] 会展开成 const char (&)[2], int (&)[1]
// 模板参数 Ts 被展开为 const char
// 非类型参数 N 被展开为数组大小,即 2 和 1
int n[1];
g<const char, int>("a", n);
}
例 3:调整可变参数的位置
template <typename A, typename B, typename... C>
void func(A arg1, B arg2, C... arg3)
{
container<A, B, C...> t1; // 展开成 container<A, B, E1, E2, E3>
container<C..., A, B> t2; // 展开成 container<E1, E2, E3, A, B>
container<A, C..., B> t3; // 展开成 container<A, E1, E2, E3, B>
}