c语言学习

#include<stdio.h>
int main(){
#ifdef ABC
	printf("----------------------%s---------------\n",__FILE__);
#endif
	printf("hello,world!\n");
	return 0;
}
/*
编译:
 gcc -DABC -o 001 001.c
运行输出:
----------------------001.c---------------
hello,world!
*/
#include<stdio.h>
#define Day(x) #x //相当于把x的两边加上双引号
#define HDay(x) variety_name##x //相当于对变量名进行控制
int main(){
	printf(Day(abc\n));	
	int variety_name1 = 10;
	int variety_name2 = 20;
	printf("%d\n",HDay(1));
	printf("%d\n",HDay(2));
	return 0;
}
/*
输出:
abc
10
20
*/
/*
sizeof是编译器提供给我们的一个关键字,在什么环境下(包括裸板)都可以使用
所以sizeof并不是一个函数
*/
#include<stdio.h>
int main(){
	int a = 3;
	printf("%d\n",sizeof a);
	printf("%d\n",010);//以0开始的数字被编译器当做8
	return 0;
}

C操作对象:资源/内存(内存类型的资源,LCD缓存、LED灯)
C语言如何描述这些属性那?
资源属性【大小】
限制内存(土地)的大小,关键字
int a;
sizeof(a)占用多大的内存?数据类型的大小涉及到intchar时,不是一个定义,
这时一个和编译器有关的答案,数据类型的大小一般由默认值指定。

硬件芯片操作的最小单位:
bit 1 0
软件操作的最小单位:8bit == 1B
4M	4Mbit    Kbit/s KB/s
char a;		bit a

应用场景:
硬件处理的最小单位
char buf[xx];	int buf[x];
ASCII表	8bit
溢出问题:
1 = 2
2 = 4
3 = 8
4 = 16
...
8bit == 256
char a = 300;这是错误的
=============================
编译器最优的处理大小:
系统一个周期所能接受的最大处理单位,int
32bit 4B int
16bit 2B int

int a;	char a;

==========================
类型常量
char a = 300, 300l 300L(用来描述一个int)
2B 65535
  int a = 65535;
进制表示:
10
十进制	八进制	十六进制	二进制

3bit 8进制
111	 0x7
1000 0x8 int a = 010 //8

12 001 010
4bit 16进制 int a = 0x10 //16

longshort是补齐charint的短板
long是扩展的,longlong long
===========================
无符号: 数据
有符号: 数字
内存空间的最高字节 是符号位 还是数据
unsigned int a;
char a;

>>
char a = -1; 0xff
a >> 1,有符号数右移会进行符号位的扩展

