20240325,结构嵌套,联合,全局变量,编译预处理和宏,声明

目录

二,结构 

2.3 结构中的结构

2.3.1 结构数组

2.3.2 结构中的结构【嵌套】

2.3.3 结构中的结构的数组

三,联合

3.1 类型定义 TYPEDEF

3.2 联合UNION

程序结构

一,全局变量

1.1 全局变量

1.2 静态本地变量 STATIC

1.3 返回指针的函数

二,编译预处理和宏

2.1 宏定义

2.1.1 没有值的宏 

2.1.2 预定义的宏

2.2 带参数的宏

三,大数据结构

3.1 多个源代码文件

3.2 头文件

3.3 声明

二,结构 

2.3 结构中的结构
2.3.1 结构数组
#include<stdio.h>//下一秒 
struct time{
	int hour;
	int min;
	int sed;
};
struct time timeupdate(struct time now);
int main(){
	struct time testTime[5]={
		{11,59,59},{12,0,0},{1,29,59},{23,59,59},{19,12,27} 
	};//结构数组
	int i;
	for(i=0;i<5;i++){
		printf("Time is %.2i:%.2i:%.2i\n",
		testTime[i].hour,testTime[i].min,testTime[i].sed);
		testTime[i]=timeupdate(testTime[i]);//函数运算 ,赋值 
		printf("...one second later is %.2i:%.2i:%.2i\n",
		testTime[i].hour,testTime[i].min,testTime[i].sed);
	}
	return 0;
}
struct time timeupdate(struct time now){
	++now.sed;
	if(now.sed==60){
		now.min++;
		now.sed=0;//秒的特殊情况 
		if(now.min==60){
			now.min=0;
			now.hour++;//分的 
		}if(now.hour==24){
			now.hour=0;//小时的 
		}
	}
	return now; //NOW就可以了 
}
2.3.2 结构中的结构【嵌套】
#include<stdio.h>
struct point{
	int x;
	int y;
}; 
struct rectangle{
	struct point pt1;
	struct point pt2;
};
int main(){
	struct rectangle r;
	scanf("%d %d %d %d",&r.pt1.x,&r.pt1.y,&r.pt2.x,&r.pt2.y);
	printf("%d-%d-%d-%d\n",r.pt1.x,r.pt1.y,r.pt2.x,r.pt2.y);
	return 0;
}

注:指针不能嵌套 

#include<stdio.h>
struct point{
	int x;
	int y;
}; 
struct rectangle{
	struct point pt1;
	struct point pt2;
};
int main(){
	struct rectangle *rp;
	struct rectangle r={
		{2,3},{4,5},
	};//初始化
	rp=&r;
	printf("%d-%d-%d-%d\n",r.pt1.x,r.pt1.y,r.pt2.x,r.pt2.y);
	printf("%p\n",&r);
	printf("%p\n",rp);
	printf("%d\n",r.pt1.x);
	printf("%d\n",rp->pt1.x);// 。 先算,指针指向PT1.X
	printf("%d\n",(r.pt1).x);
	printf("%d\n",(rp->pt1).x);
	//meiyou rp->pt1->x,yinwwei (rp->pt1)不是指针 
	return 0;
}
2.3.3 结构中的结构的数组

 地址和SIZE之间的关系,居然!!!!!&da,和da输出的地址不一样,只要加上&都一样

#include<stdio.h>
struct point{
	int x;
	int y;
}; 
struct rectangle{
	struct point pt1;
	struct point pt2;
};
void print(struct rectangle da);
void size(struct rectangle da);
void di(struct rectangle da);
int main(){
	struct rectangle rr[2]={
		{{0,1},{2,3}},{{3,4},{5,6}}
	}; 
	int i;
	for(i=0;i<2;i++){
		print(rr[i]);
		size(rr[i]);
		di(rr[i]);
	}
	return 0;
}
void print(struct rectangle da){
	printf("<%d-%d>-<%d-%d>\n",
	da.pt1.x,da.pt1.y,da.pt2.x,da.pt2.y);
}
void size(struct rectangle da){
	printf("%d\n",sizeof(da));
	printf("%d\n",sizeof(da.pt1));
	printf("%d\n",sizeof(da.pt1.x));
}
void di(struct rectangle da){
	printf("%p\n",da);
	printf("%p\n",&da);
	printf("%p\n",&da.pt1);
	printf("%p\n",&(da.pt1.x));
}

三,联合

