一,结构
结构是一种由程序员自己设计的数据类型,用于描述一个事物的的各项特征数据,由若干个可以不同的基础类型组成
设计:
struct 结构类型名
{
类型 成员名1;
类型 成员们2;
...
};
定义结构变量:
struct 结构类型名 结构变量名 = {v1,v2,...};顺序初始化
一定要与成员类型顺序一致
struct 结构类型名 结构变量名 = { ,.成员名1=v1,...,.成员名5=v5};指定初始化
只初始化某些成员,顺序无需一致
struct Student stu;
struct Student stu1 = stu; 整体初始化
tap: struct 在C语言中,定义结构变量时不能省略,这种赋值方式只能在初始化时使用
tap:同类型的结构变量可以直接给另一个结构变量初始化
重定义:
在C语言中,struct、union、enum关键字在定义变量时不能省略,可以使用typdef进行类型重定义
typedef struct 结构类型名
{
}结构类型名;
访问成员:
struct Student stu;
static Student* sp;
结构变量名.成员名;
由于结构体变量的字节数一般都比较大,普通的值传递效率非常低,因此传递结构变量的地址,也即是传递结构指针
变量,此时想要通过结构指针变量访问成员时借助 -> 结构指针变量名->成员名
如何计算结构体的总字节数:
结构成员的顺序可能会影响它的总字节数,如果能够在设计结构体时合理的安排成员顺序,可以大大节约内存
内存对齐:
假设第一个成员从0地址开始,存储每个成员的地址编号必须能被该成员的类型字节数整除,如果不能整除就自动补空白字节补齐
内存补齐:
结构体的总字节数必须是它最大成员字节数的整数倍,如果不能整除,则在末尾补齐一些空白字符,在32位Linux系统下计算结构体的对齐补齐时
如果最大字节数大于4则按4来算。windows是按实际字节数对齐补齐
#pragma pack(n) 可以设置对齐、补齐的最大字节数为n 但 n <=4(1,2,4)
tap:结构体一般适合堆内存存储
二,联合 union
联合与结构的用法基本一致,与结构的区别是联合的所有成员公用一块内存,一个成员的值发生改变,其他成员也随之发生改变
联合的效果就是使用少量的内存对应多个标识符,以此达到节约内存的目的,但是现在几乎不使用
计算联合总字节数时不用考虑对齐,但是要考虑补齐问题
如何判断系统的大小端?
例 0x01020304 存在 0x0A~0x0D
大端系统:高位数据存储在低位地址,低位数据存储在高位地址
(0x0D,0x04 ; 0x0C,0x03 ; 0x0B,0x02 ; 0x0A,0x01)
小端系统:低位数据存储在低位地址,高位数据存储在高位地址
(0x0D,0x01 ; 0x0C,0x02 ; 0x0B,0x03 ; 0x0A,0x04)
个人计算机一般都是小端系统,而UNIX网络设备大多是大端系统,网络字节序是大端模式数据,本地字节序就是小端模式的数据
序列化与反序列化
实现一个程序,判断当前系统是大端还是小端
#include <stdio.h>
union Dx
{
char ch;
int num;
};
int main(int argc,const char* argv[])
{
union Dx date={date.num=0x01020304};
if(0x04 == date.ch)
printf("小端系统\n");
else
printf("大端系统\n");
}
三,枚举 enum
枚举就是一种数据类型,把可能出现的所有值都罗列出来,并起一个有意义的名字表示这些值,除此之外给该类型的变量赋其他值,是非法的(愿望)
枚举可以看作一种值受限的int类型,但是在C编译器为了效率不检查,所以在C中枚举就相当于int类型变量
如果不给成员值,那么枚举中的第一个成员默认从0开始,逐渐+1,如果设了某个值,后面的成员在它的基础上逐渐+1
为什么要使用枚举:
为无意义的值起一个有意义的名字,提高代码可读性,提高安全性(变量更安全)
四,文件
文件的分类:
文本文件:存储的是ASCII码的二进制
二进制文件:存储的是数据的补码
文件IO:
FILE* fopen(const char *path,const char *mode);
功能:打开或创建文件
path:文件的路径
mode:文件打开的模式
r 以只读权限打开文件,如果文件不存在则失败
r+ 在r的基础上增加写权限
w 以只写权限打开文件,如果存在则清空打开,如果不存在则创建文件再打开
w+ 在w的基础上增加读权限
a 以只写权限打开文件,如果存在在末尾追加,如果不存在则创建文件
a+ 在a的基础上增加读权限
返回值:文件指针,不需要关心里面有什么数据,只需要知道它是一个指针对已打开文件的凭证,打开失败返回NULL
二进制方式读写:
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从文件读取数据到内存中
ptr:接收文件数据的内存首地址
size:一次读取多少字节
nmemb:读取的次数
stream:文件指针,表示从那个文件读取
返回值:成功读取的次数
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
功能:把内存中的数据写入到文件中
ptr:待写入的内存首地址
size:一次写入的字节数
nmemb:写入的次数
stream:文件指针(fopen的返回值)
返回值:成功写入的次数
文本方式读写:
int fprintf(FILE *stream, const char *format, ...);
功能:以文本形式写入数据到文件中
stream:要写入的文件
format:占位符和提示信息
...:变量名列表
返回值:成功写入的字节数
int fscanf(FILE *stream, const char *format, ...);
功能:从文件中读取数据到变量中
stream:要读取的文件
format:占位符(数据格式)
...:变量地址列表
返回值:成功读取的变量个数
关闭文件:
int fclose(FILE *stream);
功能:关闭一个打开的文件
返回值:成功0,失败-1
tap:如果需要立即修改文件的内容,最好先关闭文件
文件位置指针:
每个通过fopen打开的文件都有一个文件位置指针来记录着接下来要读写的位置,以r、r+、w、w+打开文件,位置指针
都在文件的开头,以a、a+打开文件,位置指针在末尾。
如果想要随意读写文件的任意位置,那么可以手动设置文件位置指针的位置
int fseek(FILE *stream, long offset, int whence);
功能:手动设置位置指针的位置
stream:文件指针
offset:偏移值
whence:基础位置 SEEK_SET 文件开头 SEEK_CUR 当前位置 SEEK_END 文件末尾
返回值:成功返回0 失败返回-1
long ftell(FILE *stream);
功能:获取当前文件位置指针的位置
返回值:在第几个字节
void rewind(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);
功能:写入一个字符到文件中
返回值:成功写入返回非负值 失败返回-1
int fputs(const char *s, FILE *stream);
功能:写入一个字符串到文件中
返回值:成功写入返回非负值 失败返回-1
int remove(const char *pathname);
功能:删除一个文件
pathname:文件路径
返回值:删除成功返回0 失败返回-1
int rename(const char *oldpath, const char *newpath);
功能:重命名文件
返回值:成功返回0 失败返回-1
五,命令行参数:main函数的参数
是为了获取 ./a.out 命令行中的参数
argc 代表了命令行参数的个数
argv 每个参数字符串的首地址
tap:./a.out 是 argv[0]
使用文件实现Linux cp 命令
#include <stdio.h>
int main(int argc,const char* argv[])
{
if(3 != argc)
{
printf("输入参数有误!!!");
return 0;
}
FILE* frp = fopen(argv[1],"r");
if(NULL == frp)
{
printf("源文件不存在\n");
perror("fopen");
return -1;
}
FILE* fwp = fopen(argv[2],"r");
if(fwp)
{
printf("目标文件已存在,是否覆盖(y//n)?");
char cmd = getchar();
if('y'!= cmd && 'Y' != cmd)
{
printf("停止拷贝\n");
return 0;
}
fclose(fwp);
}
fwp = fopen(argv[2],"w");
char str[255]={};
int ret=0;
do
{
ret = fread(str,1,255,frp);
fwrite(str,1,ret,fwp);
}while(0 > ret);
}