1.结构体用指定字节对齐
(1)使用方法
#pragma pack 的作用
#pragma pack 是 编译器指令,
用于控制结构体的 内存对齐方式:
#pragma pack(push, n):保存当前对齐方式,并设置新的对齐字节数 n(n=1 表示1字节对齐)
#pragma pack(pop):恢复之前保存的对齐方式。
eg:
typedef struct {
uint16_t a; // 2字节
uint32_t b; // 4字节
} ExampleStruct;
默认情况(4字节对齐):
a 占用 2 字节,但编译器会在后面填充 2 字节(使 b 从 4 的倍数地址开始)。
实际内存布局:[a(2)][padding(2)][b(4)],总大小 = 8 字节。
1字节对齐(无填充):
a 和 b 紧密排列,无填充字节。
实际内存布局:[a(2)][b(4)],总大小 = 6 字节。
(2)例题
#include <stdio.h>
#pragma pack(1)
//bmp文件相关信息
typedef struct tagBITMAPFILEHEADER {
short bfType; // 文件类型标志
int bfSize; // 文件大小,单位为字节
short bfReserved1; // 保留字节
short bfReserved2; // 保留字节
int bfOffBits; // 数据偏移量,即实际图像数据开始的位置
}Bmp_file_head_t;
//bmp图像信息
typedef struct tagBITMAPINFOHEADER {
int biSize; // BITMAPINFOHEADER的大小,单位为字节
int biWidth; // 位图的宽度,单位为像素
int biHeight; // 位图的高度,单位为像素
short biPlanes; // 目标设备的位平面数,必须为1
short biBitCount; // 每像素位数(颜色深度)
int biCompression; // 图像压缩类型
int biSizeImage; // 图像大小,单位为字节
int biXPelsPerMeter;// 水平分辨率,单位为像素/米
int biYPelsPerMeter;// 垂直分辨率,单位为像素/米
int biClrUsed; // 实际使用颜色数
int biClrImportant; // 重要颜色数
}Bmp_info_t;
#pragma pack()
int get_bmp_head_info(const char *bmpname, Bmp_file_head_t *pheadinfo, Bmp_info_t *pbmpinfo)
{
FILE *fp = fopen(bmpname, "r");
if (NULL == fp)
{
printf("fopen error\n");
return -1;
}
fread(pheadinfo, sizeof(Bmp_file_head_t), 1, fp);
fread(pbmpinfo, sizeof(Bmp_info_t), 1, fp);
fclose(fp);
return 0;
}
int main(int argc, const char *argv[])
{
Bmp_file_head_t headinfo;
Bmp_info_t bmpinfo;
get_bmp_head_info("./3.bmp", &headinfo, &bmpinfo);
printf("sizeof(Bmp_file_head_t) = %ld\n", sizeof(Bmp_file_head_t));
printf("sizeof(Bmp_info_t) = %ld\n", sizeof(Bmp_info_t));
printf("biWidth = %d, biHeight = %d, biBitCount = %d\n", bmpinfo.biWidth, bmpinfo.biHeight, bmpinfo.biBitCount);
return 0;
}
说明:
①#pragma pack(push, 1) 的作用
取消结构体对齐优化,使所有字段紧密排列(无填充字节)。
适用于 二进制文件解析(如BMP、PNG等),确保结构体与文件格式完全匹配。
push可以省略。
②#pragma pack(pop) 的作用
恢复之前的对齐方式,避免影响其他代码。
如果不恢复,后续的结构体可能也会被强制1字节对齐,影响性能。
上述没有push的情况,pop也要省略,但是()不能省。
2.fgets和gets的区别
fgets:
1. 从指定的已打开文件中读取最多一行数据(遇到\n停止读取)
2. fgets保留\n字符并字符串末尾添加\0
3. fgets最多读取size-1个字符,最后一个位置存放\0gets:
1. 指定从终端设备读取数据
2. gets会将终端读到的\n字符替换成\0;
3. gets是危险的,因为在读取时,没有大小的限制,可能造成内存越界。
3.fread和fwrite函数
(1)基本用法
size_t fwrite(const void *ptr, size_t size, size_t nmemb,FILE *stream);
功能:向文件中写入nmemb个大小是size的数据到文件中
参数:
ptr : 要写入的数据的首地址
size:每个元素的字节数
nmemb:要写入的元素个数
stream:要写入的文件流指针
返回值:
成功:返回实际写入的元素个数
size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
功能:从文件中读取nmemb个大小是size的元素
参数:
ptr:存储读取到数据的首地址
size:每个元素的大小
nmemb:希望从文件中读取的元素个数
stream:要读的文件流指针
返回值:
成功:实际读到的元素个数
读到文件末尾:0
(2)函数接口
#include <stdio.h>
struct stu
{
int id;
char name[32];
int score;
};
int main(int argc, const char *argv[])
{
FILE *fp = fopen("./1.txt", "r");
if (NULL == fp)
{
printf("fopen error\n");
return -1;
}
struct stu s;
struct stu ss[10];
size_t cnt = fread(&s, sizeof(struct stu), 1, fp);
printf("cnt = %ld\n", cnt);
printf("%d %s %d\n", s.id, s.name, s.score);
cnt = fread(ss, sizeof(struct stu), 10, fp);
printf("cnt = %ld\n", cnt);
for (int i = 0; i < cnt; i++)
{
printf("%d %s %d\n", ss[i].id, ss[i].name, ss[i].score);
}
fclose(fp);
return 0;
}
#include <stdio.h>
struct stu
{
int id;
char name[32];
float score;
};
int main(int argc, char const *argv[])
{
FILE *fp = fopen("./1.txt", "w");
if(NULL == fp)
{
printf("open error\n");
return -1;
}
struct stu s[5] = {{1, "zhangsan", 99.9}, {2,"lisi", 98.9},
{3, "wangwu", 96.3}, {4,"laoliu", 95.2},
{5, "zhangqi", 99.1}};
size_t cnt = fwrite(s, sizeof(struct stu), 5, fp);
printf("cnt = %ld\n", cnt);
fclose(fp);
return 0;
}
4.Linux默认打开的文件
Linux操作系统:默认已打开的三个文件:
FILE * -->stdin:标准输入流 ---》标准输入设备:键盘
FILE * -->stdout:标准输出流--》标准输出设备:显示屏
FILE * --> stderr:标准出错流--》标准出错设备:显示屏
5.流定位相关接口
(1)基本用法
fseek:
int fseek(FILE *stream, long offset, int whence);
功能:实现文件流重新定位
参数:
stream:需要定位的文件流指针
offset:偏移量
whence:定位的相对位置
SEEK_SET : 从头进行偏移
SEEK_CUR:从当前位置开始偏移
SEEK_END: 从文件末尾偏移
返回值:
成功:返回当前的偏移量
失败:-1ftell:
long ftell(FILE *stream);
功能:获取流的当前位置到文开头的偏移量
参数:
stream:文件流
返回:
偏移量:byterewind:
void rewind(FILE *stream);
功能:流复位函数(复位到开头)
fseek(fp, 0, SEEK_SET);
(2)函数接口
#include <stdio.h>
int main(int argc, char const *argv[])
{
FILE *fp = fopen("./1.txt", "w");
if(NULL == fp)
{
printf("open error!\n");
return -1;
}
fseek(fp,10, SEEK_SET);
fputc('a', fp);
fseek(fp,10, SEEK_CUR);
fputc('b', fp);
fseek(fp, -5, SEEK_CUR);
fputc('c', fp);
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
printf("size = %ld\n", size);
rewind(fp);
fseek(fp, 0, SEEK_CUR);
fputc('d', fp);
fclose(fp);
return 0;
}
6.strtok函数
char *strtok(char *str, const char *delim);
功能:截取字符串,当遇到分隔符或者\0时停止截取
参数:
str:要截断的字符串
delim:要截取的分隔符
返回值:
成功:返回截取到的字符串的首地址
失败:NULL
7.文件复制
(1)fgetc和fputc
#include <stdio.h>
#include <string.h>
//主函数传参
//./a.out aaa bbb ----->实参
//形参:主函数中的参数
// argc : 程序运行时,给主函数传递的参数个数 3
// argv : 指针数组:argc个指针,分别指向了给主函数传递的实参
// argv[0] ---->"./a.out"
// argv[1] ---->"aaa"
// argc[2] ---->"bbb"
int main(int argc, char const *argv[])
{
if(argc != 3)
{
printf("Usage: ./a.out <srcfile> <dstfile>\n");
}
FILE *fp = fopen(argv[1], "r");
if(NULL == fp)
{
printf("open failed!\n");
return -1;
}
FILE *fb = fopen(argv[2], "w");
if(NULL == fb)
{
printf("open failed!\n");
return -1;
}
while(1)
{
char ret = fgetc(fp);
if(EOF == ret)
{
break;
}
fputc(ret, fb);
}
fclose(fp);
fclose(fb);
return 0;
}
(2)fgets和fputs
#include <stdio.h>
#include <string.h>
int main(int argc, char const *argv[])
{
if(argc != 3)
{
printf("Usage: ./a.out <srcfile> <dstfile>\n");
return -1;
}
FILE *fp = fopen(argv[1], "r");
if(NULL == fp)
{
printf("open failed!\n");
return -1;
}
FILE *fb = fopen(argv[2], "w");
if(NULL == fb)
{
printf("open failed!\n");
return -1;
}
while(1)
{
char a[100];
if(NULL == fgets(a, sizeof(a), fp))
{
break;
}
fputs(a, fb);
}
fclose(fp);
fclose(fb);
return 0;
}
(3)fread和fwrite
#include <stdio.h>
struct stu
{
int id;
char name[32];
float score;
};
int main(int argc, char const *argv[])
{
FILE *fp = fopen("./1.txt", "w");
FILE *fb = fopen("./2.txt", "w+");
if(NULL == fp || NULL == fb)
{
printf("open error\n");
return -1;
}
struct stu s[5] = {{1, "zhangsan", 99.9}, {2,"lisi", 98.9},
{3, "wangwu", 96.3}, {4,"laoliu", 95.2},
{5, "zhangqi", 99.1}};
fwrite(s, sizeof(struct stu), 5, fp);
fclose(fp);
fp = fopen("./1.txt", "r");
if(NULL == fp)
{
printf("open error\n");
return -1;
}
fseek(fp, 0, SEEK_END);
long size = ftell(fp);
fseek(fb, size-1, SEEK_SET);
fputc('\0', fb);
rewind(fp);
rewind(fb);
char buffer[1024];
size_t ret;
while((ret = fread(buffer,1,sizeof(buffer),fp)) > 0)
{
fwrite(buffer, 1, ret, fb);
}
rewind(fb);
struct stu ss[10];
size_t cnt = fread(ss, sizeof(struct stu), 5, fb);
printf("cnt = %ld\n", cnt);
for(int i = 0;i < cnt;++i)
{
printf("%d %s %f\n", ss[i].id, ss[i].name, ss[i].score);
}
fclose(fp);
fclose(fb);
return 0;
}