您的位置:首页 > 编程语言 > C语言/C++

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

2015-09-28 22:09 423 查看
fgets():是一个文件操作相关的函数,暂时使用这个函数可以从键盘接受一个字符串,保存在数组中
原型:fgets(char *p, int len, FILE)
键盘接收:fgets(str, 50,
stdin) //键盘输入缓冲区

接受字符串的方法:char str[50]
1、scanf(“%s”, str) //缺点:不能接收空格,可能越界,不安全
2、gets(str) //优点:可以接受空格
//会有一个警告:当字符串长度为50时,就没有空间存结束符\0

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

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("内存申请失败!\n");
}


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\n", 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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: