翁凯C语言学习笔记

一、翁凯C语言

1.定义宏常量的两种方式

const int AMOUNT=100;
or
#define AMOUNT 100

2.浮点数

//float是单精度浮点数
//double是双精度浮点数

3.数据类型

//整数
int
printf("%d",...)
scanf("%d",...)
//带小数点的数
double
printf("%f",...)
scanf("%lf",...)
//浮点数输入必须要用%lf

4.复合赋值

total+=5;
equal
total=total+5;
//
total+=(sum+100)/2;
equal
total=total+(sum+100)/2;
//
total*=sum+12;
equal
total=total*(sum+12);
//
total/=12+6;
equal
total=total/(12+6);

++和–

//++和--可以放在变量的前面,叫做前缀形式,也可以放在变量的后面,叫做后缀形式。
//a++的值是a加1以前的值,而++a的值是加了1以后的值,无论哪个,a自己的值都加了1了。
#include
int main()
{
	int a=10;
	printf("a++=%d\n",a++);
	printf("a=%d\n",a);
	printf("++a=%d\n",++a);
	printf("a=%d\n",a);
	return 0;
}
//输出结果
/*
a++=10
a=11
++a=12
a=12
*/

表达式:count++ 运算:给count加1 表达式的值:count原来的值
表达式:++count 运算:给count加1 表达式的值:count+1以后的值
5.关系运算的结果

//当两个值得关系符合关系运算符的逾期时,关系运算的结果为整数1,否则为整数0
printf("%d\n",5==3);
printf("%d\n",5>3);
printf("%d\n",5<=3);
//输出结果
0
1
0

6.关系运算的优先级
所有的关系运算符的优先级比算术运算的低,但是 比赋值运算的高