3.1 类型定义 TYPEDEF

信的名字是某种类型的 别名,改善了程序的可读性
最后一个单词就是新名字

#include<stdio.h>
typedef int Lenght;
typedef struct adata{
	int mouth;
	int day;
	int year;
}Data;
int main(){
	Lenght i=8;
	printf("%d\n",i);
	Lenght a[9]={4};
	printf("%d\n",a[8]);
	Data t={10,3,2012};//DATA T==STRUCT ADATA T
	printf("%d\n",t.year);
	struct adata tt={0,0,0};
	printf("%d\n",tt.year);
	return 0;
}
3.2 联合UNION
#include<stdio.h>
typedef union {
	int i;
	char ch[sizeof(int)];//4个字节可以被看作是i,也可以被看作CHAR【4】 
}CHI;
int main(int argc,char const *argv[]){
	CHI chi;
	int i;
	chi,i=1234;//I=1234=04d2 
	for(i=0;i<sizeof(int);i++){
		printf("%02hhX",chi.ch[i]);//很多修饰符
	}//……我的只能输出一串0 
	printf("\n");
	return 0;
}

可以用来得到INT DOUBLE的内部字节,什么低位在前
感觉没懂
存储
所有的成员共享一个空间;同一时间只有一个成员是有效的;union的大小是其最大的成员
初始化
对第一个成员做初始化

程序结构

一,全局变量

1.1 全局变量

定义在函数外面的变量是全局变量
全局变量具有全局的生存期和作用域
它们与任何函数都无关
在任何函数内部都可以使用它们,在任何函数访问都是对全局变量的访问

#include<stdio.h>
int f(void);
int gALL=12;
int main(int argc,char const *argv[]){
	printf("in %s gALL=%d\n",__func__,gALL);
	//__func__表示当前这个函数的名字 
	f();
	printf("agn in %s gALL=%d\n",__func__,gALL);
	return 0;
} 
int f(void){
	printf("in %s gALL=%d\n",__func__,gALL);
	gALL+=2;
	printf("agn in %s gALL=%d\n",__func__,gALL);
	return gALL;
}

没有做初始化的全局变量会得到0值
指针会得到NULL值
只能用编译时刻已知的值来初始化全局变量
它们的初始化发生在main函数之前 

#include<stdio.h>
int f(void);
int gALL;
//X --int gALL=f()
//可以但是不建议
//const int g2=12;
//int gALL=g2; 
int main(int argc,char const *argv[]){
	printf("in %s gALL=%d\n",__func__,gALL);
	//__func__表示当前这个函数的名字 
	f();
	printf("agn in %s gALL=%d\n",__func__,gALL);
	return 0;
} 
int f(void){
	printf("in %s gALL=%d\n",__func__,gALL);
	gALL+=2;
	printf("agn in %s gALL=%d\n",__func__,gALL);
	return gALL;
}

如果函数内部存在与全局变量同名的变量,则全局变量被隐藏

#include<stdio.h>
int f(void);
int gALL;
int main(int argc,char const *argv[]){
	printf("in %s gALL=%d\n",__func__,gALL);
	//__func__表示当前这个函数的名字 
	f();
	printf("agn in %s gALL=%d\n",__func__,gALL);
	return 0;
} 
int f(void){
	int gALL=12;
	printf("in %s gALL=%d\n",__func__,gALL);
	gALL+=2;
	printf("agn in %s gALL=%d\n",__func__,gALL);
	return gALL;
}
1.2 静态本地变量 STATIC

在本地变量定义时加上static修饰符就成为静态本地变量
当函数离开的时候,静态本地变量会继续存在并保持其值
静态本地变量的初始化只会在第一次进入这个函数时做,以后进入函数时会保持上次离开时的值

#include<stdio.h>
int f(void);
int gALL=12;
int main(int argc,char const *argv[]){
	f();
	f();
	f();
	return 0;
} 
int f(void){
	int all=1;
	static int tll=1;
	printf("in %s gALL=%d\n",__func__,gALL);
	printf("in %s all=%d\n",__func__,all);
	printf("in %s tll=%d\n",__func__,tll);
	gALL+=2;
	all+=2;
	tll+=2;
	printf("agn in %s gALL=%d\n",__func__,gALL);
	printf("in %s all=%d\n",__func__,all);
	printf("in %s tll=%d\n",__func__,tll);
	printf("\n");
	return gALL;
}