unsigned char b = -1;
b>>1,无符号数据的右移会导致在最高位使用0进行填充
*/
#include<stdio.h>
//#define MOD 0
//#define TUE 1
//#define WED 2
//使用枚举的方式和使用上面的使用define定义的方式效果是相同的
//enum abc{MOD,TUE,WED};//这种方式按照0 1 2 的方式赋值
enum abc{MOD=100,TUE,WED};//这种方式按照100 101 102 的方式赋值
//enum {MOD=100,TUE,WED};//也可以去掉前面的变量名abc
int main(){
	printf("the %d\n",MOD);//这条语句的输出结果是:the 0	
	printf("the %d\n",WED);//这条语句的输出结果是:the 0	
	enum abc a1;
	printf("the sizeof of a1 is %lu\n",sizeof a1);//输出的结果是:the a1 is 4
	enum abc a2 = WED;
	printf("the sizeof of a2 is %lu,%d\n",sizeof a2,a2);//输出的结果是:the sizeof of a2 is 4,102
	enum abc a3 = 800;
	printf("the sizeof of a3 is %lu,%d\n",sizeof a3,a3);//输出的结果是:the sizeof of a3 is 4,102
	对于gcc编译器来说,不管有多少个,enum是一个语义的范畴,
	可以把enum当做是任何人之间交流的符号,相当于使用一个组合来描述一个人。
	比如驱动变成,芯片a支持3种功能,芯片b支持4种功能。如传的1表示功能一,
	传的2表示功能二。所以可以把跟芯片有关的功能可以起个名字,另外一个芯片再
	定义一个共用体。
	应用举例:可以看出,使用数字来表示功能,让资源和程序员之间更好地交流
	enum c509cmd{TotalReset = 0<<11,SelectWindow = 1<<11,StartCoax = 2<<11};
	*/
}
#include<stdio.h>
typedef int time_t;
int main(){
	//len_t a = 170;
	//time_typedef t = 3600;
	//当我们看到 xxx_t时,第一反应是这个是使用typedef定义的变量,这在linux高级编程中会有体现,这个是不成文的规定
	time_t time1 = 360;
	printf("%d秒\n",time1);//输出:360秒
	return 0;
}
#include<stdio.h>
int main(){
	
	对内存资源存放位置的限制
	类型修饰符:auto,register,static,const,extern,volatile
	如:auto char a;
	区域如果在{}内部,就认为是栈空间
	auto int a;
	register int a;//限制变量定义在寄存器上的修饰符
	*/
	return 0;
}

	char *p

	//const char和char const修饰的是指针指向的内容。而指针仍然具有指向不同的内存单元的能力。
	const char *p//字符串。这两种用法相同,这种推荐。指针p指向的内容是只读的,尽管在黑客眼里并不完全是这样。
	char const *p;

	//下面表示指针只具有单指向。一般是硬件资源的定义。如显卡的缓存的地址是固定的
	char * const p;//这两种用法相同,这种推荐,也就是说,指向的地址不变,地址内的内容可变
	char *p const;


	const char * const p;//ROM,地址和地址指向的内容都不可变
*/
#include<stdio.h>

int main(){
	char *p1 = "hello world!\n";//双引号是常量,改变时会发生段错误。双引号相当于const char
	//*p1 = 'a';//这句会发生段错误
	char buf[] = {"hello world!\n"};
	char *p2 = buf;
	*p2 = 'a';
	printf("%s\n",buf);
return 0;
}
#include<stdio.h>

	switch(day){//这种方式比较浪费时间并且比较麻烦
		case 1:
			fun1();
			break;
		case 2:
			func2();
			break;
	}
	下面的方式是改进:
		int (*p[7])(int,int);//函数名是地址这个思想很重要,知道了地址就可以进行调用
		p[0] = fun1;//这个叫做注册
		p[1] = fun2;

		p[day](10,20);//这个叫做回调
*/

int main(){
	int (*myshow)(const char *,...);	
	printf("the printf is %p\n",printf);
	myshow = printf;//看到printf,要首先想到这是一个地址
	//myshow = (int (*)(const char *,...))0x12345657;//如果这个printf换成一个十六进制的地址,那么需要通过强制类型转换转换为合适的类型,由此可见,函数名就是一个地址。
	myshow("==========================\n");

	return 0;
}
#include<stdio.h>
struct abc{
	char a;
	short e;
	int b;
};
struct my{
	char a;
	int e;
	short b;
};

int main()
{
	struct abc buf;
	struct my myy;
	printf("%d,%d\n",sizeof(buf),sizeof(myy));
	return 0;
}

	volatile: 防止优化指向内存地址
	volatile char *p;//对指针指向的内容进行修饰
	什么类型,变量的名称是谁

	char *name_t;  name_t是一个指针,指向一个char类型的内存
	typedef char *name_t; name_t是一个指针类型的名称,指向了一个char类型的内存

	int *p = xxx;[0x12]
	p+1[0x12 + 1 * (sizeof(*p))],也就是说,p+1指向下一个单位
	指针的加法运算,实际上加的是一个单位

	p++ p--  更新地址。原来的值可能就没有了

	地址内容的标签访问方式,取出标签里面内容的值。[]方括号的使用。变量名[n],n是ID或者标签。非线性访问,对多个单位跨越访问。

	强制类型转换,建议大量使用
	int a = 0x12345678;
	int b = 0x99999999;
	int *p1 = &b;
	char *p2 = (char *)&b;//这种方式是以char的方式读取内存
