结构体嵌套二级指针练习
有多少个malloc要有多少释放

示例
struct Teacher
{
char* name
char** students
}
void allocateSpace(struct Teacher*** teacher)
{
if (teacher ==NULL)
{
return
}
//开辟内存
struct Teacher** ts = malloc(sizeof(struct Teacher*) * 3)
//给每个老师分配内存
for (size_t i = 0
{
ts[i] = malloc(sizeof(struct Teacher))
//给老师的姓名分配内存
ts[i]->name = malloc(sizeof(char) * 64)
//给老师起名称
sprintf(ts[i]->name, "Teacher_%d", i + 1)
//给学生数组分配内存
ts[i]->students = malloc(sizeof(char*) * 4)
//给学生的兴明光开辟内存以及赋值
for (size_t j = 0
{
ts[i]->students[j] = malloc(sizeof(char) * 64)
sprintf(ts[i]->students[j], "%s_Students_%d", ts[i]->name, j + 1)
}
}
*teacher = ts
}
void printTeacher(struct Teacher** pArray)
{
if (pArray == NULL)
{
return
}
for (size_t i = 0
{
printf("%s\n", pArray[i]->name)
for (size_t j = 0
{
printf(" %s\n", pArray[i]->students[j])
}
}
}
void freeSpace(struct Teacher** pArray)
{
if (pArray == NULL)
{
return
}
for (size_t i = 0
{
//先释放老师姓名
if (pArray[i]->name != NULL)
{
free(pArray[i]->name)
pArray[i]->name = NULL
}
//释放学生姓名
for (size_t j = 0
{
if (pArray[i]->students[j] != NULL)
{
free(pArray[i]->students[j])
pArray[i]->students[j] = NULL
}
}
//释放学生数组
if (pArray[i]->students != NULL)
{
free(pArray[i]->students)
pArray[i]->students = NULL
}
//释放老师
if (pArray[i] != NULL)
{
free(pArray[i])
pArray[i] = NULL
}
}
//释放老师数组
if (pArray != NULL)
{
free(pArray)
pArray = NULL
}
}
void test01()
{
struct Teacher** pArray = NULL
allocateSpace(&pArray)
printTeacher(pArray)
freeSpace(pArray)
}
int main(void)
{
test01()
system("pause")
return EXIT_SUCCESS
}
结构体偏移量
获取属性偏移 offsetof
1. offsetof
2. (int)&(p->b) - (int)p
通过偏移量 获取内存
结构体嵌套结构体
示例
struct Teacher
{
char a;
int b;
};
void test01()
{
struct Teacher t1;
struct Teacher* p = &t1;
printf("b的属性偏移量为:%d\n", (int)&(p->b) - (int)p);
printf("b的属性偏移量为:%d\n", offsetof(struct Teacher,b));
}
void test02()
{
struct Teacher t1 = { 'a',10 };
printf("t1.b =%d\n", *(int*)((char*)&t1 + offsetof(struct Teacher, b)));
printf("t1.b =%d\n", *(int*)((int*)&t1 +1));
}
struct Teacher2
{
char a;
int b;
struct Teacher c;
};
void test03()
{
struct Teacher2 t1 = { 'a',10,'b',20 };
int offset1 = offsetof(struct Teacher2, c);
int offset2 = offsetof(struct Teacher, b);
printf("%d\n", *(int*)((char*)&t1 + offset1 + offset2));
printf("%d\n", ((struct Teacher*)((char*)&t1 + offset1))->b);
}
int main(void)
{
test01();
test02();
test03();
system("pause");
return EXIT_SUCCESS;
}
内存对齐

查看对齐模数 #pragma pack(show)
默认对齐模数 8
自定义数据类型 对齐规则
第一个属性开始 从0开始偏移
第二个属性开始 要放在 该类型的大小 与 对齐模数比 取小的值 的整数倍
所有属性都计算完后,再整体做二次偏移,将整体计算的结果 要放在 结构体最大类型 与对齐模数比 取小的值的 整数倍上
结构体嵌套结构体
结构体嵌套结构体时候,子结构体放在该结构体中最大类型 和对齐模数比 的整数倍上即可
示例
#pragma pack(show)
#pragma pack(1)
typedef struct _STUDENT
{
int a;
char b;
double c;
float d;
} Student;
void test01()
{
printf("size of srudent = %d\n", sizeof(Student));
}
typedef struct _STUDENT2
{
char a;
Student b;
double c;
} Student2;
void test02()
{
printf("size of srudent = %d\n", sizeof(Student2));
}
int main(void)
{
test01();
test02();
system("pause");
return EXIT_SUCCESS;
}
文件读写回顾

按照字符读写
写 fputc
读 fgetc
while ( (ch = *fgetc*(f_read)) != *EOF* ) 判断是否到文件尾
示例
// 按照字符读写文件:fgetc(),fputc()
void test01()
{
//写文件
FILE * f_write =fopen("./test01.txt", "w+")
if (f_write == NULL)
{
return
}
char buf[] = "this is first test"
for (size_t i = 0
{
fputc(buf[i], f_write)
}
fclose(f_write)
//读文件
FILE* f_read = fopen("./test01.txt", "r")
if (f_read == NULL)
{
return
}
char ch
while ((ch = fgetc(f_read)) != EOF) //EOF End of File
{
printf("%c", ch)
}
fclose(f_read)
}
int main(void)
{
test01()
system("pause")
return EXIT_SUCCESS
}
按行读写
写 fputs
读 fgets
示例
//按照行读写文件
void test02()
{
FILE* f_write = fopen("test02.txt", "w")
if (f_write ==NULL)
{
return
}
char* buf[] =
{
"锄禾日当午,\n",
"汗滴禾下土。\n",
"谁知盘中餐,\n",
"粒粒皆辛苦。\n"
}
for (size_t i = 0
{
fputs(buf[i], f_write)
}
fclose(f_write)
//读文件
FILE* f_read = fopen("test02.txt", "r")
if (f_read ==NULL)
{
return
}
while (!feof(f_read))
{
char buf[1024] = { 0 }
fgets(buf, 1024, f_read)
printf("%s", buf)
}
fclose(f_read)
}
按块读写
写 fwrite
参数1 数据地址 参数2 块大小 参数3 块个数 参数4 文件指针
读 fread
示例
//按块读写文件 fread() fwrite()
struct Hero
{
char name[64]
int age
}
void test03()
{
//写文件 wb二进制方式
FILE* f_w = fopen(".test03.txt", "wb")
if (!f_w)
{
return
}
struct Hero heroes[4] =
{
{"Author",18},
{"Jeff", 23},
{"Andy", 22},
{"Bob",15}
}
for (size_t i = 0
{
//参数1 数据地址;参数2 块大小;参数3:块个数;参数4:文件指针
fwrite(&heroes[i], sizeof(struct Hero), 1, f_w)
}
fclose(f_w)
//读文件
FILE* f_r = fopen(".test03.txt", "rb")
if (f_r == NULL)
{
return
}
struct Hero temp[4]
fread(&temp, sizeof(struct Hero), 4, f_r)
for (size_t i = 0
{
printf("name %s age:%d\n", temp[i].name, temp[i].age)
}
fclose(f_r)
}
格式化读写
写 fprintf
读 fscanf
示例
void test04()
{
FILE* fw = fopen("test04.txt", "w");
if (!fw)
{
return;
}
fprintf(fw, "hello world %d年 %d月 %d日", 2022, 9, 17);
fclose(fw);
FILE* fr = fopen("test04.txt", "r");
if (!fr)
{
return;
}
char buf[1024] = { 0 };
while (!feof(fr))
{
fscanf(fr, "%s", buf);
printf("%s\n", buf);
}
fclose(fr);
}
随机位置读写
fseek()偏移文件光标 ftell获取当前光标位置 rewind()让文件光标返回文件首部
fseek()偏移文件光标( 文件指针, 偏移, 起始位置 )
SEEK_SET 从头开始
SEEK_END 从尾开始
SEEK_CUR 从当前位置
rewind 将文件光标置首
error宏 利用perror打印错误提示信息
示例
void test05()
{
FILE* f_w = fopen(".test05.txt", "wb");
if (!f_w)
{
return;
}
struct Hero heroes[4] =
{
{"Author",18},
{"Jeff", 23},
{"Andy", 22},
{"Bob",15}
};
for (size_t i = 0; i < 4; i++)
{
fwrite(&heroes[i], sizeof(struct Hero), 1, f_w);
}
fclose(f_w);
FILE* fr = fopen(".test05.txt", "r");
if (!fr)
{
perror("fail to open fr");
return;
}
struct Hero temp;
fseek(fr, sizeof(struct Hero) * 2,SEEK_SET);
rewind(fr);
fread(&temp, sizeof(struct Hero), 1, fr);
printf("name of %s age of %d", temp.name, temp.age);
fclose(fr);
}
文件读写注意事项
注意事项1:
不要用feof 按照字符方式读文件,原因有滞后性,会读出EOF
注意事项2:
如果属性开辟到堆区,不要存指针到文件中,要将指针指向的内容存放到文件中
配置文件读写案例
1. 文件中按照键值对方式 存放了有效的信息需要解析出来
2. 创建 config.h 和 config.c做配置文件读操作
3. 获取有效信息的行数 getFileLines
4. 判断字符串是否是有效行 int isValidLines(char *str)
5. 解析文件到配置信息数组中
void parseFile(char * filePath, int lines , struct ConfigInfo ** configinfo);
6. 通过key获取value值
char * getInfoByKey(char * key, struct ConfigInfo * configinfo, int len);
7. 释放内存
void freeConfigInfo(struct ConfigInfo * configinfo);
示例:头文件 config.h
#pragma once
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>
#include <time.h>
#include <Windows.h>
struct ConfigInfo
{
char key[64];
char value[64];
};
int getFileLInes(char *filePath);
int isVaildLInes(char *str);
void parseFile(char* filePath, int lines, struct ConfigInfo** configinfo);
char* getInfoByKey(char* key, struct ConfigInfo* configinfo, int len);
void freeConfigInfo(struct ConfigInfo* configinfo);
示例:函数文件 config.c
#include "config.h"
int getFileLines(char* filePath)
{
FILE* file = fopen(filePath, "r");
if (file == NULL)
{
return NULL;
}
char buf[1024] = { 0 };
int lines=0;
while (fgets(buf, 1024, file) != NULL)
{
if (isValidLines(buf))
{
lines++;
}
memset(buf, 0, 1024);
}
return lines;
fclose(file);
}
int isValidLines(char* str)
{
if (strchr(str, ':') == NULL)
{
return 0;
}
else
{
return 1;
}
}
void parseFile(char* filePath, int lines, struct ConfigInfo** configinfo)
{
struct ConfigInfo* info = malloc(sizeof(struct ConfigInfo) * lines);
if (info == NULL)
{
return;
}
FILE* file = fopen(filePath, "r");
if (!file)
{
return;
}
char buf[1024] = { 0 };
int index = 0;
while (fgets(buf, 1024, file) != NULL)
{
if (isValidLines(buf))
{
memset(info[index].key, 0, 64);
memset(info[index].value, 0, 64);
char * position =strchr(buf, ':');
strncpy(info[index].key, buf, position - buf);
strncpy(info[index].value, position + 1, strlen(position+1)-1);
index++;
}
memset(buf, 0, 1024);
}
*configinfo = info;
}
char* getInfoByKey(char* key, struct ConfigInfo* configinfo, int len)
{
for (size_t i = 0; i < len; i++)
{
if (strcmp(key, configinfo[i].key) == 0)
{
return configinfo[i].value;
}
}
return NULL;
}
void freeConfigInfo(struct ConfigInfo* configinfo)
{
if (configinfo != NULL)
{
free(configinfo);
configinfo = NULL;
}
}
示例:主程序
# include "config.h"
int main(void)
{
char* filePath = "./config.txt";
int len = getFileLines(filePath);
printf("文件的有效行数为:%d\n", len);
struct ConfigInfo* configInfo =NULL;
parseFile(filePath, len, &configInfo);
printf("hero id = %s\n", getInfoByKey("heroId", configInfo, len));
printf("hero Name = %s\n", getInfoByKey("heroName", configInfo, len));
printf("hero Atk = %s\n", getInfoByKey("heroAtk", configInfo, len));
printf("hero Def = %s\n", getInfoByKey("heroDef", configInfo, len));
printf("hero Info = %s\n", getInfoByKey("heroInfo", configInfo, len));
freeConfigInfo(configInfo);
configInfo == NULL;
system("pause");
return EXIT_SUCCESS;
}
添加文件加密和解密的模块
void codeFile(char* sourceFilePath, char* destFilePath)
{
FILE* fp1 = fopen(sourceFilePath, "r");
FILE* fp2 = fopen(destFilePath, "w");
if (!fp1 || !fp2)
{
perror("fopen");
return;
}
char ch;
while ((ch = fgetc(fp1)) != EOF)
{
short temp = (short)ch;
temp = temp << 4;
temp = temp | 0x8000;
temp += rand() % 16;
fprintf(fp2,"%hd\n", temp);
}
fclose(fp1);
fclose(fp2);
}
void decodeFile(char* sourceFilePath, char* destFilePath)
{
FILE* fp1 = fopen(sourceFilePath, "r");
FILE* fp2 = fopen(destFilePath, "w");
if (!fp1 || !fp2)
{
perror("fopen");
return;
}
short temp;
while (!feof(fp1))
{
fscanf(fp1, "%hd", &temp);
temp = temp << 1;
temp = temp >> 5;
char ch = (char)temp;
fputc(ch, fp2);
}
fclose(fp1);
fclose(fp2);
}
main函数里 可以添加srand((unsigned int)time(NULL));
通过随机数种子来让每次加密后的文件不一样。