静态本地变量实际上是特殊的全局变量
它们位于相同的内存区域
静态本地变量具有全局的生存期,函数内的局部作用域
static在这里的意思是局部作用域(本地可访问) 

#include<stdio.h>
int f(void);
int gALL=12;
int main(int argc,char const *argv[]){
	f();
	return 0;
} 
int f(void){
	int all=1;
	static int tll=1;
	printf("&gALL=%xp\n",&gALL);
	printf("&tll=%xp\n",&tll);
	printf("&all=%xp\n",&all);
	return gALL;
}
1.3 返回指针的函数

返回本地变量的地址是危险的
返回全局变量或静态本地变量的地址是安全的
返回在函数内malloc的内存是安全的,但是容易造成问题
最好的做法是返回传入的指针

#include<stdio.h>
int* f(void);
void g(void);
int main(int argc,char const *argv[]){
	int *p=f();
	printf("*p=%d\n",*p);
	g();
	printf("*p=%d\n",*p);
	return 0;
} 
int* f(void){
	int i=12;
	printf("&i=%p\n",&i);
	return &i;
}//woring address of variable"i" returned
void g(void){
	int k=24;
	printf("k=%d\n",k);
	printf("&k=%p\n",&k);
}

I的地址,在出了函数F之后,进入函数G,又分配给了G使用

不要使用全局变量来在函数间传递参数和结果尽量避免使用全局变量
丰田汽车的案子
使用全局变量和静态本地变量的函数是线程不安全的 

二,编译预处理和宏

2.1 宏定义

#开头的是编译预处理指令
它们不是C语言的成分,但是C语言程序离不开它们
#define用来定义一个宏

#include<stdio.h>
const double PI=3.14159;
#define PI1 3.14159//没有等号,没有分号 
int main(int argc,char const *argv[]){
	printf("%f\n",2*PI*3.0);
	printf("%f\n",2*PI1*3.0);
	return 0;
} 

--save--temps【GCC】
.C源文件——》.I中间结果文件——》.S汇编代码文件——》.O目标代码文件——》.OUT

#include<stdio.h>
const double PI=3.14159;
#define PI1 3.14159//没有等号,没有分号 
#define FORMAT "%f\n"
int main(int argc,char const *argv[]){
	printf("%f\n",2*PI*3.0);
	printf("%f\n",2*PI1*3.0);
	printf(FORMAT,2*PI1*3.0);
	printf("FORMAT\n",2*PI1*3.0);
	return 0;
} 

·#define<名字><值>
注意没有结尾的分号,因为不是C的语句名字必须是一个单词,值可以是各种东西
在C语言的编译器开始编译之前,编译预处理程序(cpp)会把程序中的名字换成值
·完全的文本替换 

#include<stdio.h>
#define PI 3.14159
#define PI2 2*PI//不能带空格,纯文本替换 
#define PI3 printf("%f\n",PI);\
			printf("%f\n",PI2)
			
int main(int argc,char const *argv[]){
	printf("%f\n",2*PI*3.0);
	printf("%f\n",2*PI2*3.0);
	PI3;
	return 0;
} 
2.1.1 没有值的宏 

#define DEBUG
这类宏是用于条件编译的,后面有其他的编译预处理指令来检查这个宏是否已经被定义过了

2.1.2 预定义的宏
//__LINE__当前源代码文件的行号
//__FILE__源代码文件的文件名
//__DATE__,__TIME__当前源代码创建日期,时间
//__STDC__ ,当编译器以 ANSI 标准编译时,则定义为 1;判断该文件是不是标准 C 程序。
#include<stdio.h>
int main(int argc,char const *argv[]){
	
	printf("文件名%s:行号%d\n",__FILE__,__LINE__);
	printf("日期%s,时间%s\n",__DATE__,__TIME__); 
	return 0;
} 
2.2 带参数的宏

原则:一切都要括号
整个值要括号
参数出现的每个地方都要括号

#include<stdio.h>
#define cube(x) ((x)*(x)*(x)) 
#define dada(x) (x)*50.343
#define dada2(x) x*50.343
int main(int argc,char const *argv[]){
	int i;
	scanf("%d",&i);
	printf("%d\n",cube(i));
	printf("%d\n",cube(i+2));
	printf("%f\n",dada(i+2)); 
	printf("%f\n",dada2(i+2));
	
	printf("%f\n",180/dada(2+1));// 180/(2+1)*50.343
	printf("%f\n",dada2(5+2));// 5+2*50.343
	return 0;
} 