*/

	static的用法
		应用场景:
		1)函数内部的变量
		2) 函数外部的变量
		3)函数的修饰符
			int fun() ===> static int fun()
	extern 外部声明
	const  代表常量的定义,const是C语言的软肋,希望是常量,const int a = 100
			但是,只读的变量,但是,还是可以通过某些方法改变这些值。只不过不能通过在编译器中进行显式的修改。
	volatile 告知编译器编译方法的关键字,不优化编译
	修饰变量值的更改不仅仅通过软件,也可以通过其他技术(硬件,外部的用户)
		int a = 100;//如果是接收按键的输入
		while(a == 100 );
		mylcd()
		------------------
		[a]: a的地址
		f1:	LDR R0,[a]
		f2: CMP R0,100
		f3: JMPeq f1    ----->  优化之后:JMPEQ f2
		f4: mylcd()
		上面4行代码的哪一行是影响执行效率的,答案是f1,和内存地址打交道。f1有么有必要存在?
		不优化:
		优化:

	运算符
	*,不同的运算符的场景是不同的。
	CPU
	int a = b * 10;//有些cpu没有实现乘法的硬件,因此要利用软件的模拟方法来实现乘法
	int a = b + 10;
	乘除法有时候能不用先不用	
	% 用于求模
*/
#include<stdio.h>
int fun(){
	static int a = 12;
	return 1;
}
int main(){
	static int a = 10;	
	printf("hello,world\n");
	return 0;
}

	char *p

	//const char和char const修饰的是指针指向的内容。而指针仍然具有指向不同的内存单元的能力。
	const char *p//字符串。这两种用法相同,这种推荐。指针p指向的内容是只读的,尽管在黑客眼里并不完全是这样。
	char const *p;

	//下面表示指针只具有单指向。一般是硬件资源的定义。如显卡的缓存的地址是固定的
	char * const p;//这两种用法相同,这种推荐,也就是说,指向的地址不变,地址内的内容可变
	char *p const;


	const char * const p;//ROM,地址和地址指向的内容都不可变
*/
#include<stdio.h>

