当前位置: 首页 > >

C语言第七节-结构体-枚举-typedef

发布时间:

fgets():是一个文件操作相关的函数,暂时使用这个函数可以从键盘接受一个字符串,保存在数组中


原型:fgets(char *p, int len, FILE)


键盘接收:fgets(str, 50, stdin) ?//键盘输入缓冲区


? ? ? ? ? 接受字符串的方法:char str[50]


? ? ? ? ? 1、scanf(
“%s”
, str) ? ?//缺点:不能接收空格,可能越界,不安全


? ? ? ? ? 2、gets(str) ? ? ? ? ? ? ? ?//优点:可以接受空格


? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?//会有一个警告:当字符串长度为50时,就没有空间存结束符



? ? ? ? ? 3、fgets() ? ? ? ? ?//继承1,2的优点,弥补2的缺点,当长度大于50,自动在第50个位置存上,替换输入的第50个字符;小于50时,会保存回车符
,然后再存上







fputs():也是一个文件操作相关的函数


原型:fputs(char *p , FILE)


fputs(数组名,stdout) ? ? ? ? ? ? ? ? //输出到控制台,和puts一样也不能格式化输出,但是puts可以自动换行







const:类型修饰符,使用const修饰变量则可以让变量的值不能修改






const使用的地方:


1、修饰变量:使得变量变常量,不可改


2、修饰指针变量:


? ? ?1)
const?
int? *P ??
指向可变,指向的变量的值不可变


? ? ?2) int? *?
const?
p ??
指向不可变,指向的变量的值可变


? ? ?3)const int * const p ?
指向不可变,指向的变量的值也不可变


记忆技巧:const和*的位置,左,右,两则


3、修饰数组



内存管理的概念和内存分区


概念:是指软件运行时对计算机内存资源的分配和使用技术。主要目的是如何高效,快速地分配,并且再适合的时候释放和回收内存资源。


内存的分配方式:


1、从静态存储区域分配


2、从栈上分配


3、从堆上分配,亦称动态内存分配











内存分区:


BSS段:未初始化的全局变量和静态变量


数据段(常量区):已初始化的全局变量和静态变量,字符串常量


代码段:程序执行代码的一块内存区域


堆(heap):动态分配的内存段


栈(stack):用户存放程序临时创建的局部变量






常见内存分配函数(堆区):


1、malloc


? ? ? ? ? ? ? ?void *malloc (unsigned size),size是内存分配的字节,库函数stdlib.h,一般要判断是否分配成功


? ? ? ? ? ? ? ?从内存的堆区分配大小为size个字节的连续的内存空间


? ? ? ? ? ? ? ?如果内存分配成功 返回内存的首地址


? ? ? ? ? ? ? ?失败 ?NULL


//
从内存中申请一块内存空间,可以存储
4
个整数


?? int *p = (int *)malloc(4 * sizeof(int)); ? //16个字节,void *强制转换为int *


???
if
(p!=
NULL
) {
???????
//
申请成功做的事情


???????
//p
中存放的是新申请的内存空间的首地址


? ? ? ? ? ? ? ?
//
注意:
malloc
申请的空间如果不赋值,则存放的是垃圾数


??????? *p =
10
;
??????? *(p +
1
) =
100
;
??????? *(p +
2
) =
1000
;
??????? *(p +
3
) =
10000
;
//
存放
4
个整数

??? }
else
{
???????
//
内存申请失败

???????
printf
(
"
内存申请失败!

"
);


??? }


? ? ? ? ? ? ? ? ?



2、calloc


? ? ? ? ? ? ? ?calloc(块数,长度),分配指定块数和长度的内存空间,地址也是连续的


? ? ? ?int *p = (int *)malloc(4,?sizeof(int)); ?//四块长度为4的内存


? ? ? ? ? ? ? ?calloc会自动初始化,初始化为0



3、realloc


? ? ? ? ? ? ?? 可以给已经存在的空间扩充大小


? ? ? ? ? ? ? ?p = realloc(p,40*sizeof(int) )


