LinuxC语言编程基本原理与实践(2018-06-1619:12:15)LinuxC语言编程基本原理与实践有目的高效学习:Whatis->Whattodo->如何使用C语言C语言是一种通用的、面向过程的程序设计语言,广泛应用于系统和应用软件的开发中。它是人类与计算机交流的一种方式。ANSIC:是C语言标准,为了避免不同开发者使用的C语言语法的差异。C语言的特点:简单、快速、高性能、兼容性好、功能强大、容易学什么C语言适用于嵌入式Linux、小工具(命令行下cd、ls等命令)小巧灵活,语法简单,适合小工具和硬件程序:操作系统、ARM嵌入式、单片机编程、Arduino编程等对性能要求高的应用:NGINX(C)concurrency=Apache(C++)*10C适合领域小工具(语法简单)和硬件相关程序ARM嵌入式、单片机、Arduino编程(带指针、可操作内存)高性能要求程序nginx:capache:c++linux嵌入式开发环境及配置:C语言是一种随着UNIXMac电脑的诞生而诞生的编程语言是Unix内核;Windows下可以安装Linux虚拟机Ubuntu:Ubuntu和CentOS是比较常用的Linux发行版,Ubuntu更适合个人电脑。kylin版的Ubuntu对中文的支持很好。amd64版本:AMD率先推出64位CPU,所以Ubuntu将64位CPU型号定义为amd64(Intel使用的),沿用至今;32位使用x86LTS版本:长期技术支持版本安装Ubuntu系统可以选择双系统,或者在原来的Windows电脑上安装虚拟机PS:尝试在Linux环境下开发C语言程序常用命令终端编辑器:emacsvim安装软件:sudoaptinstall[软件名称]更新软件:sudoaptupdateCtrl+Alt+T:打开终端cd~:进入当前用户根目录pwd:查看当前路径ls:包含哪些文件当前章节目录ls-l:显示当前文件类型、权限、创建时间、名称ls-alorll:如果d在前面则显示隐藏文件文件夹,-是一种常见的文件类型touch**:创建一个字符文件rm**:删除mkdir**:创建一个目录(文件夹)vi**:打开(输入)一个不存在的文件vi,进入后无法输入内容,因为当前处于命令模式;按字符i,可以进入INSERT插入模式,可以输入内容,按Esc返回命令模式;命令模式下::w:保存文件:q:exiti:当前插入字符Shift+i光标前:跳转到当前行行首插入字符a:在当前光标后插入字符Shift+a:跳转到当前行末插入字符o:在当前下一行插入字符Shift+o:在当前上一行插入字符x:删除当前光标所在的字符d+d:删除整行最好Linux下的文本编辑器:emacs、vimcc-v(gcc-v):查看编译器版本apt-get是一个linux命令,适用于deb包管理操作系统,主要用于自动搜索、安装、升级、卸载软件或操作systemsfromInternetsoftwarewarehousesclear:cleanscreenlinux下第一个C程序一般不用voidmainlinux下,最新的C语言标准,intmain#includeintmain(){printf("hello,world!\n");return0;}cca.c默认会为我们编译生成一个可执行文件a.out(可读可写可执行)./表示在当前路径下,./a.out执行当前路径下的a.out文件r表示可读w表示可写x表示可执行三个重复的顺序是“创建者”、“用户组”、“任何其他用户”多源文件分而治之C语言是一种支持多种功能的结构化编程语言。一个程序可以包含多个函数。最原始版本的vimhello.c(hello.c)的实现:#includeintmax(inta,intb){if(a>b){returna;}else{返回b;}}intmain(){inta1=33;inta2=21;intmaxNum=max(a1,a2);printf("最大值为%d\n",maxNum);return0;}我们的stdio.h是在我们的user/include中构建的,在写max函数的时候对齐,内部写的时候括号缩进对齐。补充知识:vim分屏显示:sp文件名//创建(打开)屏幕上的新文件:ctrl+w+向上箭头向下屏幕:ctrl+w+向下箭头打开行号:setnucut:(lastlinenumber)+dd粘贴:p//这两个不需要冒号结束行号:setnonu上面的代码如果直接编译会报错。这是一个未声明的函数。分离方案有两种:第一种是intmax(inta,intb);,在hello.c中声明方法,然后编译时需要加上max.c;另一种是#include"max.c"那么编译版本1:0-hello.c不需要加max.c:#includeintmax(inta,intb);intmain(){inta1=33;inta2=21;intmaxNum=max(a1,a2);printf("最大值为%d\n",maxNum);return0;}0-max.c:intmax(inta,intb){if(a>b){返回a;}else{返回b;}}编译命令:gcc0-hello.c0-max.c-o0-hello.out如果不加0-max.c编译会报错gcc0-hello.c-o0-hello.out/tmp/cc8GuaAH.o:在函数“main”中:0-hello.c:(.text+0x21):未检查“max”定义的引用collect2:错误:ld返回1退出状态版本21-hello.c:#include#include"0-max.c"intmain(){inta1=33;inta2=21;intmaxNum=max(a1,a2);printf("最大值为%d\n",maxNum);return0;}0-max.c与原来的编译命令一致:gcc1-hello.c-o1-hello.out如果此时加上0-max.c一起编译gcc1-hello.c0-max.c-o1-hello.out/tmp/ccjcCmVa.o:infunction'max':0-max.c:(.text+0x0):`max'被定义多次/tmp/cclIxMtD.o:1-hello.c:(.text+0x0):第一次在这里定义collect2:error:ldreturned1exitstatus终端下:gccfilename.c-oname.outgenerate.outandname#include<>表示在pre-中找#include安装库“max.c”表示在当前目录下搜索文件。include"max.c"相当于把整个函数copy进去,效果相当于写进去,wqa是把多个文件放在一起保存。头文件与函数定义分离,将函数声明和定义分离,没有main函数就无法执行代码,而main是入口点。.h头文件.o编译后的中间文件.c源码mtianyan@ubuntu:~/Desktop/zjuPlan/CSF878/CCode/linux_c/2-lesson/part1$ls0-max.c1-hello.c加快编译gcc-c0-max.c-o0-max.o把max.c改成max.o后,我们需要把hello.c中的include注释掉,增加一个方法声明#include//#include"0-max.c"intmax(inta,intb);可读可写不可执行,max.o相当于一个计算器,将源代码翻译成计算机可识别的机器码gcc0-max.o1-hello.c-o1-hello.out创建一个新的min.cintmin(inta,intb){if(a中调用minNum//#include"0-max.c"intmax(inta,intb);intmin(inta,intb);intmain(){inta1=33;inta2=21;intmaxNum=max(a1,a2);intminNum=min(a1,a2);printf("最大值为%d\n",maxNum);printf("最小值为%d\n",minNum);return0;}编译命令:gcc-cmin.c-omin.ogcc0-max.omin.o1-hello.c-o2-hello.out加快编译速度。不再修改的函数,公共框架和公共类编译生成静态库。gcc的编译过程分为4个步骤:预处理(Pre-Processing)->编译(Compling)->汇编(Assembling)->链接(Linking)将预处理后的文件编译成汇编程序.sassembly:compileassembler。sinto.obinaryfilegcc-cmin.c-omin.o//预编译文件min.c为文件min.ocpfileNameafileb//copyfileaintoanewfileb使用yy复制一行使用行号n+yy复制n行使用p粘贴复制的行使用cat命令查看源码文件那么问题又来了,我们现在的max函数和min函数都是自己写的。即使我们不声明,我们仍然知道需要哪些参数,参数是什么类型,返回值是什么类型。如果这个函数不是我们写的,别人已经写成了max.omin.o。我们看不到源码,也不知道函数的传入参数和返回。.h文件的好处来了。我们创建一个文件夹part2max.h代码:intmaxNum(inta,intb);min.h代码:intminNum(inta,intb);hello.c代码:#include#include"max.h"#include"min.h"intmain(){inta1=33;inta2=21;intmaxNum=max(a1,a2);intminNum=min(a1,a2);printf("最大值为%d\n",maxNum);printf("最小值是%d\n",minNum);}gccmax.omin.ohello.c-ohello.outwarning:函数'max'的隐式声明;您指的是'main'吗?出现警告,但不影响正常编译运行。makFile的make工具可以把一个大型的开发项目分成几个模块make-vsudoapt-getinstallmake工具可以非常清晰快速的组织源文件使用make和makeinstall这两个命令因为当我们有很多源文件,gccmax.cmin.chello.c-ohello.out命令会很长。写一个Makefile可以同时编译多个文件,告诉依赖关系。vimMakefile写道:hello.out:max.omin.ohello.cgccmax.omin.ohello.c-ohello.outmax.o:max.cgcc-cmax.cmin.o:min。cgcc-cmin.c#添加注释执行命令make写入Makefile使用tab键缩进(八个空格,否则报错)Makefile:3:***missingdelimiter(null)。停止。重命名文件mvMakeFile从上到下搜索makefile,从下到上编译。已经生成的文件不会重新生成。make:"hello.out"是最新的。(最新)详细解释main函数中返回值的作用和main函数中参数的含义main.c:#includeintmain(intargc,char*argv[])//主要功能完成Theform{printf("hello,world\n");return101;}gccmain.c-omain.out&&./main.outgccmain.c-omain.out&&./main.out可以执行两条命令return0用于验证程序是否运行成功.命令回显$?用于查看返回值。/main.out&&ls打印helloworld并列出当前目录。因为main.out的返回值为0,所以判断执行成功。./main2.out&&ls打印出helloworld,没有列出当前目录。因为main.out的返回值为101(不是0),所以判断为执行不成功。主函数中的参数intmain(intargc,char*argv[])//主函数argc的完整形式统计输入参数个数。只输入文件名时,数字为1。argv[]存放各个参数的内容,如:输入./main3.out-l-aargv=3argc[0]=./main.outargc[1]=-largc[2]=-aargc(argumentcount),存放传入main函数的参数个数。argv(argumentvector),存放具体的传入参数//内部我;for(inti=0;iintmain(){//printf("helloworld!\n");fprintf(stdout,"你好世界\n");诠释;//扫描("%d",&a);fscanf(标准输入,“%d”,&a);if(a<0){fprintf(stderr,"该值必须>0\n");#如果返回值不等于0,说明函数有错误return1;}//printf("输入值为:%d\n",a);return0;}输出流和错误流的重定向linux几乎可以用在任何领域,这里不得不提一个linuxchannel。管道起着非常重要的作用。需要流水线来配合不同的应用程序。Demo:main.c先了解输入流、输出流、错误流的重定向机制,理解管道会更容易。#includeintmain(){inti,j;printf("输入int值i:\n");\\printf实际上是从标准输出流(即stdout)中封装了fprintf输出这个过程scanf("%d",&i);//默认输入流为键盘printf("inputtheintvaluej:\n");scanf("%d",&j);printf("i+j=%d\n",i+j);}执行编译命令ccmain.c,获取a.out,运行a.out,我们分别在终端输入3和5。我们可以使用命令./a.out1>>a.txt,其中>>符号(输出流如果不写参数就是输出流),之前默认输出流是终端,现在我们改一下输出到a.txt,我们执行命令后,分别输入3,再回车回车5。再次使用命令cata.txt,我们可以看到输出到文件中的内容。我们的标准输出流是1>>,输入流是0>>。我们再次执行./a.out>>a.txt,再次输入参数。完成后,我们再次使用cat查看a.txt文件的内容。发现之前的内容还在,后面追加了新的输出内容。再举一个重定向的例子,我们使用命令ls/etc>>etc.txt,我们将ls目录下的内容输入到etc.txt文件中,但是如果我们改变重定向符号覆盖之前的内容,我们就可以把双箭头>>改成单箭头>,那么文件中之前的内容就会被覆盖。输入流重定向我们可以创建一个文件viinput.txt,内容如下:189我们再次执行./a.outintmain(){inti,j;printf("输入整数值i:\n");scanf("%d",&i);//默认的输入流是键盘printf("inputtheintvaluej:\n");scanf("%d",&j);if(0!=j){printf("%d/%d=%d\n",i,j,i/j);}else{fprintf(stderr,"j!=0\n");返回1;}return0;}注意代码规范提示,比较0和j,等号省略则把0放在前面报错echo$?检查返回值。/main.out1>true.txt2>false.txtintmain(){ints,n;scanf("%d,%d",&s,&n);浮动v=s/n;printf("v=%f\n",v);return0;}编译命令:ccavg.c-oavg.outinput.c:#includeintmain(){intflag=1;诠释我;整数=0;整数计数=0;while(标志){scanf("%d",&i);如果(0==i)中断;#等于0跳出count++;s+=我;}printf("%d,%d\n",s,计数);return0;}ccinput.c-oinput.out注意上面代码中s要初始化为0。否则,就会出现问题。使用时:./input.out|./avg.out实现输入数据之间的平均值。