int main(){
	char *p1 = "hello world!\n";//双引号是常量,改变时会发生段错误。双引号相当于const char
	//*p1 = 'a';//这句会发生段错误
	char buf[] = {"hello world!\n"};
	char *p2 = buf;
	*p2 = 'a';
	printf("%s\n",buf);
return 0;
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

/*
	实参 传递给 形参
	传递的形式:
		拷贝(包括值传递和地址传递也是)
		逐一拷贝的概念
*/
#include<stdio.h>
void myswap(void){//传递的参数为空

}

void myswap2(int buf){//传递的参数不为空
	printf("buf is %x\n",buf);//以十六进制的形式输出
}

void myswap3(char buf){
	printf("the buf is %x\n",buf);
}
int main(){
	int a = 20;
	myswap();
	myswap2(0x123);// buf = 0x123
	myswap3(0x1234);// buf = 0x1234,但是建议实参和形参之间的内存大小的一致性

	char *p = "hello world\n";//p中存放的是地址
	printf("the p is %x,sizeof(p)=%d\n",p,sizeof(p));
	myswap2(p);
	return 0;
}
/*
	运行结果:
	buf is 123
	the buf is 34
	the p is 8846a79e,sizeof(p)=8
	buf is 8846a79e

*/
/*
	值传递	
	地址传递
*/
#include<stdio.h>
void swap(int a,int b){//值传递
	int c;
	c = a;
	a = b;
	b = c;
}

void swap_2(int *a,int *b){//地址传递,指针是标识变量的唯一...	
//上层调用者让下层子函数修改自己空间值的方式
	int c;
	c = *a;
	*a = *b;//使用指针来改变内存单元中的内容
	*b = c;
}
int main(){
	int a = 20;
	int b = 30;
	int c;
	printf("the a is %d, the b is %d\n",a,b);
//	c = a;
//	a = b;
//	b = c;
	swap(a,b);//传递进去的是值
	printf("after swap,the a is %d, the b is %d\n",a,b);
	//下面是地址传递
	swap_2(&a,&b);//传递进去的是指针(地址)
	printf("after swap_2,the a is %d, the b is %d\n",a,b);

	return 0;
}

	连续空间的传递	
	传感器采集的数据,交给专门的处理函数来处理
	处理完成之后告诉应用程序
	1.数组
		数组名 --- 标签
		实参:
			int abc[10];
			fun(abc);//实参也是地址
		形参:
			void fun(int *p);//那么形参也就是指针
			void fun(int p[10]);//这种是给人看的,实际上p还是和上一行一样,是地址
	2.结构体
		结构体变量,建议使用指针
		struct abc{int a;int b;int c;};
		struct abc buf;
		实参:
			fun(buf);
			fun(&buf);//推荐
		形参:
			void fun(struct abc a1) //这种方式可能存在多分拷贝,浪费内存
			void fun(struct abc *a2) //推荐,节约内存
	构成连续空间的两个主要因素,虽然语法不太一样。
	


	需要考虑的问题:连续空间的可写性
	void fun(char *p);//这种形式定义,p指向的内存很大可能可变
	void fun(const char *p);//这种形式定义:p指向的内存绝对不可变
	const char *p;//只读空间,为了空间看看。这种绝对变不了
	以后看到:char *p;//则很大可能性该空间可以修改

	strcpy(char *dest,const char *src);
	strncpy(char *dest,const char *src,int n);

*/
#include<stdio.h>
void fun(char *c){
	while(*c){
		printf("%c",*c);	
		c++;
	}
	printf("\n");
}
void fun_2(char *c){
	*c = 'i';
}
int main(){
	fun("hello,world");//双引号,常量区,修改则出现段错误。实际上,传递进去的实参是首地址	
	char buf[] = "hello,world\n";
	fun_2(buf);
	printf("%s",buf);
	return 0;
}
/*
	输出:
	hello,world
	iello,world	
*/

	1、修改 int *,char *,...
	2、空间传递
		2.1 子函数查看空间里的情况 const
		2.2 子函数反向修改上层空间里的内存(这次将这个)
			空间的分类:字符空间和非字符空间

			p[1000],p[100],p[10000],可以无穷访问?
			字符空间:结束标志不同
			空间:空间首地址、结束标志
			
			结束标志:内存里存放了0x00(1B),字符空间。非字符空间中0x00不能作为结束标志
			
			----->
			void fun(char *p){
				int i = 0;
				p[i] i++;//从头找到尾,跟循环有关系,找到结束标记,while循环
				while(p[i] != 0){
					p[i]操作  p[i] = x; a = p[i] + -
					i++;
				}
			}
	
	strcpy(char *dest,const char *src)
	"" --> 初始化const char *
	char buf[10] ---> 初始化char *



	非字符空间
	void fun(unsigned char *p)
	{
		p[100] = xx
		p[1000] = yy;//非字符空间,我们最关心的还是结束标记
		即:数量,一般都是以字节为单位
	}
	void fun(unsigned char *p,int len)//一般这样写
	{
		int i;
		for(i = 0;i < len;i++){
			p[i] = ; a = p[i] //++++++ -----	
		}
	}

	使用void *,形参化的类型。void *,非字符空间或者数据空间的标识
	void还得跟一个大小,没有结束标记
	memcpy(void *dest,const void *src,size_t n)
	int main(){
		struct sensor_data buf;
		int buf1[100];
		fun(&buf,sizeof(buf)*1)
	}


	ssize_t recv(int sockdd,void *buf,size_t len,int flags)

	//遇到void *,尽量开unsigned char。char buf[100],这里应当定义为unsigned char buf[100]
	recv(buf); 00 00 hello
	printf("%s",buf);//且不要使用%s
	------------------------------------------------------------	
	int fun1(void)     int a = 0; a = fun1();//这样可以修改a
	void fun2(int *p)  int a = 0; fun2(&a);//这样可以修改a

	int fun(int *) //承上启下改两个

	int *fun1(void);//改写成
	int main()
	{
		int *p;
		p = fun1();//可以认为p被改变了
	}


	void fun2(int *p)
	int main()
	{
		int *p;
		//fun2(p);//p是值传递,不能改变p,尽管p指向的内容不敢保证不变
		fun2(p);
	}

	void fun3(int **p)//引申
	int main()
	{
		int *p;
		fun3(&p);//传入的是p的地址,p本身是一个指针,这样p的内容就能够发生改变了
	}
	----------------------------------------------------------
	指针作为空间返回的唯一数据类型
	int *fun();

	作为函数的设计者,必须保证函数返回的地址所指向的空间是合法的【不是局部变量】
	使用者:
	int *fun();
	int *p = fun();
	int p[100];
	---------------------------------------
	char *fun(void)
	{
		//static char buf[] = "hello world";	
		char *s = (char *)malloc(100)
		strcpy(s,"hello world");
		return s;
	}
	int main(){
		char *p;
		p = fun();
		printf("the p is %s\n",p);
		free(p);
		return 0;
	}
	
*/
#include<stdio.h>
char *fun(void){
	char buf[] = "hello world";
	//return "hello world";
	return buf;//buf占用的是fun的栈空间,fun函数执行结束是,也就销毁了
}
int main(){
	char *p;
	p = fun();
	printf("the p is %s\n",p);

	return 0;
}
/*
	宏定义:
	用预处理指令#define声明一个常数,用以表明1年中有多少秒

	c语言中:宏名:大写字母表示

*/
#include<stdio.h>
//#define SECOND_OF_YEAR (1234556)//这个写法有点杞人忧天
#define SECOND_OF_YEAR (365*24*3600UL) //这个UL要写在括号的内部
//常量表达式,编译的过程中计算机就能够算出来值了
//宏定义一定要注意括号的问题,需要使用括号进行保护。
int main(){
	int a = SECOND_OF_YEAR;
	printf("%d\n",a);	

	return 0;
}

	数据申明
	申请什么样的空间,以及如何使用
	a)一个整型数
		int a
	b)一个指向整型数的指针
		int *a;
	c)一个指向指针的指针,它指向的指针是指向一个整型数
		int **a;
	d)一个有10个整型数的数组
		int a[10];
	e)一个有10个指针的数组,该指针是指向一个整型数的
		int *a[10];
	f)一个指向有10个整型数的数组的指针(一个指针,指向的是数组)
		int (*a)[10]//这个时候先看左边的括号,则表明是一个指针,
		int [10] *a =====>  int *a [10]

	g)一个指向函数的指针,该函数有一个整型参数并返回一个整型数
		int (*a)(int) //函数名就是一个地址
	h)一个有10个指针的数组,该指针指向一个函数。该函数有一个整型参数并返回一个整型参数
	int (*a[10])(int)
