复习:
结构:是一种由程序员自己设计的数据类型,它用于描述一个事物的各项数据,由若干个基础的数据类型组成
设计:
struct 结构类型名
{
类型名 成员名;
};
定义:
struct 结构类型名 结构变量名
struct Student stu;/结构变量
struct Student* sp;/结构指针
访问成员:
结构变量.成员名;
结构指针->成员名;
初始化:
顺序:
struct 结构类型名 结构变量名={v1,v2,...};
指定:
struct 结构类型名 结构变量名={.成员名=v2,.成员名=v2,...};
整体:
相同类型的结构变量,可以直接初始化
struct Student stu;
struct Student stu1 = stu;
类型重定义:
在C语言中struct、union、enum关键字在定义变量不能省略,可以使用typedef进行类型重定义,简约这些类型名
typedef struct 结构类型名 结构类型名;
typedef struct 结构类型名
{
}结构类型名;
结构体字节数的计算:
内存对齐:假设第一个成员从零地址开始,每个成员使用的内存编号,必须是它字节数的整数倍,如果不是则需要填充一些空白字节
内存补齐:结构体总字节数必须是它最大成员字节数的整数倍,如果不是,则在末尾补充一些空白字节
注意:在对齐和补齐时。Linux 32位下,超过4字节计算,windows按实际算
char ch; 0
char c; 1
short a; 2
int in; 4
int b; 8
成员的数据不同会影响结构体的总字节数,如何顺序安排合理可以节约内存
#pragma pack(n) 可以设置对齐、补齐的最大字节
数为n 但 n<=4(1,2,4)
注意:结构体适合使用堆内存存储
联合union
联合与结构使用方法基本一致。与结构的区别是联合的所有成员共用一块内存,一个成员的值发生改变,其他成员也随之改变
联合的效果就是使用少量的内存对应多个标识符,以此达到节约内存的目的,但是现在几乎不使用
常考联合题
union Data
{
char ch[5];0~4
int num; 0~3
}
sizeof(union Data) == 8
注意:计算联合总字节数时,不需要考虑内存对齐,但是需要考虑内存补齐
如何判断系统的大小端?
假设十六进制 0x01020304 存储在以0x0A~0x0D范围内的4字节内存中
大端系统:高位数据存储在低位地址,低位数据存储在高位地址
(0x0D:0x04 0x0C:0x03 0x0B:0x02 0x0A:0x01)
小端系统:高位数据存储在高位地址,低位数据存储在低位地址
(0x0D:0x01 0x0C:0x02 0x0B:0x03 0x0A:0x04)
个人计算机一般都是小段系统,而UNIX服务器和网络设备都是大段,网络字节序也是大端模式的数据,本地字节序就是小端模式的数据
序列化和反序列化
常考编程题:实现一个程序,判断系统是大端还是小端 /day10/1.c
3 typedef union Data
4 {
5 char ch;
6 int num;
7 }Data;
8
9 int main(int argc,const char* argv[])
10 {
11 Data d;
12 d.num = 0x01020304;
13 if(0x04 == d.ch)
14 {
15 printf("x\n");
16 }
17 }
枚举enum
枚举就是一种数据类型,把可能出现的所有值罗列出来,并起一个有意义的名字表示这些值,除此之外给该类型的变量赋其他值,是非法的(愿望)
枚举可以看做一种值受限的int类型,但是C编译器为了效率不检查,所以在C中枚举就相当于int类型变量
如果不给成员值,那么枚举中的值第一个默认从0开始,逐渐+1,如果设置了某个值,后面的成员在它的基础上+1
为什么要使用枚举:
为无意义的值取一个有意义的名字,提高代码的可读性,提高安全性(比变量更安全)
作业:通讯录改为使用结构存储,使用堆内存存储+
文件的分类:
文本文件: 存储的ASCII码的二进制 '2''5''5' 50 53 53
二进制文件:存储的是数据的补码 255 1111 1111
文件IO:
FILE* fopen(const char *path, const char *mode);
功能:打开或创建文件
path:文件的路径
mode:文件打开模式
r 以只读权限打开文件,如果文件不存在则失败
r+ 在r的基础上,增加写权限,如果文件不存在则失败
w 以只写权限打开文件,如果存在则清空打开,文件不存在则新建
w+ 在w的基础上,增加读权限,如果存在则清空打开,文件不存在则新建
a 以只写权限打开文件,如果存在则在末尾追加,不存在则新建
a+ 在a的基础上,增加读权限
返回值:文件指针,不需要关心里面有什么数据,只需要知道它是一个针对已打开文件的凭证,如果打开失败,会返回NULL
二进制方式读写:
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
功能:把内存中的数据写入到文件中
ptr:待写入的内存的首地址
size:一次写入的字节数
nmemb:写入的次数
stream:文件指针,fopen的返回值,表示往哪个文件写入数据
返回值:成功写入的次数
perror("fopen");
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从文件读取数据到内存中
ptr:接受文件数据的内存首地址
size:一次读取多少字节
nmemb:读取多少次
stream:文件指针,表示从哪个文件读取
返回值:成功读取到次数
练习2:设计并定义一个教师结构变量并初始化,以二进制方式写入到文件中
练习3:从文件中读取教师结构体数据到变量中并显示
/day10/6.c 7.c teacher.h
文本方式读写文件:
int fprintf(FILE *stream, const char *format, ...);
功能:以文本形式写入数据到文件中
stream:要写入的文件
format:占位符和提示信息
...:变量名列表
返回值:成功写入的字节数
int fscanf(FILE *stream, const char *format, ...);
功能:以文本形式读取数据
stream:要读取的文件
format:占位符(提示信息)
...:变量地址列表
返回值:成功读取的变量个数
/day10/8.c 9.c
练习4:设计并定义一个教师结构变量并初始化,以文本方式写入到文件中
练习5:从文件中读取教师结构体数据到变量中并显示
关闭文件;
int fclose(FILE *stream);
功能:关闭一个打开的文件
返回值:成功返回0,失败返回-1
文件位置指针:
每个通过fopen打开的文件都有一个文件位置指针来记录着接下来要读写的位置
以r、r+、w+打开文件,位置指针在文件的开头,以a a+打开文件,位置指针自动在末尾
如果想随意读写文件的任意位置,那么可以通过手动设置文件位置指针的位置
int fseek(FILE *stream, long offset, int whence);
功能:手动设置位置指针的位置
stream:文件指针
offset:偏移值
whence:基础位置
SEEK_SET 文件开头
SEEK_CUR 当前位置
SEEK_END 文件末尾
返回值:成功(调整后的位置还在文件中)返回0,失败返回-1
fseek(fwp,0,SEEK_SET);
void rewind(FILE *stream);
功能:把文件位置指针设置到文件开头
long fteel(FILE *stream);
功能:获取当前文件位置指针的位置
返回:在第几个字节
int fgetc(FILE *stream);
功能:从文件中读取一个字符
返回值:失败、读取完毕返回EOF(-1)
char *fgets(char *s, int size, FILE *stream);
功能:读取一行字符串到's'中,最多读size-1个
int fputc(int c, FILE *stream);
功能:写入一个字符到文件中
返回值:成功返回0,失败返回-1
int fputs(const char *s, FILE *stream);
功能:写入一个字符串到文件中
返回值:成功返回非负整数,失败返回-1
int remove(const char *pathname);
功能:删除文件
pathname:文件路径
返回值:成功返回非负整数,失败返回-1
int rename(const char *oldpath, const char *newpath);
功能:重命名文件
返回值:成功返回0,失败返回-1
命令行参数:main函数的参数 int argc,const char* argv[]
是为了获取./a.out 命令行中的参数
argc 代表了命令行参数的个数
argv 每个参数字符串的首地址
注意:./a.out是argv[0]
作业2:实现cp命令
cp dest src
fread fwrite
先读,存起来char buf[256],然后写进去
循环直到读完为止