带多个参数,也可以组合(嵌套)其他宏

#include<stdio.h>
#define MIN(a,b) ((a)>(b)?(b):(a)) 
int main(int argc,char const *argv[]){
	int a=5,b=10;
	printf("%d\n",MIN(a,b));//a>b?b:a不懂 
	return 0;
} 
//表达式1 ? 表达式2 :表达式3
如果 1 为真,则2,否则3
三目运算符?

千万不要加分号哦!#不是C?啥,用空间换取效率
在大型程序的代码中使用非常普遍 ,可以非常复杂,如“产生”函数--在#和##这两个运算符的帮助下,存在中西方文化差异
什么涉及到多个参数类型没人检查?
部分宏会被inline函数替代 

其他编译预处理指令,条件编译,REEOR……看不懂,先放着,丢

三,大数据结构

3.1 多个源代码文件

 单个.C文件拆成多个.C文件……建项目,CEV C++允许不建项目只建一个文件
一个.c文件是一个编译单元
编译器每次编译只处理一个编译单元

#include<stdio.h>
int max(int a,int b) ;
int main(){
	int a=7;
	int b=8;
	printf("%d\n",max(a,b));
	return 0;
} 
int max(int a,int b){
	return a>b?a:b;
}
3.2 头文件
#include<stdio.h>
int main(){
	int a=7;
	int b=8;
	printf("%d\n",max(a,b));
	return 0;
} 
double max(double a,double b){
	return a>b?a:b;
}

出错,假笑

把函数原型放到一个头文件(以.h结尾)中,在需要调用这个函数的源代码文件(.c文件)中#include这个头文件,就能让编译器在编译的时候知道函数的原型

#include<stdio.h>
#include "max.h" //要和文件名一样哦 
int main(){
	int a=7;
	int b=8;
	printf("%f\n",max(a,b));
	return 0;
} 
#include "max.h" 
double max(double a,double b){
	return a>b?a:b;
}
double max(double a,double b);

 #include是一个编译预处理指令,和宏一样,在编译之前就处理了
它把那个文件的全部文本内容原封不动地插入到它所在的地方
所以也不是一定要在.c文件的最前面#include

#include有两种形式来指出要插入的文件
“”要求编译器首先在当前目录(.c文件所在的目录)寻找这个文件,如果没有,到编译器指定的目录去找
<>让编译器只在指定的目录去找
编译器自己知道自己的标准库的头文件在哪里环境变量和编译器命令行参数也可以指定寻找头文件的目录

#include不是用来引入库的
stdio.h里只有printf的原型,printf的代码在另外的地方,某个.lib(Windows)或.a(Unix)中
现在的C语言编译器默认会引入所有的标准库
#include <stdio.h>只是为了让编译器知道printf函数的原型,保证你调用时给出的参数值是正确的类型

声明——在使用和定义这个函数的地方都应该#include这个头文件
一般的做法就是任何.c都有对应的同名的.h,把所有对外公开的函数的原型和全局变量的声明都放进去

不对外公开的函数——在函数前面加上static就使得它成为只能在所在的编译单元中被使用的函数
在全局变量前面加上static就使得它成为只能在所在的编译单元中被使用的全局变量

3.3 声明
#include<stdio.h>
#include "max.h" //要和文件名一样哦 
int main(){
	int a=7;
	printf("%d\n",max(a,g));
	return 0;
} 

#include "max.h" 
int g=12; //定义 
int max(int a,int b){
	return a>b?a:b;
}

int max(int a,int b);
extern int g; //声明,不能初始化 

声明是不产生代码的东西;函数原型,变量声明,结构声明,宏声明,枚举声明,类型声明,inline函数
定义是产生代码的东西;函数,全局变量

只有声明可以被放在头文件中
是规则不是法律
否则会造成一个项目中多个编译单元里有重名的实体*某些编译器允许几个编译单元中存在同名的函数,或者用weak修饰符来强调这种存在

同一个编译单元里,同名的结构不能被重复声明
如果你的头文件里有结构的声明,很难这个头文件不会在一个编译单元里被#include多次
所以需要标准头文件结构

#ifndef _MAX_H_
#define _MAX_H_
//如果没有定义过
//那么定义 

int max(int a,int b);
extern int g; 

#endif

暴躁这个头文件在一个编译单元中只会#INCLUDE一次
#pragmaonce也能起到相同的作用,但是不是所有的编译器都支持

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值