//7>=3+4;
//int r=a>0;
#include
int main(){
	printf("%d\n",7>=3+4);
	int a=5;
	int r=a>0;
	printf("r=%d\n",r);
	return 0;
//输出结果
1
r=1

判断是否相等的==和!=的优先级比其他的低,而连续的关系运算是从左到右进行的

/*
5>3==6>4
6>5>4
a==b==6
a==b>0
*/
#include
int main()
{
	printf("%d\n",5>3==6>4);
	printf("%d\n",6>5>4);
	int a,b=5;
	printf("%d\n",a==b==6);
	printf("%d\n",a==b>0);
	return 0;
	}
//输出结果
1
0
0
1

7.if语句

//当if或者else语句后没有大括号的时候,只有后面那一句话是有效的
//计算薪水
#include
int main(){
	const double RATE=8.25;
	const int STANDARD=40;
	double pay=0.0;
	int hours;
	printf("请输入工作的小时数:");
	scanf("%d",&hours);
	if(hours>STANDARD)
		pay=STANDARD*RATE+(hours-STANDARD)*(RATE*1.5);
	else
		pay=hours*RATE;
	printf("应付工资:%f\n",pay);
	return 0;
}

8.switch-case

switch(控制表达式){
	case 常量:
		语句
		.....
	case 常量:
		语句
		.....
	case 常量:
		语句
		.....
	default:
		语句
		.....
}
//这个控制表达式只能是整数型的结果
//常量可以是常数,也可以是常数计算的表达式
/*case 1+1:
or
*/
/*const int MRN=2;
case MRN:
*/

break作用就是如果没有break就会顺序执行到下面的case里去,直到遇到一个break,或者switch结束为止

#include 
int main(){
	int type;
	scanf("%d",&type);
	switch(type){
		case 1:
		case 2:
			printf("hello");
			break;
		case 3:
			printf("good morning\n");
		case 4:
			printf("goodbye");
			break;
		default:
			printf("what's up");
			break;
	}
	return 0;
}
//输出结果
1
hello
2
hello
3
good morning
goodbye
//NOTE:case只是一个入口,break才是出口

9.while循环和do while循环

//判断一个正数是几位数
#include
int main(){
	int x;
	int n=0;
	scanf("%d",&x);
	n++;
	//x/=10依次划去末尾的数
	x/=10;
	while(x>0){
		n++;
		x/=10;
		}
	printf("%d\n",n);
	return 0;
}

在进入循环的时候不做检查,而是在执行完一轮循环体的代码之后,再来检查循环的条件是否满足,如果满足则继续下一轮循环,不满足则结束循环

do
{
	<循环体语句>
}while(<循环条件>);
//上边的代码如果用do-while来写
#include
int main(){
	int x;
	int n=0;
	scanf("%d",&x);
	/*n++;
	//x/=10依次划去末尾的数
	x/=10;
	while(x>0){
		n++;
		x/=10;
		}*/
	do{
		x/=10;
		n++;
	}while(x>0);
	//执行do-while,循环体至少执行一次,就算结果不满足循环条件也会执行一次循环体
	printf("%d\n",n);
	return 0;
}

10.猜数游戏
NOTE:%100
x%n的结果是[0,n-1]的一个整数

#include
#include
#include
int main(){
	//rand()产生一个随机数
	//rand()%100能随机产生一个0-99的随机数
	//rand()%100+1能随机产生一个1-100的随机数
	srand(time(0));
	int count=0;
	int number=rand()%100+1;
	int a;
	do{
		printf("请输入一个0-100的数");
		scanf("%d",&a);
		count++;
		if(a>number){
			printf("输入的数大了");
		}else if(a<number){
			printf("输入的数小了");
		}
	}while(a!=number);
	printf("太棒了,你用了%d次猜中了",count);
	return 0;
}

11.算平均数

#include
#include
#include
int main(){
	int count=0;
	int t=0;
	double average=0.0;
	int number=0;
	while(number!=-1){
		scanf("%d",&number);
		if(number!=-1){
			count++;
			t+=number;
		}else{
			average=(1.0*t)/count;
			printf("average=%f",average);
		}
	}
	return 0;
}

12.整数逆序

#include
int main(){
	//123-321
	int x;
	int digit;
	scanf("%d",&x);
	//123%10=3
	//123/10=12
	//12%10=2;
	//12/10=1
	//1%10=1
	while(x>0){
		digit=x%10;
		printf("%d",digit);
		x/=10;
	}
	return 0;
}

13.for循环次数

#include
int main(){
	int i;
	for(i=0;i<5;i++){
		printf("i=%d",i);
	}
	printf("\n最后i=%d\n",i);
	return 0;
//等价于
/*
	for(i=1;i<=5;i++){
		printf("i=%d",i);
	}
	printf("\n最后i=%d\n",i);
	return 0;
}
*/

14.break和continue区别
break直接跳出循环,continue是跳出此次循环,执行下一次循环

//break直接跳出循环
//continue是跳出此次循环,执行下一次循环
#include
int main(){
	int i;
	for(i=0;i<5;i++){
		printf("%d",i);
		continue;
		printf("hello world");
	}
	return 0;
}
//输出结果
0
1
2
3
4
#include
int main(){
	int i;
	for(i=0;i<5;i++){
		printf("%d\n",i);
		break;
		printf("hello world");
	}
	return 0;
}
//输出结果
0

15.输出100以内的所有素数

#include
int main(){
	int x;
	for(x=1;x<100;x++){
		int i;
		int isprime=1;
		for(i=2;i<x;i++){
			if(x%i==0){
				isprime=0;
				break;
			}
		}
		if(isprime==1){
			printf("%d\n",x);
		}
	}
	return 0;
}

16.凑钱游戏

#include
int main(){
	//如何用1角,2角,5角的硬币凑出10元以下的金额
	int x;
	int one,two,five;
	scanf("%d",&x);
	for(one=1;one<x*10;one++){
		for(two=1;two<x*10/2;two++){
			for(five=1;five<x*10/5;five++){
				if(one+two*2+five*5==x*10){
					printf("可以用%d个一角和%d个两角和%d个五角凑成%d个一元\n",one,two,five,x); 
				}
			}
		}
	}
	return 0;
}
//输出结果
2
可以用1个一角和2个两角和3个五角凑成2个一元
可以用1个一角和7个两角和1个五角凑成2个一元
可以用2个一角和4个两角和2个五角凑成2个一元
可以用3个一角和1个两角和3个五角凑成2个一元
可以用3个一角和6个两角和1个五角凑成2个一元
可以用4个一角和3个两角和2个五角凑成2个一元
可以用5个一角和5个两角和1个五角凑成2个一元
可以用6个一角和2个两角和2个五角凑成2个一元
可以用7个一角和4个两角和1个五角凑成2个一元
可以用8个一角和1个两角和2个五角凑成2个一元
可以用9个一角和3个两角和1个五角凑成2个一元
可以用11个一角和2个两角和1个五角凑成2个一元
可以用13个一角和1个两角和1个五角凑成2个一元
//使用break使其输出一种情况就可以了
#include
int main(){
	//如何用1角,2角,5角的硬币凑出10元以下的金额
	int x;
	int one,two,five;
	scanf("%d",&x);
	int exit=0;
	for(one=1;one<x*10;one++){
		for(two=1;two<x*10/2;two++){
			for(five=1;five<x*10/5;five++){
				if(one+two*2+five*5==x*10){
					printf("可以用%d个一角和%d个两角和%d个五角凑成%d个一元\n",one,two,five,x); 
					exit=1;
					break;
				}
			}
			if(exit)break;
		}
		if(exit)break;
	}
	return 0;
}
//输出结果
2
可以用1个一角和2个两角和3个五角凑成2个一元

上面叫做接力break
也可以使用goto语句,如下:

#include
int main(){
	//如何用1角,2角,5角的硬币凑出10元以下的金额
	int x;
	int one,two,five;
	scanf("%d",&x);
	for(one=1;one<x*10;one++){
		for(two=1;two<x*10/2;two++){
			for(five=1;five<x*10/5;five++){
				if(one+two*2+five*5==x*10){
					printf("可以用%d个一角和%d个两角和%d个五角凑成%d个一元\n",one,two,five,x); 
					goto out;
				}
			}
		}
	}
	out:
		return 0;
}
//输出结果
2
可以用1个一角和2个两角和3个五角凑成2个一元

17.求前n项和

//1+1/2+1/3+1/4+...+1/n
#include
int main(){
	int n;
	scanf("%d",&n);
	int i;
	double sum=0.0;
	for(i=1;i<=n;i++){
		sum+=1.0/i;
	}
	printf("f(%d)=%f",n,sum);
	return 0;
}
//1-1/2+1/3-1/4+1/5-...+1/n
#include
int main(){
	int n;
	scanf("%d",&n);
	int i;
	double sum=0.0;
	double sign=1.0;
	for(i=1;i<=n;i++){
		sum+=sign/i;
		sign=-sign;
	}
	printf("f(%d)=%f\n",n,sum);
	return 0;
}

18.数据类型

//整数
char,short,int,long,long long
//浮点数
float,double,long double
//逻辑
bool
//指针
//自定义类型


//sizeof是一个运算符,给出某个类型或变量在内存所占据的字节数
sizeof(int)//int在内存中占据几个字节
sizeof(i)//i这个变量在内存中占据几个字节

#include
int main(){
	int a;
	a=6;
	printf("sizeof(int)=%lf\n",sizeof(int));
	printf("sizeof(double)=%lf\n",sizeof(double));
	printf("sizeof(a)=%lf\n",sizeof(a));
	return 0;
}
//输出结果
sizeof(int)=4
sizeof(double)=8
sizeof(a)=4


#include
int main(){
	printf("sizeof(char)=%ld\n",sizeof(char));
	printf("sizeof(short)=%ld\n",sizeof(short));
	printf("sizeof(int)=%ld\n",sizeof(int));
	printf("sizeof(long)=%ld\n",sizeof(long));
	printf("sizeof(long long)=%ld\n",sizeof(long long));
	return 0;
}
//输出结果
sizeof(char)=1
sizeof(short)=2
sizeof(int)=4
sizeof(long)=8
sizeof(long long)=8

19.unsigned

//unsigned初衷并非扩展数能表达的范围,而是为了做纯二进制运算,主要是为了移位
#include
int main(){
	char c=255;
	//255二进制为11111111,2的8次方-1
	//没有unsigned表示最高位是符号位,所以输出结果是-1
	printf("%d",c);
	return 0;
}
//输出结果
-1
#include
int main(){
	unsigned char c=255;
	printf("%d",c);
	return 0;
}
//输出结果
255

20.整数的输入输出

整数的输入输出只有两种形式:intlong long
%d:int
%u:unsigned
%ld:long long
%lu:unsigned long long

21.输出精度
float有7位是有效的,double有15为有效数字

//在%和f之间加上.n可以指定输出小数点后几位,这样的输出是做4舍5入的
//%3.0f  前一个数字是字符宽,后一个数是小数点位数
#include
int main(){
	printf("%.3f\n",-0.0049);
	printf("%.30f\n",-0.0049);
	printf("%.3f\n",-0.00049);
	return 0;
}
//输出结果
-0.005
-0.004899999999999999800000000000
-0.000

22.超过范围的浮点数

//printf输出inf表示超过范围的浮点数
//printf输出nan表示不存在的浮点数
#include
int main(){
	printf("%f\n",12.0/0.0);
	printf("%f\n",-12.0/0.0);
	printf("%f\n",0.0/0.0);
	return 0;
	}
//输出结果
inf
-inf
nan

带小数点的字面量是double而非float,float需要用f或F后缀

f1==f2可能失败
所以用
fabs(f1-f2)<1e-12//f1-f2的绝对值小于1e-12

如果没有特殊要求,只是用double,现代cpu能直接对double作硬件计算,性能不会比float差,在64位的机器上,数据存储的速度也不会比float慢

23.字符类型

/*
1.char是一种整数,也是一种特殊的类型:字符。这是因为:
2.用单引号表示字符字面量:'a','1'
3.''也是一个字符
4.printf和scanf里用%c来输入输出字符
*/
#include
int main(){
	char c;
	char d;
	c=1;
	d='1';
	if(c==d){
		printf("相等\n"); 
	}else {
		printf("c=%d\n",c);
		printf("d=%d\n",d);
	}
	return 0;
}
//输出结果
不相等
c=1
d=49

//如何输入'1'这个字符给char c
//两种方式,如下
//方法1
#include
int main(){
	char c;
	scanf("%c",&c);
	printf("c=%d\n",c);
	printf("c='%c'\n",c);
	return 0;
}
//输出结果
1
c=49
c='1'
//方法2
#include
int main(){
	char c;
	int d;
//因为scanf里%d,所以只能用int接受,所以用一个int整形的变量d
	scanf("%d",&i);
	d=i;
	printf("c=%d\n",c);
	printf("c='%c'\n",c);
	return 0;
}
//输出结果
49
c=49
c='1'

%d %c中间的空格意思是直到后面读到的不是空格为止

#include
int main(){
	char c;
	int i;
	scanf("%d %c",&i,&c);
	printf("i=%d c=%d c='%c'\n",i,c,c);
	return 0;
	}
//输出结果
12 1
i=12 c=49 c='1'


#include
int main(){
	char a;
	int b;
	scanf("%d%c",&a,&b);
	printf("a=%d b=%d b='%c'\n",a,b,b);
	return 0;
	}
//输出结果
12 1
a=12 b=32 b=' '

24.逃逸字符

/*
\b回退一格
\t到下一个表格位
\n换行
\r回车
\''双引号
\'单引号
\\反斜杠本身
*/
#include
int main(){
	printf("123\bA\n456\n");
	return 0;
	}
//输出结果
12A
456

25.自动类型转换
对于printf,任何小于int的类型都会被转换成int;float会被转换成double,所以%f就可以输出double,不需要%lf,因为%f在printf里自动被转换成了%lf。但是scanf不会,要输入short,需要%hd。
26.强制类型转换
要把一个量强制转换为另一个类型(通常是较小的类型),需要:(类型)值

//(int)10.2
//(short)32
注意这时候的安全性,小的变量不总能表达大的量
//(short)32768
//强制类型转换的优先级高于四则运算
double a=1.0;
double b=2.0;
int i=(int)a/b;
//int i=(int)(a/b);

26.bool类型

#include
//之后就可以使用bool和true,false

27.条件运算和逗号运算

m<n ? x : a+5//如果m
a++>=1 && b-->2 ? a:b//如果两个都满足结果是a,否则是b
//逗号表达式主要是在for条件中使用,基本上就只有这一个用处
for(i=10,j=5;i<=20;i++,j--)

28.函数传值
每个函数都有自己的变量空间,参数也位于这个独立的空间中,和其他的函数没有关系
过去,对于函数参数表中的参数,叫做“形式参数”,调用函数时给出的值,叫做“实际参数”,我们不建议继续用这种古老的方式称呼它们
翁凯C语言学习笔记_第1张图片
现在,我们就把叫做参数和值,这中间发生的就只有一件事情,就是传值
翁凯C语言学习笔记_第2张图片
29.本地变量(局部变量)
翁凯C语言学习笔记_第3张图片
30.数组的例子:统计个数

//写一个程序,输入数量不确定的[0,9]范围内的整数,统计每一种数字出现的次数,输入-1表示结束
#include
int main(){
	int i;
	int x;
	int count[10];
	//初始化数组 
	for(i=0;i<10;i++){
		count[i]=0;
	}
	scanf("%d",&x);
	while(x!=-1){
		if(x>=0 && x<=9){
			count[x]++;
		}
		scanf("%d",&x);
	}
	for(i=0;i<10;i++){
		printf("%d:%d\n",i,count[i]);
	}
	return 0;
}

31.数组运算
数组初始化

#include
int main(){
	int i;
	//注意这种写法
	int number[10]={[1]=2,4,[5]=6};
	for(i=0;i<10;i++){
		printf("%d\t",number[i]);
	}
	return 0;
}
//输出结果
0       2       4       0       0       6       0       0       0       0

数组的大小

sizeof(a)/sizeof(a[0]);
//sizeof(a[0])给出单个元素的大小,于是相除就是数组的单元个数
//这样的代码,一旦修改数组中初始的数据,不需要修改遍历的代码

数组的赋值

数组变量本身不能被赋值
要把一个数组的所有元素交给另外一个数组,必须采用遍历
#include
int main(){
	int a[]={1,2,3};
	//int b[]=a;//这是错误的,只能采用遍历
	for(i=0;i<length;i++){
		b[i]=a[i];
	}
	return 0;
	}
//判断数组中数字的下标
#include
int main(){
	int a[]={1,2,3,4,6};
	int x;
	int loc;
	printf("请输入一个数字:");
	scanf("%d",&x);
	loc=search(x,a,sizeof(a)/sizeof(a[0]));
	if(loc!=-1){
		printf("%d在第%d个位置上\n",x,loc); 
	}else{
		printf("不存在"); 
	}
	return 0;
} 
int search(int key,int a[],int length){
	int i;
	int ret=-1;
	for(i=0;i<length;i++){
		if(a[i]==key){
			ret=i;
			break;
		}
	}
	return ret;
}
//输出结果
请输入一个数字:2
2在第1个位置上

32.二维数组
int a[3][5]是一个三行五列的矩阵

//二维数组的遍历
//比如
for(i=0;i<3;i++){
	for(j=0;j<5;j++){
		a[i][j]=i*j;
	}
}
//二维数组初始化
int a[][5]={
	{0,1,2,3,4},
	{2,3,4,5,6},
}
//列数是必须给出的,行数可以由编译器来数
//每行一个{},逗号分离
//最后的逗号可以存在,有古老的传统
//如果省略,表示补0

33.取地址计算
1.运算符&,获得变量的地址,他的操作数必须是变量。
2.int i;printf("%x",&i);
3.地址的大小是否与int相同取决于编译器
4.int i;printf("%p",&i);
5.&不能对没有地址的东西取地址
34.指针

/*
指针:就是保存地址的变量
int i;
int* p=&i;
//这两种写法都是说明p是一个指针,而q是一个int类型的变量,只是写法不同而已
int* p,q;
int *p,q;
//p和q都是指针
int *p,*q;
*********************************
作为参数的指针
void f(int *p);
在被调用的时候得到了某个变量的地址:
int i=0;f(&i);
在函数里面可以通过这个指针访问外面的这个i
*/
#include
void f(int *p);
int main(){
	int i=6;
	printf("&i=%p\n",&i);
	f(&i);
	return 0;
}
void f(int *p){
	printf("p=%p\n",p);
}
//输出结果
&i=000000000062FE4C
p=000000000062FE4C
/*
访问那个地址上的变量*
1.*是一个单目运算符,用来访问指针的值所表示的地址上的变量
2.可以做右值也可以做左值
//*的意思是访问指针所指向那个地址的变量
3.int k=*p;
4.*p=k+1;
5.*p这个整体可以当做一个变量值
*/

指针的运算符

/*
1.互相反作用
2.*&yptr->*(&yptr)->*(yptr的地址)->得到那个地址上的变量->yptr
3.&*yptr->&(*yptr)->&(y)->得到y的地址,也就是yptr->yptr
*/

指针有什么作用

/*
1.指针应用场景一:交换两个变量的值
void swap(int *pa,int *pb)
{
	int t=*pa;
	*pa=*pb;
	*pb=t;
}
*/
#include
int main(){
	int a=5,b=3;
	swap(&a,&b);
	printf("a=%d,b=%d\n",a,b);
	return 0;
}
void swap(int *pa,int *pb){
	int t=*pa;
	*pa=*pb;
	*pb=t;
}
//输出结果
a=3,b=5
/*
2.指针应用场景二
函数返回多个值,某些值就只能通过指针返回
传入的参数实际上是需要保存带回的结果的变量
*/
#include
void minmax(int a[],int len,int *min,int *max);
int main(){
	int a[]={1,2,3,4,5,6,7,8,9,13,45,67};
	int min,max;
	minmax(a,sizeof(a)/sizeof(a[0]),&min,&max);
	printf("min=%d,max=%d\n",min,max);
	return 0;
}
void minmax(int a[],int len,int *min,int *max){
	int i;
	*min=*max=a[0];
	for(i=1;i<len;i++){
		if(a[i]<*min){
			*min=a[i];
		}
		if(a[i]>*max){
			*max=a[i];
		}
	}
}
//输出结果
min=1,max=67

/*
指针应用场景二b
1.函数返回运算的状态,结果通过指针返回
2.常用的套路是让函数返回特殊的不属于有效范围内的值来表示出错
3.-1或0(在文件操作会看到大量的例子)
4.但是当任何数值都是有效的可能结果时,就得分开返回了(状态通过retun来返回,结果通过指针来返回)
*/
#include
int main(){
	int a=5;
	int b=2;
	int c;
	if(divide(a,b,&c)){
		printf("%d/%d=%d\n",a,b,c);
	}
	return 0;
}
int divide(int a,int b,int *result){
	int ret=1;
	if(b==0)ret=0;
	else{
		*result=a/b;
	}
	return ret;	
}

指针最常见的错误
1.定义了指针变量,还没有指向任何变量,就开始使用指针
比如
int *p;
*p=12;是错误的
35.指针与数组

/*
1.函数参数表中的数组实际上是指针
2.sizeof(a)==sizeof(int*)
3.但是可以用数组的运算符[]进行运算
*/
数组参数
/*
以下四种函数原型是等价的
1.int sum(int *ar,int n);
2.int sum(int *,int);
3.int sum(int a[],int n);
4.int sum(int [],int);

数组变量是特殊的指针
1.数组变量本身表达地址,所以
int a[10];int* p=a;//无需用&取地址
但是数组的单元表达的是变量需要用&取地址
a==&a[0]
2.[]运算符可以对数组做,也可以对指针做:
p[0]<==>a[0]
如果p是数组,*p表示的就是第一个数组元素的地址,所以*p和p[0]是等价的
3.*运算符可以对指针做,也可以对数组做:
*a=25;
4.数组变量是const的指针,所以不能被赋值
//数组变量之间是不能做互相赋值的
int a[];
int b[];
b=a//错误
*/

36.指针与const
翁凯C语言学习笔记_第4张图片

//表示一旦得到了某个变量的地址,不能再指向其他变量
int *const q=&i;//q是const
*q=26;//OK
q++;//error,q是i的地址,q指向i这个事实不能被改变了   

//所指是const
1.表示不能通过这个指针去修改那个变量(并不能使得那个变量成为const2.const int* p=&i;
3.*p=26;//ERROR(*p)是const
4.i=26;//OK
5.p=&i;//OK
NOTE:注意区分两者的区别

这些是啥意思
int i;
const int* p1=&i;
int const* p2=&i;
//前面两种const都是在*号的前面,所以意思是一样的,即不能通过这个指针去修改i这个变量,可以理解为*p1是const,即窒息那个的i这个数据是不能再修改了
int *const p3=&i;        
//const在*号后面,说明指针不能被修改,即指向了i这个地址,不能再指向其他变量的地址
判断哪个被const的标志是const*号的前面还是后面

转换
总是可以把一个非const的值转换成constvoid f(const int *x);
int a=14;
f(&a);//ok
const int b=a;
f(&b);//ok
b=a+1;//ERROR       
当要传递的参数的类型比地址大的时候,这是常用的手段;即能用比较少的字节数传递值给参数,又能避免函数对外面的变量的修改 

const数组
1.const int a[]={1,2,3,4};
2.数组变量已经是const的指针了,这里的const表明数组的每个单元都是const int
3.所以必须通过初始化进行赋值    

保护数组值
1.因为把数组传入函数时传递的是地址,所以那个函数内部可以修改数组的值
2.为了保护数组不被函数破坏,可以设置参数为const
3.int sum(const int a[],int length); //这样的话,在sum这个函数里就不能对a这个数组作任何的修改                                                                                                                                                                                             

37.指针运算

/*
1.这些算术运算可以对指针做
2.给指针加、减一个整数(+,+=,-,-=)
3.递增递减(++/--)
4.两个指针可以相减

*p++
//取出p所指那个地方的东西,顺便把p+1
1.取出p所指的那个数据来,完事之后顺便把p移到下一个位置去
2.*的优先级虽然高,但是没有++高
3.常用于数组类的连续空间操作
4.在某些CPU上,这可以直接被翻译成一条汇编指令
*/
#include
int main(){
		int a[]={1,2,3,4,5,-1};
		int *p;
		p=a;
		//用*p++遍历数组
		while(*p!=-1){
			printf("%d\t",*p++);
		}
		return 0;
}
//输出结果
1       2       3       4       5

指针比较
1.<,<=,==,>,>=,!=都可以对指针做
2.比较它们在内存中的地址
3.数组中的单元的地址肯定是线性递增的

0地址
1.当然你的内存中有0地址,但是0地址通常是个不能随便碰的地址
2.所以你的指针不应该具有03.因此可以用0地址来表示特殊的事情
	返回的指针是无效的
	指针没有被真正初始化(先初始化为04.NULL是一个预定定义的符号,表示0地址
5.有的编译器不愿意你用0来表示0地址


指针的类型转换
1.void* 表示不知道指向什么东西的指针
	计算时与char*相同(但不相通)
2.指针也可以转换类型
	int *p=&i;void* q=(void*)p;
3.这并没有改变p所指的变量的类型,而是让后人用不同的眼光通过p看它所指的变量
	我不再当你是int啦,我认为你就是void!

用指针来做什么
1.需要传入较大的数据时作为参数
2.传入数组后对数组做操作
3.函数返回不止一个结果
4.需要用函数来修改不止一个变量
5.动态申请的内存...

38.动态内存分配
malloc
#include
void* malloc(size_t size);
1.向malloc申请的空间大小是以字节为单位的
2.返回的结果是void*,需要类型转换为自己需要的类型
3.(int*)malloc(n*sizeof(int));
free(name);

//example
#include
#include
int main(){
	int number;
	int *a;
	int i;
	printf("输入数量:\n");
	scanf("%d",&number);
	a=(int*)malloc(number*sizeof(int));
	for(i=0;i<number;i++){
		scanf("%d",&a[i]);
	}
	for(i=number-1;i>=0;i--){
		printf("%d",a[i]);
	}
	free(a);
	return 0;
}
//输出结果
输入数量:
3
1
2
3
321

free
1.把申请得来的空间还给系统
2.申请过的空间,最终都应该要还
3.只有还申请来的空间的首地址

写程序的好习惯,任何的指针定义出来的,就初始为0
void* p=0;
free(p);//这种情况没有问题

常见的问题
1.申请了没free->长时间运行内存逐渐下降
2.新手:忘了
3.老手:找不到合适的free的时机
4.free过了再free,会崩溃

39.字符串

//字符数组
char word[]={'H','E','L','L','O'};
word[0]=H
word[1]=...
//字符串
char word[]={'h','e','l','l','l','\0'};
这个\0就使得这个word是个字符串,虽然仍是个字符数组,但是可以当字符串来进行运算

字符串
'\0’和’0’是不一样的,‘0’代表是ASCII的零,而’\0’代表的就是0
翁凯C语言学习笔记_第5张图片

//字符串变量
char *str="hello";
char word[]="hello";
char line[10]="hello";
//字符串常量
example:"hello"
1."hello"会被编译器变成一个字符数组放在某处,这个数组的长2.度是6,结尾还有表示结束的0
3.两个相邻的字符串常量会被自动连接起来
//字符串
1.C语言的字符串是以字符数组的形态存在的
2.不能用运算符对字符串做运算
3.通过数组的方式可以遍历字符串
4.唯一特殊的地方是字符串字面量可以用来初始化字符数组
5.以及标准库提供了一系列字符串函数

40.字符串常量
char* s=“hello world”;
1.s是一个指针,初始化为指向一个字符串常量
2.由于这个常量所在的地方,所以实际上s是const char* s,但是由于历史的原因,编译器接受不带const的写法
3.但是试图对s所指的字符串做写入会导致严重的后果
4.如果需要修改字符串,应该用数组
char s[]=“hello world”;
用指针还是数组?
1.char *str=“hello”;
2.char str[]=“hello”;
3.数组:这个字符串在这里
作为本地变量空间自动被回收
4.指针:这个字符串不知道在哪里
就是表达一个字符串,只读的不去写
处理参数
动态分配空间
NOTE:
如果要构造一个字符串->数组
如果要处理一个字符串->指针

char*是字符串
1.字符串可以表达为char*的形式
2.char*不一定是字符串
	本意是指向字符的指针,可能指向的是字符的数组(就像int*一样)
3.只有它所指的字符数组有结尾的0,才能说它所指的是字符串

41.字符串输入输出

/*
char string[8];
scanf("%s",string);
printf("%s",string);
scanf读入一个单词(到空格、tab或回车为止)
scanf是不安全的,因为不知道要读入的内容的长度

char string[8];
scanf("%7s",string);//告诉机器最多读入7个字符,多于7个的就不要了
*/
#include
#include
void f(){
	char str1[8];
	char str2[8];
	scanf("%7s",str1);
	scanf("%7s",str2);
	printf("%s##%s\n",str1,str2);
}
int main(){
	f();
	return 0;
} 
//输出结果
12345678
1234567##8

/*
安全的输入
char string[8];
scanf("%7s",string);
1.在%和s之间的数字表示最多允许读入的字符的数量,这个数字应该比数组的大小小1
2.下一次scanf从哪里开始?

常见错误
char* string;//含义是定义了一个指针变量string
scanf("%s",string);
1.以为char*是字符串类型,定义了一个字符串类型的变量string就可以直接使用了
2.由于没有对string初始化为0,所以不一定每次运行都出错

空字符串
1.char buffer[100]="";
这是一个空的字符串,buffer[0]=='\0'
2.char buffer[]="";
这个数组的长度只有1!
*/

42.字符串数组

/*
1.char **a
a是一个指针,指向另一个指针,那个指针指向一个字符(串)
2.char a[][]
char a[][10];//意思是a[0]-->char [10]每个数组元素是一个字符长度为10的字符串
char a[][10]={
	"hello",
	"world",
	"python"
	};
3.还有一种写法是
	char *a[]={
		"hello",
		"world",
		"python"
	};
	此时a[0]-->char*
*/

43.单字符的输入输出

/*
putchar
1.int putchar(int c);
2.向标准输出写一个字符
3.返回写了几个字符,EOF(-1)表示写失败

getchar
1.int getchar(void);
2.从标准输入读入一个字符
3.返回类型是int是为了返回EOF(-1)
4.windows--->Ctrl-z
	unix--->Ctrl-D
*/
#include
int main(){
	int ch;
	while((ch=getchar())!=EOF){
		putchar(ch);
		}
	return 0;
	}

44.字符串函数strlen

/*
string.h
1.strlen
2.strcmp
3.strcpy
4.strcat
5.strchr
6.strstr

strlen
1.size_t strlen(const char*s);
2.返回s 的字符串长度(不包括结尾的0)
*/
#include
#include
int main(){
	char str[]="hello";
	printf("%lu\n",strlen(str));
	printf("%lu\n",sizeof(str));
	return 0;
} 
//输出结果
5
6
/*
strcmp
1.int strcmp(const* char *s1,const char* s2);
2.比较两个字符串,返回:
	0:s1==s2
	1:s1>s2
	-1:s1
/*
strcpy
1.char *strcpy(char *restrict dst,const char* restrict src);//目的在前,源在后
2.把src的字符串拷贝到dst
	restrict表明src和dst不重叠(C99)
3.返回dst
	为了能链起代码来

//复制一个字符串
char *dst=(char*)malloc(strlen(src)+1);
strcpy(dst,src);
*/
#include
#include
char *mypy(char *dst,const char *src){
	int idx=0;
	//数组版 
	while(src[idx]){
		dst[idx]=src[idx];
		idx++;
	}
	dst[idx]='\0';
	//指针版 
	/*
	char *ret=dst;
	while(*src){
		*dst=*src;
		dst++;
		src++;
	} 
	*dst='\0';
	return ret;*/
}
int main(){
	char s1[]="abc";
	char s2[]="";
	mypy(s2,s1);
	printf("%s\n",s2);
	return 0; 
}
//输出结果
abc

/*
strcat
1.char* strcat(char *restrict s1,const char*restrict s2);
2.把s2拷贝到s1的后面,接成一个长的字符串
3.返回s1
4.s1必须具有足够的空间

安全问题
strcpy和strcat都可能出现安全问题
	如果目的地没有足够的空间?
建议尽可能不要使用这两个函数

安全版本
1.char * strncpy(char *restrict dst,const char* restrict src,size_t n);
2.char *strncat(char* restrict s1,const char *restrict s2,size_t n);
3.int strncmp(const char *s1,const char *s2,size_t n);//这个作用是比较前n个
*/

45.字符串搜索函数

/*
字符串中找字符
char *strchr(const char* s,int c);//从左边找
char *strrchr(const char* s,int c);//从右边找
返回NULL表示没有找到

如何寻找第2个?
*/
#include
#include
int main(){
	char s[]="hello";
	char *p=strchr(s,'l');
	p=strchr(p+1,'l');
	printf("%s\n",p);
	return 0;
}
//输出结果
lo

#include
#include
int main(){
	char s[]="hello";
	char *p=strchr(s,'l');
	char *t=(char*)malloc(strlen(p)+1);
	p=strchr(p+1,'l');
	t=strcpy(t,p);
	printf("%s\n",t);
	return 0;
}
//输出结果
lo

#include
#include
int main(){
	char s[]="hello";
	char *p=strchr(s,'l');
	char c=*p;
	*p='\0';
	char *t=(char*)malloc(strlen(s)+1);
	p=strchr(p+1,'l');
	t=strcpy(t,s);
	printf("%s\n",t);
	free(t);
	return 0;
}
//输出结果
he

/*
字符串中找字符串
char *strstr(const char *s1,const char* s2);
char *strcasestr(const char *s1,const char *s2);//忽略大小写做寻找
*/

46.枚举

/*
1.枚举是一种用户定义的数据类型,它用关键字enum以如下语法来声明
enum 枚举类型名字{name1,name2,...,name3};
2.枚举类型名字通常并不真的使用,要用的是在大括号里的名字,因为它们就是常量符号,它们的类型是int,值则依次从0到n。如:
enum colors{red,yellow,green};
3.这样就创建了3个常量,red的值是0,yellow是1,而green是2.
4.当需要一些可以排列起来的常量值时,定义枚举的意义就是给了这些常量值名字。
*/
#include
enum color{red,yellow,green};//相当于定义一个color变量类型,其实里面的red,yellow,green相当于是int类型
void f(enum color c);
int main(){
	enum color t=red;//定义一个color类型的变量t,把red赋值给t,C语言中这个enum不能省略,C++中可以省略
	scanf("%d",&t);
	f(t);
	return 0;
}
void f(enum color c){
	printf("%d\n",c);
}



//套路:自动计数的枚举
#include
enum COLOR{RED,YELLOW,GREEN,NumCOLORS};
int main(){
	int color=-1;
	char *ColorNames[NumCOLORS]={"red","yellow","green"};
	char *colorName=NULL;
	
	printf("请输入你喜欢的颜色代码:");
	scanf("%d",&color);
	if(color>=0&&color<NumCOLORS){
		colorName=ColorNames[color];
	}else{
		colorName="unknown";
	}
	printf("你喜欢的颜色是%s\n",colorName);
	return 0; 
}
//输出结果
请输入你喜欢的颜色代码:0
你喜欢的颜色是red

/*
枚举量
1.声明枚举量的时候可以指定值
2.enum COLOR{RED=1,YELLOW,GREEN=5};
*/
#include
enum COLOR{RED=3,YELLOW,GREEN=5};
int main(){
	printf("GREEN=%d\n",GREEN);
	return 0;
}
//输出结果
GREEN=5

/*
枚举只是int
1.虽然枚举类型可以当做类型使用,但是实际上很少使用,可以说不好用
2.如果有意义上排比的名字,用枚举比const int方便
3.枚举比宏(macro)好,因为枚举有int类型
*/

47.结构类型

struct date{
	int month;
	int day;
	int year;
};
//别忘了最后这个分号

/*
声明结构的形式:第一种形式
struct point{
	int x;
	int y;
};
struct point p1,p2;
p1和p2都是point,里面有x和y的值
第二种形式:
struct{
	int x;
	int y;
}p1,p2;
p1和p2都是一种无名结构,里面有x和y
第三种形式:
struct point{
	int x;
	int y;
}p1,p2;
p1和p2都是point里面有x和y的值
*/
#include
struct date{
	int month;
	int day;
	int year;
};
int main(){
	//第一种初始化方式
	struct date today={07,31,2021};
	//第二种初始化方式
	struct date thismonth={.month=7,.year=2021};
	printf("today's date is %i-%i-%i.\n",today.month,today.day,today.year);
	printf("this month is %i-%i-%i.\n",thismonth.year,thismonth.month,thismonth.day);
	return 0;
}
//输出结果
today's date is 7-31-2021.
this month is 2021-7-0.

结构成员
1.结构和数组有点像
2.数组用[]运算符合下标访问其成员
a[0]=10;
3.结构用.运算符和名字访问其成员
today.day
student.firstName
p1.x
p1.y
结构运算
1.要访问整个结构,直接用结构变量的名字
2.对于整个结构,可以做赋值、取地址,也可以传递给函数参数
p1=(struct point){5,10};//相当于p1.x=5;p1.y=10;
p1=p2;//相当于p1.x=p2.x;p1.y=p2.y;
结构指针
1.和数组不同,结构变量的名字并不是结构变量的地址,必须使用&运算符
2.struct date *pDate=&today;
48.结构与函数

/*
结构作为函数参数
int numberOfDays(struct date d)
1.整个结构可以作为参数的值传入函数
2.这时候是在函数内新建一个结构变量,并复制调用者的结构的值
3.也可以返回一个结构
4.这与数组完全不同

输入结构
1.没有直接的方式可以一次scanf一个结构
2.如果我们打算写一个函数来读入结构
3.->
4.但是读入的结构如何送回来呢?
5.记住C在函数调用时是传值的
	所以函数中的p与main中的y是不同的
	在函数读入了p的数值之后,没有任何东西回到main,所以y还是{0,0}
*/
#include
struct point{
	int x;
	int y;
};
void getstruct(struct point);
void output(struct point);
int main(){
	struct point y={0,0};
	getstruct(y);
	output(y);
	return 0;
}
void getstruct(struct point p){
	scanf("%d",&p.x);
	scanf("%d",&p.y);
	printf("%d,%d",p.x,p.y);
}
void output(struct point p){
	printf("%d,%d",p.x,p.y);
}
//输出结果
1 2
1,20,0
/*
解决的方案
1.之前的方案,把一个结构传入了函数,然后在函数中操作,但是没有返回回去
	问题在于传入函数的是外面那个克隆体,而不是指针
	传入结构和传入数组是不同的
2.在这个输入函数中,完全可以创建一个临时的结构变量,然后把这个结构返回给调用者
*/
#include
struct point{
	int x;
	int y;
};
struct point getstruct(void);
void output(struct point);
int main(){
	struct point y={0,0};
	y=getstruct();
	output(y);
	return 0;
}
struct point getstruct(void){
	struct point p;
	scanf("%d",&p.x);
	scanf("%d",&p.y);
	printf("%d,%d",p.x,p.y);
	return p;
}
void output(struct point p){
	printf("%d,%d",p.x,p.y);
}
//输出结果
1,2
1,21,2

/*
结构指针作为参数
指向结构的指针
struct date{
	int month;
	int day;
	int year;
}myday;
struct date *p=&myday;
(*p).month=12;
p->month=12;
用->表示指针所指的结构变量的成员
*/
//改造上面的程序
#include
struct point{
	int x;
	int y;
};
struct point *getstruct(struct point *y);
int main(){
	struct point p={0,0};
	getstruct(&p);
	print(&p);
	output(p);
	return 0;
}
struct point *getstruct(struct point *y){
	scanf("%d",&y->x);
	scanf("%d",&y->y);
	printf("%d,%d\n",y->x,y->y);
	return y;
}
//因为print函数只是输出值并不做修改,所以可以加const
void print(const struct point *y){
	printf("%d,%d\n",y->x,y->y);
}
void output(struct point p){
	printf("%d,%d\n",p.x,p.y);
}
//输出结果
1 2
1,2
1,2
1,2

49.结构数组
struct date dates[100];
struct date dates[]={
{4,5,2005},{2,4,2005}};

#include
struct time{
	int hour;
	int minutes;
	int seconds;
};
struct time timeUpdate(struct time now);
int main(){
	struct time testTimes[5]={
	{11,59,59},{12,0,0},{1,2,3},{4,5,6},{6,7,8}
	};
	int i;
	for(i=0;i<5;i++){
		printf("Time is %.2i:%2i:%2i",testTimes[i].hour,testTimes[i].minutes,testTimes[i].seconds);
		testTimes[i]=timeUpdate(testTimes[i]);
		printf("...one second later it's %.2i:%.2i:%.2i\n",testTimes[i].hour,testTimes[i].minutes,testTimes[i].seconds);
	}
	return 0;
}
struct time timeUpdate(struct time now){
	++now.seconds;
	if(now.seconds==60){
		now.seconds=0;
		++now.minutes;
		if(now.minutes==60){
			now.minutes=0;
			++now.hour;
			
			if(now.hour==24){
				now.hour=0;
			}
		}
	}
	return now;
}
//输出结果
Time is 11:59:59...one second later it's 12:00:00
Time is 12: 0: 0...one second later it's 12:00:01
Time is 01: 2: 3...one second later it's 01:02:04
Time is 04: 5: 6...one second later it's 04:05:07
Time is 06: 7: 8...one second later it's 06:07:09
/*
结构中的结构
struct dateAndTime{
	struct date sdate;
	struct time stime;
};
嵌套的结构
struct point{
	int x;
	int y;
};
struct rectangle{
	struct point pt1;
	struct point pt2;
};
如果有变量
struct rectangle r;
就可以有r.pt1.x
r.pt1.y
r.pt2.x
r.pt2.y
如果有变量定义:
struct rectangle r,*rp;
rp=&r;
那么下面四种形式是等价的:
r.ptl.x
rp->ptl.x
(r.ptl).x
(rp->prl).x
但是没有rp->ptl->x,因为ptl不是指针
*/
//结构中的结构的数组
#include
struct point{
	int x;
	int y;
};
struct rectangle{
	struct point p1;
	struct point p2;
};
void printRect(struct rectangle r){
	printf("<%d,%d> to <%d,%d>\n",r.p1.x,r.p1.y,r.p2.x,r.p2.y);
}
int main(){
	int i;
	struct rectangle rects[]={
		{{1,2},{3,4}},{{3,4},{5,6}}
	};
	for(i=0;i<2;i++){
		printRect(rects[i]);
	}
	return 0;
}
//输出结果
<1,2> to <3,4>
<3,4> to <5,6>

50.类型定义

/*
自定义数据类型
1.C语言提供了一个叫做typedef的功能来声明一个已有的数据类型的新名字。比如:
	typedef int Length;
	使得Length成为int类型的别名。
	这样,Length这个名字就可以代替int出现在变量定义和参数声明的地方了:
	Length a,b,len;
	Length numbers[10];

typedef
1.声明新的类型的名字
	新的名字是某种类型的别名
	改善了程序的可读性
typedef long int64_t;
typedef struct ADate{
	int month;
	int day;
	int year;
}Date;//Date就代表了typedef和Date中间所有的东西
int64_t i=100000000;
Date d={9,1,2005};

typedef
typedef int Length;//Length就等价于int类型
typedef *char[10] Strings;//Strings是10个字符串的数组的类型
typedef struct node{
	int data;
	struct node*next;
	}aNode;
或
typedef struct node aNode;//这样用aNode就可以代替struct node
*/

51.联合

52.全局变量
1.定义在函数外面的变量是全局变量
2.全局变量具有全局的生存期合作用域
它们与任何函数都无关
在任何函数内部都可以使用它们

#include
int f(void);
int gAll=12;
int main(){
	printf("in %s gall=%d\n","main",gAll);
	f();
	printf("int %s gall=%d\n","main",gAll);
	return 0;
} 
int f(void){
	printf("in %s gall=%d\n","f",gAll);
	gAll=gAll+2;
	printf("in %s gall=%d\n","f",gAll);
	return gAll;
}
//输出结果
in main gall=12
in f gall=12
in f gall=14
int main gall=14

全局变量初始化
1.没有做初始化的全局变量会得到0值
指针会得到NULL值
2.只能用编译时刻已知的值来初始化全局变量
3.它们的初始化发生在main函数之前
被隐藏的全局变量
1.如果函数内部存在与全局变量同名的变量,则全局变量被隐藏
53.静态本地变量
//本来一个变量在进入函数时才有,离开函数时就消失了,加上static就变成静态本地变量,每次离开它还在那,再一次进入函数还是上一次离开函数时的值
1.在本地变量定义时加上static修饰符就成为了静态本地变量
2.当函数离开的时候,静态本地变量会继续存在并保持其值
3.静态本地变量的初始化只会在第一次进入这个函数时做,以后进入函数时会保持上次离开时的值

#include
int f(void);
int main(){
	f();
	f();
	f();
	return 0;
}
int f(void){
	static int all=1;
	printf("in %s all=%d\n","f",all);
	all+=2;
	printf("in %s all=%d\n","f",all);
	return all;
}
//输出结果
in f all=1
in f all=3
in f all=3
in f all=5
in f all=5
in f all=7

静态本地变量
1.静态本地变量实际上是特殊的全局变量
2.它们位于相同的内存区域
3.静态本地变量具有全局的生存期,函数内的局部作用域
static在这里的意思是局部作用域(本地可访问)
53.后记:返回指针的函数,使用全局变量的贴士
*返回指针的函数
1.返回本地变量的地址是危险的
2.返回全局变量或静态变量的地址是安全的
53.后记:返回指针的函数,使用全局变量的贴士
*返回指针的函数
1.返回本地变量的地址是危险的
//如果函数里定义的是本地变量,最好不要返回本地变量的地址
2.返回全局变量或静态变量的地址是安全的
//全局变量或静态变量返回地址是可以的
3.返回在函数内malloc的内存是安全的,但是容易造成问题
4.最好的做法是返回传入的指针
tips
1.不要使用全局变量在函数间传递参数和结果
2.尽量避免使用全局变量
丰田汽车的案子
3.*使用全局变量和静态变量的函数是线程不安全的
54.宏定义
编译预处理指令
1.#开头的事编译预处理指令
2.它们不是C语言的成分,但是C语言程序离不开它们
3.#define用来定义一个宏

#include
#define PI 3.14
#define FORMAT "%f\n"
#define PI2 2*PI//pi*2
#define PRT printf("%f\n",PI);\
            printf("%f",PI2)
int main(){
    PRT;
    return 0;
}
//输出结果
3.140000
6.280000

没有值的宏
1.#define _DEBUG
2.这类宏是用于条件编译的,后面有其他的编译预处理指令来检查这个宏是否已经被定义过了
55.带参数的宏

/*
像函数的宏
1.#define cube(x)((x)*(x)*(x))
2.宏可以带参数
*/
#include "stdio.h"
#define cube(x)((x)*(x)*(x))
int main(){
    printf("%d\n",cube(5));
    return 0;
}
//输出结果
125
/*
错误定义的宏
1.#define RADTODEG(x) (x*57.29578)
2.#define RADTODEG(x) (x)*57.29578
*/
/*
带参数的宏的原则
1.一切都要括号
	整个值要括号
	参数出现的每个地方都要括号
2.#define RADTODEG(x) ((x)*57.29578)
*/
/*
带参数的宏
1.可以带多个参数
	#define MIN(a,b) ((a)>(b)?(b):(a))
2.也可以组合(嵌套)使用其他宏
3.在大型程序的代码中使用非常普遍
4.可以非常复杂,如“产生”函数
	在#和##这两个运算符的帮助下
5.存在中西方文化差异
6.部分宏会被inline函数替代
*/

56.格式化输入输出

%[flags][width][.prec][hlL]type
flags:
	-:左对齐
	+:在前面放+-
	(space):整数留空
	0:0填充
width或prec:
	number:最小字符数
	*:下一个参数是字符数
	.number:小数点后的位数
	.*:下一个参数是小数点后的位数
类型修饰:
	hh:单个字节
	h:short
	l:long
	ll:long long
	L:long double

翁凯C语言学习笔记_第6张图片
翁凯C语言学习笔记_第7张图片
翁凯C语言学习笔记_第8张图片
57.文件的输入输出
用>和<做重定向

//FILE
1.FILE* fopen(const char* restrict path,const char* restrict mode);
2.int fclose(FILE* stream);
3.fscanf(FILE*,...);
4.fprintf(FILE*,...);
//打开文件的标准代码
FILE* fp=fopen("file","r");
if(fp){
	fscanf(fp,...);
	fclose(fp);
}else{
	...
}

翁凯C语言学习笔记_第9张图片
58.二进制文件

1.其实所有的文件最终都是二进制的
2.文本文件无非是用最简单的方式可以读写的文件
	more、tail
	cat
	vi
3.而二进制文件是需要专门的程序来读写的文件
4.文本文件的输入输出是格式化,可能经过转码
//二进制读写
1.size_t fread(void *restrict ptr,size_t size,size_t nitems,FILE *restrict stream);
2.size_t fwrite(const void *restrict ptr,size_t size,size_t nitems,FILE *restrict stream);
3.注意FILE指针是最后一个参数
4.返回的是成功读写的字节数

你可能感兴趣的:(C语言,c语言)