//想为已经存在的4个空间的P扩充36个空间,如果4的后面有36个空的内存,则返回4的首地址;如果没有36个,则另外重新申请一个40的空间,先将4里面的内容放进去之后,再返回新空间的首地址。p则指向“新”的地址



野指针和内存泄露


野指针:1、未初始化的指针 ?2、指针所指向的空间已经释放,再去使用指针,该指针就是野指针
? ?


? ? ? ? ?


内存泄露:防止内存泄露,在指针释放之前,先释放指向的空间,使用free(释放空间的首地址)函数,释放完之后,理论上不可以访问已经释放的空间,但是还是可以访问。此时的指针就是野指针,访问的结果是一些以前的垃圾值。所以为了防止这种无理的操作,我们需要:
指针初始化?>使用指针结束后?>再次赋值为NULL,以防还可以访问。free(p); p = NULL






指针函数:返回值类型是一个指针(地址)的函数


形式:类型说明符 *函数名(形参表) {


? ? ? ? ? ? ? ? ? ? ? ? ?函数体


? ? ? ? ? ? ? ? ? ? ? ? ?返回的是地址



}



函数指针:指向函数(函数的首地址就是函数名)的指针变量


形式:类型说明符 (*变量名)(函数的参数)


初始化:int (*p)(int a , int b) = ∑ ? ? //sum是一个求和函数


定义函数指针的时候可以不写形参名,但是类型必须写






构造类型及结构体



构造类型:根据已定义的一个或多个数据类型用构造的方法定义的。一个构造类型的值可以分解为若干元素,每个元素又可以分为基本类型或构造类型。


分类:数组,结构体类型,共用体(联合)类型






结构体structure:存储类型相同或不同的数据


定义结构体:


? ? ? ? ? ? ? ? ? ? struct 结构名{


? ? ? ? ? ? ? ? ? ? ? ? ?成员列表;



};



//
定义一个学生结构体

???
struct
student{
???????
//
学生学号

???????
int
ID;
???????
//
姓名

???????
char
name[
21
];
???????
//
年龄

???????
int
age;
???????
//
成绩

???????
float
score;


??? };
//分号不能省


结构变量成员访问:结构变量名.成员名 ? ?//stu.num


结构体变量的初始化:


1、先定义结构体变量,再初始化


? ? ??
struct
student
stu1;


???
//
给结构体变量初始化

??? stu1.
ID
=
38
;
??? stu1.
age
=
18
;


??? stu1.score = 59.9;


? ??
//
字符串类型的初始化,如果是数组不可以写成
stu1.name = "
张三丰
";
应该用
strcpy
函数拷贝;如果不是数组则可以。但是定义的同时初始化则也可以


???
strcpy
(stu1.
name
,
"
张三丰
"
);


??? //UTF-8国际通用编码,一个汉字占用三个字节


2、定义的同时,进行完全初始化


? ? ? ?
struct
student
stu2 = {8, “凤姐”, 18, 49.99f};
? ??


3、定义的同时,指定元素初始化