*/


	关键字static的作用是什么?
	1、修饰局部变量
		默认局部变量在栈空间存在,生存期比较短
		加上static之后,叫做局部静态化,局部变量保存在静态数据段中保存(整个程序结束才结束,函数返回过后还在)
	2、修饰全局变量
		防止重命名,限制变量名只在本文件内起作用。函数名和变量名都是标签。
		static int a;只在本文件内部起作用,对于外部的源文件不可见。
	3、修饰全局函数
		防止重命名,限制该函数只在本文件内起作用


	关键字const的作用是什么?
	C:只读,建议性,不具备强制性   != 常量
	const int a = 100;//只用指针的形式可以修改
	c++: 常量,C++编译器对const做了一些比较严格的定义

	关键字volatile有什么含义?并给出三个不同的例子
	防止C语言编译器的优化
	它修饰的变量,该变量的修改只能通过第三方来修改。
	当涉及到计数器、多线程和中断时,也就涉及到了第三方。

	嵌入式系统系统总是要用户对变量或寄存器进行位操作。给定一个整型变量a,
	写两段代码,第一个设置a的bit 3,第二个清楚a的bit 3.在以上两个操作中,
	要保证其他位不变。
	unsigned int a;
	a = a | (0x1<<3)
	a = a & (~0x1<<3)

	在某工程中,要求设置一绝对地址为0x67a9的整型变量为0xaa66.编译器是一个
	纯粹的ANSI编译器。写代码去完成这一任务
	int *p = (int *)0x67a9;
	p[0] = 0xaa66; 或者*p = 0xaa66;	

	*((int *)0x67a9) = 0xaa66;
	(
		( void (*)(void) ) 0x67a9
	)();
*/
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值