前言
本文主要包含,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 <string.h>
多维数组的定义
数组:是一块连续的,大小固定并且里面的数据类型一致的内存空间,在声明数组后没有进行初始化的时候,静态(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 2
argc[0] is ./arykill.o
argc[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 World
max num is 57
max num is 14
只需要编译 带有main函数的那个文件,把不会再修改的函数,放到公共框架内,公共类内,并生成静态库,打包放在一起。
makeFile
用make进行编译,make内部其实使用的也是gcc。通过make把大型开发项目分成若干个模块。但是,一般情况下,都是使用makefile文件,来进行结构化构建。
对于上方的c语言代码,我们来进行makefile的构建
# this is make file
testmake.o:max.o min.o helloworld.c
gcc max.o min.o helloworld.c -o testall.o
max.o:max.c
gcc -c max.c
min.o:min.c
gcc -c min.c
结果
make && ls
gcc -c max.c
gcc -c min.c
gcc max.o min.o helloworld.c -o testall.out
Makefile 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 World
max num is 57
max num is 14
linux嵌入式标准流
linux介文件
启动每一个c语言程序,只要include <stdio.h>,就会自动开启三个处理程序的文件工具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.txt
ls /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
3000
5000
9000
1234
5678
9999
0
6个人的平均值是5651.000000
附录
闰年
#include <stdio.h>
int main()
{
int year = 2014; //今年是2014年
//补全一下代码
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,百位数hd
int 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的话就输出m
printf("%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 = 2008;
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("2008年8月8日是该年的第%d天。",sum+day);
return 0;
}
2008年8月8日是该年的第221天。
遗留问题
1.函数如何返回数组或者对象。
2.static静态的,在C语言中如何深入理解。
3.排序算法和查找算法。