C语言关于我回头学的那些命令行参数等
Posted 啥都不会吖
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言关于我回头学的那些命令行参数等相关的知识,希望对你有一定的参考价值。
前言
我的第一门语言就是C,但是学艺不精,中途跑去学了C#和Java后,感觉到了C的重要性,毕竟是最接近底层的语言,又跑回来学C。
毕竟前两门的控制语句,变量什么的都是类似的,回到C后只需要学习一些特定C的语法,比如宏,预编译指令等等,这些对我来说都是陌生的词汇。
所以边学边记录一下以前的知识。
文章目录
一、命令行参数
执行程序时,可以从命令行传值给 C 程序。这些值被称为命令行参数,它们对程序很重要,特别是当您想从外部控制程序,而不是在代码内对这些值进行硬编码时,就显得尤为重要了。
命令行参数是使用 main() 函数参数来处理的。
其中,
argc
:传入参数的个数,默认为1(程序名称算一个)argv[]
:一个指针数组,指向传递给程序的每个参数。
下面是一个简单的实例,检查命令行是否有提供参数,并根据参数执行相应的动作:
#include <stdio.h>
int main( int argc, char *argv[] )
if( argc == 2 )
printf("The argument supplied is %s\\n", argv[1]);
else if( argc > 2 )
printf("Too many arguments supplied.\\n");
else
printf("One argument expected.\\n");
使用一个参数,编译并执行上面的代码,它会产生下列结果:
$./a.out testing
The argument supplied is testing
使用两个参数,编译并执行上面的代码,它会产生下列结果:
$./a.out testing1 testing2
Too many arguments supplied
不传任何参数,编译并执行上面的代码,它会产生下列结果:
$./a.out
One argument expected
应当指出的是,argv[0]
存储程序的名称,argv[1]
是一个指向第一个命令行参数的指针,*argv[n]
是最后一个参数。
如果没有提供任何参数,argc
将为 1,就是存储程序的名称。
如果传递了一个参数,argc
将被设置为 2,就是存储程序的名称加上第一个参数。
参数带空格的情况
多个命令行参数之间用空格分隔,但是如果参数本身带有空格,那么传递参数的时候应把参数放置在双引号 “” 或单引号 ‘’ 内部。
#include <stdio.h>
int main( int argc, char *argv[] )
printf("Program name %s\\n", argv[0]);
if( argc == 2 )
printf("The argument supplied is %s\\n", argv[1]);
else if( argc > 2 )
printf("Too many arguments supplied.\\n");
else
printf("One argument expected.\\n");
输入下列命令:
$./a.out “testing1 testing2”
结果如下:
Progranm name ./a.out
The argument supplied is testing1 testing2
因为下述命令的参数只算一个,包括程序自身的名称,所以进入了第一个条件判断。
二、递归
递归指的是在函数的定义中使用函数自身的方法。
#include <stdio.h>
double factorial(unsigned int i)
if(i <= 1)
return 1;
return i * factorial(i - 1);
int main()
int i = 15;
printf("%d 的阶乘为 %f\\n", i, factorial(i));
return 0;
结果如下:
15 的阶乘为 1307674368000.000000
三、线程
是的,C语言提供了线程支持。C11标准引入了一组线程相关的库函数和头文件,称为“Thread library”。
使用这些函数和头文件,可以在C程序中创建多并发执行的线程来实现异步处理、并行计算等功能。
下面是一个简单的示例代码,用于创建多个线程并在每个线程中运行一个简单的任务:
#include <stdio.h>
#include <threads.h>
int my_thread_func(void* arg)
//1.arg是一个指向void类型的指针,表示不确定类型的数据地址。
//2.(int*)arg将arg强制转换为指向整型数据的指针
//这里假设arg所指向的内存储存了一个整型值。
//3.*(int*)arg表示通过该指针访问其所指向的内存
//并把其值作为整型数据返回给变量id。
int id = *(int*)arg;
printf("Thread %d is running", id);//打印线程ID
return 0;
int main()
const int num_threads = 4;//启动4个线程
thrd_t threads[num_threads];//在循环中被赋值,保存新建线程的ID号
int thread_args[num_threads];//参数1,2,3,4
for (int i = 0; i < num_threads; i++)
thread_args[i] = i;
//创建线程
thrd_create(&threads[i], my_thread_func, &thread_args[i]);
for (int i = 0; i < num_threads; i++) //遍历所有线程ID
thrd_join(threads[i], NULL);
return 0;
这段程序启动了4个线程,每个线程都执行同一个函数my_thread_func
,并把该函数所需的参数传递给线程。
具体解析如下:
-
首先定义了一个常量
num_threads
,值为4,表示要启动4个线程。 -
创建了两个数组:
threads
和thread_args
。前者存储线程标识符(即线程ID),后者存储传递给线程函数的参数。其中,数组长度都为num_threads
。 -
进入循环体,遍历数组
thread_args
中的每一个元素。此时数组内的元素值分别为0、1、2、3。这些值将被传递给4个线程作为参数。 -
在循环体内部,调用了
thrd_create()
函数来创建线程,并将其标识符存储在数组threads
中。该函数接受三个参数:
- 第一个参数是指向线程ID的指针,用于保存新建线程的ID号。
- 第二个参数是指向函数
my_thread_func()
的指针。当新建的线程开始运行时,它将执行这个函数。 - 第三个参数是指向要传递给新建线程的数据所在内存地址的指针。
- 退出第一个循环体后,进入第二个循环体。遍历数组
threads[]
, 调用thrd_join()
函数等待每个线程执行完成。该函数接受两个参数:
- 第一个参数是线程ID。
- 第二个参数为指向线程返回值的指针,因为这里用不到,所以传入了
NULL
。
- 所有线程执行完成后,程序退出并返回0。
最后,使用 thrd_join
函数在主线程中等待所有线程的结束并释放它们的资源。
值得注意的是,C语言标准库只提供了一些基本的线程操作函数和类型,例如创建、销毁、等待和同步线程等。
对于更高级别的功能,例如锁定和互斥、条件变量、信号量等需要使用其他库或实现。
那么C语言总结到这里基本就告一段落了,后面一些更高级的就以后在更新或者自己去学习吧。
Go语言-获取命令行参数
Go语言-获取命令行参数
1、flag库使用
Go语言标准库提供了用于快讯解析命令行参数的flag包,大致的使用步骤如下
a、通过flag.String(),flag.Bool(),flag.Int()等方式来定义命令行中需要使用的参数。
b、在定义完flag后,通过调用flag.Parse()来进行对命令行参数的解析。
c、获取flag.String(),flag.Bool(),flag.Int()等方法的返回值,即对应用户输入的参数。
需要注意的是flag.Xxx()返回的值是变量的内存地址,要获取值时要通过在变量前加*(星号)获取。
说明:
flag.Int、flag.Bool、flag.String这样的函数格式都是一样的,调用的时候需要传入3个参数
参数说明如下:
第一个arg表示参数名称,在控制台的时候,提供给用户使用。
第二个arg表示默认值,如果用户在控制台没用给该参数赋值的话,就会使用该默认值。
第三arg表示使用说明和描述,在控制台中输入-arg的时候会显示该说明,类似-help
看个完整的使用例子
package main import ( "flag" "fmt" ) func main() { married:=flag.Bool("married",false,"Are you married?") age:=flag.Int("age",22,"How old are you?") name:=flag.String("name","","What your name?") var address string //flag.StringVar这样的函数第一个参数换成了变量地址,后面的参数和flag.String是一样的。 flag.StringVar(&address,"address","GuangZhou","Where is your address?") flag.Parse() //解析输入的参数 fmt.Println("输出的参数married的值是:",*married)//不加*号的话,输出的是内存地址 fmt.Println("输出的参数age的值是:",*age) fmt.Println("输出的参数name的值是:",*name) fmt.Println("输出的参数address的值是:",address) }
运行结果:
输出的参数married的值是: false
输出的参数age的值是: 22
输出的参数name的值是:
输出的参数address的值是: GuangZhou
从运行结中可以看出,address参数我们并没有指定值,则输出的就是默认值
另外,-arg后面=号也不是必须的可以用空格代替。
如果要查看该程序的所有参数的使用,可以使用-help来查看
2、os库的使用
直接看例子
package main import ( "fmt" "os" ) func main() { args:=os.Args //获取用户输入的所有参数 if args==nil || len(args)<2{ Usage() //如果用户内有输入,或参数个数不够,则调用该函数提示用户 return } name:=args[1]; age:=args[2] fmt.Println("your name is:",name," your age is:",age) } var Usage= func() { fmt.Println("you name?") fmt.Println("you age?") }
运行结果:
you name?
you age?
个人感觉还是flag获取参数好用一点,因为flag更贴切参数的用法,而且可以查看详情。
最后补充
package main import "fmt" func main() { var name string ="abc" var nameAddress *string =&name //如果要接受变量的存储地址,变量对应的类型前面要加*号 fmt.Println("name 变量的存储地址:",&name) fmt.Println("nameAddress的值:",nameAddress) fmt.Println("&name==nameAddress?",&name==nameAddress) fmt.Println("nameAddress对应变量的值:",*nameAddress) //内存地址前面加*好可以看到对应的值了 fmt.Println("name变量的值:",*&*&name) //*号和&号可以多次组合来使用,阅读的时候从右往左 }
运行结果:
name 变量的存储地址: 0xc0000321f0
nameAddress的值: 0xc0000321f0
&name==nameAddress? true
nameAddress对应变量的值: abc
name变量的值: abc
-------------------------------------------------------------------------
[理论]
&符号的意思是对变量取地址,如:变量a 的地址是&a
*符号的意思是对指针取值,如:*&a,就是a变量所在地址的值,当然也就是a的值了
[简单的解释]
*和&可以互相抵消,同时注意,*&可以抵消掉,但&*是不可以抵消的 a和*&a是一样的,都是a的值,值为1(因为*&互相抵消掉了)
同理,a和*&*&*&*&a是一样的,都是1(因为4个*&互相抵消掉了)
以上是关于C语言关于我回头学的那些命令行参数等的主要内容,如果未能解决你的问题,请参考以下文章