前言
本文主要包含,c语言基本结构与语法、make及makefile的使用、main函数参数与返回值的说明、标准输入、输出、错误流的介绍以及linux管道的应用。
语言数据类型
联合体也有翻译为共用体的,结构类型,也有翻译为结构体的,仅仅是翻译问题,类型是一样的
每个变量都会占用一定的存储单元。
符号常量
100 // 直接常量"Hello"#define PI 3.14 //符号常量
自动类型转换
强制类型转换
在使用强制转换时应注意以下问题:
1、数据类型和表达式都必须加括号,如把(int)(x/2+y)写成(int)x/2+y则成了把x转换成int型之后再除2再与y相加了。
2、转换后不会改变原数据的类型及变量值,只在本次运算中临时性转换。
3、强制转换后的运算结果不遵循四舍五入原则。
计算顺序
int a = 3; b = 4;计算a+b%(a+b)/a+a*b-b该算式的运算结果
计算顺序
1、a+b
2、b%(a+b)/a和ab
3、a+b%(a+b)/a+ab-b
小的数字mod 大的数字,余数还是其本身。
运算符优先级
爆破彗星是个什么样的游戏?
C语言变量存储类别
C语言根据变量的生存周期来划分,可以分为静态存储方式和动态存储方式。
静态存储方式:是指在程序运行期间分配固定的存储空间的方式。静态存储区中存放了在整个程序执行过程中都存在的变量,如全局变量。
动态存储方式:是指在程序运行期间根据需要进行动态的分配存储空间的方式。动态存储区中存放的变量是根据程序运行的需要而建立和释放的,通常包括:函数形式参数;自动变量;函数调用时的现场保护和返回地址等。
C语言中存储类别又分为四类:自动(auto)、静态(static)、寄存器的(register)和外部的(extern)。
1、用关键字auto定义的变量为自动变量,auto可以省略,auto不写则隐含定为“自动存储类别”,属于动态存储方式。
2、用static修饰的为静态变量,如果定义在函数内部的,称之为静态局部变量;如果定义在函数外部,称之为静态外部变量。函数的形式参数不可以说明为satic类型。
注意:静态局部变量属于静态存储类别,在静态存储区内分配存储单元,在程序整个运行期间都不释放;静态局部变量在编译时赋初值,即只赋初值一次;如果在定义局部变量时不赋初值的话,则对静态局部变量来说,编译时自动赋初值0(对数值型变量)或空字符(对字符变量)。
3、为了提高效率,C语言允许将局部变量得值放在CPU中的寄存器中,这种变量叫“寄存器变量”,用关键字register作声明。
注意:只有局部自动变量和形式参数可以作为寄存器变量;一个计算机系统中的寄存器数目有限,不能定义任意多个寄存器变量;局部静态变量不能定义为寄存器变量。
4、用extern声明的的变量是外部变量,外部变量的意义是某函数可以调用在该函数之后定义的变量。
#include <stdio.h>void fn(){static int x = 1; //定义静态局部变量x*=2;printf("x=%d\n",x);}int main(){int i;for(i=0;i<5;i++){fn();}extern int x;//调用外部变量printf("x=%d\n",x);return 0;}int x=100;
内部函数与外部函数
人本身是有自己的特定方法的,比如当你说话的时候,不希望是别人让你怎么说你就怎么说吧,那么这种不能被外人调用的方法称为人的内部方法。人本身还有一些可以调配的方法,比如当你家人跟你说,家里没有盐了,你去买袋盐,去买盐就是他人调用你的方法,那么能被外人调用的方法称谓外部方法。
在C语言中不能被其他源文件调用的函数称谓内部函数 ,内部函数由static关键字来定义,因此又被称谓静态函数,形式为:
static [数据类型] 函数名([参数])
这里的static是对函数的作用范围的一个限定,限定该函数只能在其所处的源文件中使用,因此在不同文件中出现相同的函数名称的内部函数是没有问题的。
在C语言中能被其他源文件调用的函数称谓外部函数 ,外部函数由extern关键字来定义,形式为:
extern [数据类型] 函数名([参数])
C语言规定,在没有指定函数的作用范围时,系统会默认认为是外部函数,因此当需要定义外部函数时extern也可以省略。
字符串函数
注:C语言中不存在字符串变量,字符串只能存在字符数组中。使用这个工具类#include
多维数组的定义
数组:是一块连续的,大小固定并且里面的数据类型一致的内存空间,在声明数组后没有进行初始化的时候,静态(static)和外部(extern)类型的数组元素初始化元素为0,自动(auto)类型的数组的元素初始化值不确定。另外,二维数组一定要指定列数。
int x[][3] = {{0},{1},{1,2,3}};int x[4][3] = {{1,2,3},{1,2,3},{1,2,3},{1,2,3}};int x[][3] = {1,2,3,4};
linux嵌入式开发
linux嵌入式开发,其实就是用C语言开发的小工具。(例如cd、ls啥的都是linux嵌入式程序),另外,还可以用C语言开发与硬件打交道的程序(例如os(win、linux、ios、android)、arm嵌入式、单片机、arduino),这是因为,C语言有指针,可以直接操纵内存,因此,可以做硬件编程。另外,C语言开发的程序很搞效,nginx就是c写的,apache是c++写的,nginx更加高效。
开发环境
mac、linux(ubuntu、centos),ubuntu之所以有amd64,是因为,amd是最早推出64版本的cpu的,因此,沿用了这个说法,现在intel的cpu也是支持ubuntu的。
1.检查编译器:cc -v = gcc -v,gcc name1.c name2.c -o xxx.out
2.编译 gcc -c 文件名 -o 文件名.o
3.gcc max.o hello.c
2.创建文件:touch 文件名
-rwxrwxr-x
其中第一组表示,当前用户,第二组表示当前组,第三组表示不是当前用户,也不是当前组
vim
1.shift + i 行首
2.shift + a 行尾
3.o换行
4.shift + o上一行插入
4.dd整行删除
5.x删除
6.space * 4 = tab
7.sp打开多个vim界面
8.ctrl + w+下箭头,就跳转到了下边的窗口
9.set nu打开行号
全部格式化 : gg=G
对当前行格式化(缩进): ==
对以下多行格式化(倍数操作): [count] ==
选择多行后, 执行 等号命令 =
开发
helloworld.c
gcc max.o min.o helloworld.c -o helloworld.o
#include <stdio.h> // 标准的输入输出流#include "max.h"#include "min.h"// 或者把头文件里的定义放到一个文件中 int main(int argv,char* argc[]) // 不要写 void了,返回int有意义{int leftnum = 14;int rightnum = 57;int maxnum = max(leftnum ,rightnum );int minnum = min(leftnum ,rightnum );printf("Hello World\n");printf("max num is %d\n",maxnum);printf("max num is %d\n",minnum );return 0;}
main函数的返回值
在linux下,可以通过echo $?,来查看之前执行的程序的返回值,只有返回值为0,才表示执行成功,否则,就是执行失败。
echo $?0
因此,return 0 ,部署随便写的,是有意义的。
main函数的参数
通过参数,可以赋值给不同的参数选项
#include <stdio.h> // 标准的输入输出流int main(int argv,char* argc[]) // 不要写 void了,返回int有意义{printf("argv is %d\n",argv);int i;for(i=0;i<argv;i++){printf("argc[%d] is %s\n",i,argc[i] );}return 0;}gcc arytes.c -o arykill.o./arykill.o -8 // 操作系统向程序传递参数argv is 2argc[0] is ./arykill.oargc[1] is -8
max.c
gcc -c max.c -o max.o
int max(int a,int b){if(a>b){return a;}else{return b;}}
min.c
gcc -c min.c -o min.o
int min(int a,int b){if(a<b){return a;}else{return b;}}
max.h
int max(int a,int b);
min.h
int min(int a,int b);
结果
Hello Worldmax num is 57max num is 14
C语言编译、组合多文件,自建头文件
只需要编译 带有main函数的那个文件,把不会再修改的函数,放到公共框架内,公共类内,并生成静态库,打包放在一起。
makeFile
用make进行编译,make内部其实使用的也是gcc。通过make把大型开发项目分成若干个模块。但是,一般情况下,都是使用makefile文件,来进行结构化构建。
对于上方的c语言代码,我们来进行makefile的构建
# this is make filetestmake.o:max.o min.o helloworld.cgcc max.o min.o helloworld.c -o testall.omax.o:max.cgcc -c max.cmin.o:min.cgcc -c min.c
结果
make && lsgcc -c max.cgcc -c min.cgcc max.o min.o helloworld.c -o testall.outMakefile all.h helloworld.c ji.c max.c max.h max.o min.c min.h min.o testall.c testall.out./testall.out Hello Worldmax num is 57max num is 14
linux嵌入式标准流
linux介文件
启动每一个c语言程序,只要include ,就会自动开启三个处理程序的文件工具stdin(标准输入流,读)、stdout(标准输出流,写,默认指向显示器)、stderr(标准错误流,报错)几个文件。
stdin、stdout、stderr都是文件,其实输入、输出、报错,就是看操作的文件是啥就可以了,都有默认的指向。
#include <stdio.h>int main(){printf("hello world!\n");fprintf(stdout,"please input the value a:\n");int a;scanf("%d",&a);//&a表示取a的地址。fscanf(stdin,"%d",&a);printf("input value is : %d\n",a);if(a<0){fprintf(stderr,"the value must > 0\n");return 500;}return 0;}
流的重定向
重定向到文件,可以多次写入到该文件中./a.out 1>> a.txtls /etc >> etc.txt重定向到文件,覆盖写入到该文件中ls /etc > etc.txt重定向到文件,1输出流,2代表错误输出流./a.out 1>t.txt 2>f.txt重定向文件,输入流<input.txt./a.out 1>t.txt 2>f.txt <input.txt
管道
|表示一个管道
grep 从输入(流)的文本文档中,查询包含指定字符的行,并显示到输出流中
ls /etc/ | grep aa
ps -e | grep ssh
通过管道,将前一个结果的输出流,作为后一个程序的输入流。
编写应用程序并使用管道
自己可以编写两个程序,让其中一个的输入作为下一个的输出
前
#include <stdio.h>int main(){int count =0;int s;int w = 0;int flag = 1;while(flag){scanf("%d",&s);if(0==s)break;count++;w+=s;// printf("%d,%d\n",s,count); 切记,只能有一个输出,否则记录的是一次的输出,并将其传到管道中}printf("%d,%d",w,count);return 0;}
后
#include <stdio.h>int main(){int s,n;scanf("%d,%d",&s,&n);float result = s/n;printf("%d个人的平均值是%f\n",n,result);return 0;}
结果
./f.o | ./a.out 30005000900012345678999906个人的平均值是5651.000000
附录
闰年
#include <stdio.h>int main() {int year = ; //今年是//补全一下代码if(year%4==0&&(year%100!=0||year%400==0)){printf("今年是闰年");}else{printf("今年是平年");}return 0;}
4年一闰,百年不闰,400年再闰(对于数值很大的年份,如果这年能整除3200并且能整除172800则是闰年)
水仙花数
一个三位数,其各位数字立方和等于该数
#include <stdio.h>int main(){//定义三位数num,个位数sd,十位数td,百位数hdint num, sd, td, hd;//循环所有三位数for( num =100 ; num<1000 ; num++ ){//获取三位数字num百位上的数字hd = num /100 ;//获取三位数字num十位上的数字td = (num - hd*100)/10;//获取三位数字num个位上的数字sd = num -hd*100 -td*10 ;//水仙花数的条件是什么?if(hd*hd*hd + td*td*td + sd*sd*sd==num ) {printf("水仙花数字:%d\n", num); }}return 0; }
输出个三角
#include <stdio.h>int main(){int i, j, k;for(i=1; i<5; i++){/* 观察每行的空格数量,补全循环条件 */for(j=i;j<5;j++) {printf(" "); //输出空格}/* 观察每行*号的数量,补全循环条件 */for( k=0 ; k<i*2-1 ; k++ ) {printf("*"); //每行输出的*号}printf("\n");//每次循环换行}return 0;}****************
乘法梯形
#include <stdio.h>int main() { int i, j, result;for(j=9;j>0;j--){for(i=1;i<=j;i++){printf("%d*%d=%d",j,i,j*i);printf(" ");}printf("\n"); }return 0;}9*1=9 9*2=18 9*3=27 9*4=36 9*5=45 9*6=54 9*7=63 9*8=72 9*9=81 8*1=8 8*2=16 8*3=24 8*4=32 8*5=40 8*6=48 8*7=56 8*8=64 7*1=7 7*2=14 7*3=21 7*4=28 7*5=35 7*6=42 7*7=49 6*1=6 6*2=12 6*3=18 6*4=24 6*5=30 6*6=36 5*1=5 5*2=10 5*3=15 5*4=20 5*5=25 4*1=4 4*2=8 4*3=12 4*4=16 3*1=3 3*2=6 3*3=9 2*1=2 2*2=4 1*1=1
找出素数(质数)
#include <stdio.h>int main(){int m, n;for(m=2; m<=50; m++){for(n=2; n<m; n++){if( m%n==0 ) //什么条件下跳出当前循环break;//这里应该退出当前循环了} if(m == n) //n循环结束后,如果m=n的话就输出mprintf("%d ", m);}return 0; }
递归
递归就是一个函数在它的函数体内调用它自身。
计算n的阶乘
#include <stdio.h>int factorial(int n){int result;if(n<0){printf("输入错误!\n");return 0;}else if(0==n||1==n){return 1;}else{result =factorial(n-1)*n;}return result;}int main(){int n=6;printf("%d的阶乘=%d\n",n,factorial(n));return 0;}
递归函数特点
每一级函数调用时都有自己的变量,但是函数代码并不会得到复制,如计算5的阶乘时每递推一次变量都不同;
每次调用都会有一次返回,如计算5的阶乘时每递推一次都返回进行下一次;
递归函数中,位于递归调用前的语句和各级被调用函数具有相同的执行顺序;
递归函数中,位于递归调用后的语句的执行顺序和各个被调用函数的顺序相反;
递归函数中必须有终止语句。
一句话总结递归:自我调用且有完成状态。
例如:
猴子第一天摘下N个桃子,当时就吃了一半,还不过瘾,就又多吃了一个。第二天又将剩下的桃子吃掉一半,又多吃了一个。以后每天都吃前一天剩下的一半零一个。到第10天在想吃的时候就剩一个桃子了,问第一天共摘下来多少个桃子?并反向打印每天所剩桃子数。
#include <stdio.h>int getPeachNumber(int n){int num; //定义所剩桃子数if(n==10){return 1;//递归结束条件} else{num = (getPeachNumber(n+1)+1)*2; //这里是不应该用递归呢?printf("第%d天所剩桃子%d个\n", n, num); //天数,所剩桃子个数}return num;}int main(){int num = getPeachNumber(1);printf("猴子第一天摘了:%d个桃子。\n", num);return 0;}第9天所剩桃子4个第8天所剩桃子10个第7天所剩桃子22个第6天所剩桃子46个第5天所剩桃子94个第4天所剩桃子190个第3天所剩桃子382个第2天所剩桃子766个第1天所剩桃子1534个猴子第一天摘了:1534个桃子。
做递归的问题,关键是找到突破口。再比如:
有5个人坐在一起,问第5个人多少岁?他说比第4个人大2岁。问第4个人岁数,他说比第3个人大2岁。问第3个人,又说比第2人大两岁。问第2个人,说比第1个人大两岁。最后 问第1个人,他说是10岁。请问第5个人多大?
#include <stdio.h> int guessAge(int n){int _age;if(n==1){return 10;}else{_age = guessAge(n-1) + 2;}return _age;}int main() {int _age = guessAge(5);printf("第5个人的年龄是%d岁", _age); return 0;}
每日学习单词
#include <stdio.h>/* 定义获取单词数量的函数 */int getWordNumber(int n){ if(n == 1){return 1; //第一天只会1个单词}else{return getWordNumber(n-1)+n ; //到第天会的单词数量}}int main(){int num = getWordNumber(10);//获取会了的单词数量printf("小明第10天记了:%d个单词。\n", num);return 0;}
获取数组长度
#include <stdio.h>int main(){int arr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};int i = 0;int length = sizeof(arr)/sizeof(arr[0]);for(i = 0;i<length;i++){printf("%d\n",arr[i]);}return 0;}
字符串数组
使用%c和%s的技巧不一样,需要注意。
#include <stdio.h>/* 定义say函数 */void say(char string[]) //数组参数应该怎么写呢?{printf("%s\n",string);}int main(){//定义字符串数组char string[] = "我在慕课网上学习IT技能!";int i = 0;//for(;i<sizeof(string)/sizeof(string[0]);i++)//{say(string); //调用say函数输出字符串//}return 0;}
做个排序的题
在一个长度为10的整型数组里面,保存了班级10个学生的考试成绩。要求编写5个函数,分别实现计算考试的总分,最高分,最低分,平均分和考试成绩降序排序。
排序请用多种排序方式,此处,没有实现c语言的数组返回,需要继续深入学习,后续补充这个文章。
#include <stdio.h>double _Avg(int score[],int _length){int i = 0;double sum = 0;for(;i<_length;i++){sum+=score[i];}if(_length!=0){return sum/_length;}else{return 0.0;}}int* _Desc(int score[],int _length){// int i = 0;// int _desc_score[];// for(;i<_length;i++){//_desc_score[i]=score[i];// }return score;}int main(){int score[]={67,98,75,63,82,79,81,91,66,84};int _length = sizeof(score)/sizeof(score[0]);printf("总分是:%d\n",_Total(score,_length));printf("最高分是:%d\n",_Max(score,_length));printf("最低分是:%d\n",_Min(score,_length));printf("平均分是:%f\n",_Avg(score,_length));printf("NULL-Avg是:%d\n",_Avg(score,NULL));int _desc_score[] =_Desc(score,_length);printf("考试成绩降序排序是:");int j = 0;for(;j<_length;j++){printf("%d,",_desc_score[j]);}printf("\n");return 0;}int _Total(int score[],int _length){int i = 0;int sum = 0;for(;i<_length;i++){sum+=score[i];}return sum;}int _Max(int score[],int _length){int i = 0;int _max = 0;for(;i<_length;i++){if(_max<=score[i]){_max = score[i];}}return _max;}int _Min(int score[],int _length){int i = 0;int _min = score[0];for(;i<_length;i++){if(_min>=score[i]){_min = score[i];}}return _min;}
计算一年中的第几天
#include <stdio.h>int main() { /* 定义需要计算的日期 */int year = ;int month = 8;int day = 8;/** 请使用switch语句,if...else语句完成本题* 如有想看小编思路的,可以点击左侧任务中的“不会了怎么办”* 小编还是希望大家独立完成哦~*/int sum = 0;int i=0;for(i = 0;i<month;i++){switch(i){case 1:sum+=31;break;case 2:sum+=28;break;case 3:sum+=31;break;case 4:sum+=30;break;case 5:sum+=31;break;case 6:sum+=30;break;case 7:sum+=31;break;case 8:sum+=31;break;case 9:sum+=30;break;case 10:sum+=31;break;case 11:sum+=30;break;case 12:sum+=31;break;}} if(year%4==0&&(year%100!=0||year%400==0)){sum+=1;}printf("8月8日是该年的第%d天。",sum+day);return 0;}8月8日是该年的第221天。
遗留问题
1.函数如何返回数组或者对象。
2.static静态的,在C语言中如何深入理解。
3.排序算法和查找算法。