前言
我的第一门语言就是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,并把该函数所需的参数传递给线程。
具体解析如下:
1.首先定义了一个常量num_threads,值为4,表示要启动4个线程。
2.创建了两个数组:threads和thread_args。前者存储线程标识符(即线程ID),后者存储传递给线程函数的参数。其中,数组长度都为num_threads。
3.进入循环体,遍历数组thread_args中的每一个元素。此时数组内的元素值分别为0、1、2、3。这些值将被传递给4个线程作为参数。
4.在循环体内部,调用了 thrd_create() 函数来创建线程,并将其标识符存储在数组 threads 中。该函数接受三个参数:
● 第一个参数是指向线程ID的指针,用于保存新建线程的ID号。
● 第二个参数是指向函数 my_thread_func() 的指针。当新建的线程开始运行时,它将执行这个函数。
● 第三个参数是指向要传递给新建线程的数据所在内存地址的指针。
5.退出第一个循环体后,进入第二个循环体。遍历数组 threads[], 调用 thrd_join() 函数等待每个线程执行完成。该函数接受两个参数:
● 第一个参数是线程ID。
● 第二个参数为指向线程返回值的指针,因为这里用不到,所以传入了 NULL。
6.所有线程执行完成后,程序退出并返回0。
最后,使用 thrd_join 函数在主线程中等待所有线程的结束并释放它们的资源。
值得注意的是,C语言标准库只提供了一些基本的线程操作函数和类型,例如创建、销毁、等待和同步线程等。
对于更高级别的功能,例如锁定和互斥、条件变量、信号量等需要使用其他库或实现。
那么C语言总结到这里基本就告一段落了,后面一些更高级的就以后在更新或者自己去学习吧。