? ? ? ?
struct
student
stu3 = {.name =?“小三"};






结构体变量的存储原理:结构体占用的内存空间是每个成员占用的字节之和(考虑对齐问题)


对齐问题:






计算结构体变量在内存中占用的字节数的方法:


1、先计算对齐模数:(结构体中基本数据类型占用字节最大的那个)


2、再计算结构体变量中各个成员占用的字节和






结构体作用域:与局部变量、全局变量相似






结构数组:每一个元素都是具有相同结构类型的下标结构变量

格式: ? struct 结构名{

? ? ? ? ? 成员列表



}数组名[数组长度];


1、定义结构体的同时,定义数组:如格式


2、先定义结构体,再定义数组:struct student a[5];






结构数组初始化:











?
? 1、ctrcpy ?


? ? 2、scanf






结构体指针定义和初始化


结构指针:指向结构体变量的指针


1、







2、struct Car{


? ? ?int lunzi;


? ? ?int speed;


}*p;






结构指针间接访问成员的值


形式:(*p).成员名 ? ?或 ? ? ? p->成员名 ? ??
//(*p).lunzi ?或 ?p->lunzi?






结构体嵌套:成员可以使基本数据类型,又可以是一个结构


结构体可以嵌套其他结构的变量,不可以嵌套自己的变量,但是可以嵌套自己的指针







嵌套的结构体初始化


struct Student?stuff = {“张丹峰”, 12,33,399f,{1991,9,3}};







结构体变量名及成员,结构体指针作为函数参数


1、成员值做函数的参数:本质上就是一个值传递


2、结构体变量名作为函数的参数:也是值传递(不能修改)


3、结构体指针作为函数的参数:地址传递






枚举



枚举:变量的取值被限定在一个有限的范围内,是一种基本数据类型,不是构造类型


形式:enum 枚举类型名{枚举值表};


在枚举值表中应罗列所有的可用值,这些值也称为枚举元素


系统默认会为枚举元素赋初值,依次从0开始,下一个为上一个加1


手动赋值:可以随便赋值k,但是下一个枚举元素的值为k+1






枚举类型变量:enum 枚举类型名 变量名;



typedef


typedef:为数据类型取别名


形式:typedef 原类型名 新类型名


使用:1、基本数据类型


? ? ? ? ? ? ? ?
typedef
int
MLXG;


? ? ? ?MLXG qiezi = 3;


? ? ?2、用在数组:


? ? ? ? ? ? ? ?
typedef
int
array[
5
];


? ? ? ?array a1; ? //a1就是一个长度为5数组


? ? ?3、给结构体


? ? ? ? ? ? 1) ?
struct
Person{


???????
char
*name;
???????
int
age;
??? };
???
typedef
struct
Person
p;


??? p p1 = {"xxx", 28};






? ? ?2)
typedef
struct
Car{
???????
int
lunzi;
???????
int
speed;
??? }MYCAR;


??? MYCAR car1 = {1, 200};
//
此中的
Car
可以没有,便是给匿名的结构体取个别名


? ? ?4、给枚举类型


? ? ? ? ? ? ? ?和结构体类似,也存在匿名的取别名



? ? ?5、给函数指针:指向函数的指针 ?


?


? ? ? ??
int
sum(
int
a,
int
b) {


???
return
a + b;


?? }


? ? ? ? ? ? ? ?
int
(*p)(
int
,
int
);
???
//FUN
是一个别名


??? typedef int (*FUN)(int, int);


?????FUN
f1;


??? f1 =
sum
;


??? printf("%d
", f1(23,34));







预处理


预处理指令:以#开头的预处理命令,包括#include ,#define


预处理:在编译之前做的一些处理(宏定义,文件包含,条件编译)






宏:特殊的标识符,标识符*惯大写。字符串替换(仅仅是替换,并不会自动加括号之类的)的过程叫做宏展开或宏替换






无参宏:#define 标识符 字符串



注意:1、#undef ?取消宏



? ?2、在字符串中出的宏不会被替换


? ?3、宏可以嵌套定义


? ?4、宏可以起别名


? ?5、宏不是一个语句,不需要叫分号


有参宏:#define 宏名(形参表) 字符串


注意:1、宏的参数之间可以出现空格,但是宏名和形参之间不可以出现空格


? ? ? ? ? ?2、在带参宏中,形参不分配内存空间


? ? ? ? ? ?3、传值时,不要想当然的去代换,要简单的去替换之后,再计算。所以宏的参数最好用括号括起来


? ? ? ? ? ?4、可以用宏来定义多个语句






宏定义和typedef的区别:一个仅仅是替换,一个是别名(在指针时,区别最大)






条件编译:1、按照不同的条件编译不同的程序,产生不同的目标代码,有利于程序的移植和调试


2、条件编译只编译其中满足要求的程序,目标程序较短


1、#if -#else条件编译指令


#if


#elif


.


.


#elif


#else


#endif


2、#ifdef 用来判断某个宏是否定义 ? 如果定义了,然后执行什么


? #ifndef 用来判断某个宏 ? 如果没有定义,然后执行什么






使用条件编译指令调试BUG



友情链接: