C语言结构体嵌套二级指针和文件读写相关函数

1.结构体嵌套二级指针

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>


struct Teacher
{
    char* name;
    char** Stduents;
};

static void allocateSpace(struct Teacher***teachers)
{
	struct Teacher** pArray = malloc(sizeof(struct Teacher) * 3);

    for (int i = 0; i < 3; i++)
    {
        //给每个老师分配空间
        pArray[i] = malloc(sizeof(struct Teacher));

        //给每个老师姓名分配空间
        pArray[i]->name = malloc(sizeof(char) * 64);
        sprintf(pArray[i]->name, "Teacher_%d", i + 1);

        //给老师带的学生的数组分配空间
        pArray[i]->Stduents = malloc(sizeof(char*) * 4);

        //给四个学生分配内存,并且赋值
        for (int j = 0; j < 4; j++)
        {
            pArray[i]->Stduents[j] = malloc(sizeof(char) * 64);
            sprintf(pArray[i]->Stduents[j], "%s_Student_%d", pArray[i]->name, j + 1);
        }
    }
        *teachers = pArray;
}

void showArray(struct Teacher** pArray, int len)
{
    for (int i = 0; i < len; i++)
    {
        printf("%s\n", pArray[i]->name);
        for (int j = 0; j < 4; j++)
        {
            printf("     %s\n", pArray[i]->Stduents[j]);
        }
    }
}

static void freeSpace(struct Teacher**pArray,int len)
{
    for (int i = 0; i < len; i++)
    {
        //释放老师姓名
        if (pArray[i]->name != NULL)
        {
            free(pArray[i]->name);
            pArray[i]->name = NULL;
        }

        //释放每个学生
        for (int j = 0; j < len; j++)
        {
			if (pArray[i]->Stduents[j] != NULL)
			{
                free(pArray[i]->Stduents[j]);
                pArray[i]->Stduents = NULL;
			}
        }

        //释放学生数组
        free(pArray[i]->Stduents);
        pArray[i]->Stduents = NULL;

        //释放老师
        free(pArray[i]);
        pArray[i] = NULL;
    }
    //释放老师数组
    free(pArray);
    pArray = NULL;
}

static void test01()
{
    struct Teacher** pArray = NULL;
    //分配内存
    allocateSpace(&pArray);

    //打印数组
    showArray(pArray, 3);

    //释放内存
    freeSpace(pArray, 3);
    pArray = NULL;
}


int main01()
{
    test01();

    return 0;
}

/*   打印结果
Teacher_1
     Teacher_1_Student_1
     Teacher_1_Student_2
     Teacher_1_Student_3
     Teacher_1_Student_4
Teacher_2
     Teacher_2_Student_1
     Teacher_2_Student_2
     Teacher_2_Student_3
     Teacher_2_Student_4
Teacher_3
     Teacher_3_Student_1
     Teacher_3_Student_2
     Teacher_3_Student_3
     Teacher_3_Student_4
*/

2.结构体偏移量

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<stddef.h>


struct person
{
    char a;//0~3
    int b;//4~7
};

static void test01()
{
    struct person p1;
    struct person* p = &p1;

    printf("b的偏移量为:%d\n", (int)&(p->b) - (int)p);
    printf("b的偏移量为:%d\n", offsetof(struct person, b));
}

//通过偏移量获取数据
static void test02()
{
    struct person p1 = { 'a',10 };

    printf("p.b=%d\n", *(int*)((char*)&p1 + offsetof(struct person, b)));

    printf("p.b=%d\n", *(int*)((int*)&p1 + 1));
}

//结构体嵌套结构体
struct person2
{
    char a;
    int b;
    struct person c;
};

static void test03()
{
    struct person2 p = { 'a',10,'b',20 };
     
    int offset1 = offsetof(struct person2, c);
    int offset2 = offsetof(struct person, b);

    printf("%d\n", *(int*)((char*)&p + offset1 + offset2));//10

    printf("%d\n", ((struct person*) ((char*)&p + offset1))->b);//10
}

int main02()
{
    //test01();
    test02();

    return 0;
}

3.内存的对齐方式

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>


//#pragma pack(1)  对齐模式修改为1
#pragma pack(show)//默认对齐模数为8  该值可以改为2的n次方


//对于自定义数据类型 内存的对齐规则:
/*
1、从第一个属性开始 偏移为0
2、第二个属性开始,地址要放在该类型的整数倍,
与对齐模数比 取小的值 的整数倍上。
3、所有的属性都计算结束后,整体做二次对齐,
整体需放在属性中最大类型与对齐模数比取小的值的整数倍上。
*/
typedef struct _STUDENT
{
    int a;
    char b;
    double c;
    float d;
}Student
static void test01()
{
    printf("sizeof=%d\n", sizeof(Student));//24
}
//结构体嵌套结构体时,只需看子结构体中最大数据类型即可
typedef struct _STUDENT2
{
    char a;//0~7
    Student b;//8~31  子结构体最大数据类型是8
    double c;//32~39
}Student2;
static void test02()
{
    printf("sizeof=%d\n", sizeof(Student2));
}
int main03()
{
    //test01();
    test02();
    return 0;
}

4.文件的读写

fgetc、fputc、fgets、fputs、fread、fwrite、fprintf、fscanf等函数的使用

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>

//1、字符的读写回顾 fgetc()、fputc()
static void test01()
{
    //写文件  fputc
    FILE* f_write = fopen("f:/a.txt", "w");

    if (f_write == NULL)
    {
        return;
    }
    char buf[] = "hello world";
    for (int i = 0; i < strlen(buf); i++)
    {
        fputc(buf[i], f_write);
    }
    fclose(f_write);

    //读文件  fgetc
    FILE* f_read = fopen("f:/a.txt", "r");

    if (f_read == NULL)
    {
        return;
    }
    char ch;
    while ((ch = fgetc(f_read) != EOF))//EOF即  END  OF  FILE
    {
        printf("%c\n", ch);
    }
    
    fclose(f_read);
}

//2、按行读写文件
static void test02()
{
    //写文件  fputs()
    FILE* f_write = fopen("f:/a.txt", "w+");
    if (f_write == NULL)
    {
        return;
    }
    char* buf[] =
    { 
         "君不见黄河之水天上来,奔流到海不复回\n"
         "君不见高堂明镜悲白发,朝如青丝暮成雪\n"
         "人生得意须尽欢,莫使金樽空对月\n"
         "天生我材必有用,千金散尽还复来\n"
    };

    for (int i = 0; i < 4; i++)
    {
        fputs(buf[i], f_write);
    }
    fclose(f_write);
    
    //读文件  fgets()
    FILE* f_read = fopen("f:/a.txt", "r");
    if (f_read == NULL)
    {
        return;
    }
    while (!feof(f_read))
    {
        char temp[1024] = { 0 };

        fgets(temp, 1024, f_read);

        printf("%s\n", temp);
    }
    fclose(f_read);
}

//3、按块进行读写 fread()、fwrite()
struct hero
{
    char name[64];
    int age;
};

static void test03()
{
    //写文件  fwrite()
    FILE* f_write = fopen("f:/a.txt", "wb");
    if (f_write == NULL)
    {
        return;
    }
    struct hero heros[] =
    {
        {"孙悟空",111},
        {"曹操",222},
        {"赵云",333},
        {"鲁班",444},
    };

    for(int i=0;i<4;i++)
    {
        fwrite(&heros[i], sizeof(struct hero), 1, f_write);
    }
    fclose(f_write);

    //读文件  fread()
    FILE* f_read = fopen("f:/a.txt", "rb");
    if (f_read == NULL)
    {
        return;
    }

    struct hero temp[4];
    fread(&temp, sizeof(struct hero), 4, f_read);
    for (int i = 0; i < 4; i++)
    {
        printf("姓名:%s  年龄:%d\n", temp[i].name, temp[i].age);
    }
    fclose(f_read);
}

//4、格式化读写
static void test04()
{
    //写文件  fprintf()
    FILE* f_write = fopen("f:/a.txt", "w");
    if (f_write = NULL)
    {
        return;
    }
    fprintf(f_write, "hello world %s", "abcd");
    fclose(f_write);

    //读文件 fscanf()
    FILE* f_read = fopen("f:/a.txt", "r");
    if (f_read = NULL)
    {
        return;
    }
    char temp[1024] = { 0 };
    while (!feof(f_read))
    {
        fscanf(f_read, "%s", temp);
        printf("%s\n", temp);
    }

    fclose(f_read);
}

static void test05()
{
    //写文件
    FILE* f_write = fopen("f:/a.txt", "wb");
    if (f_write == NULL)
    {
        return;
    }
    struct hero heros[] =
    {
        {"孙悟空",111},
        {"曹操",222},
        {"赵云",333},
        {"鲁班",444},
    };

    for (int i = 0; i < 4; i++)
    {
        fwrite(&heros[i], sizeof(struct hero), 1, f_write);
    }
    fclose(f_write);

    //读文件
    FILE* f_read = fopen("f:/a.txt", "rb");
    if (f_read == NULL)
    {
        //error 宏
        //printf("文件加载失败\n");
        perror("文件加载失败");//用户提示信息+系统提供信息
        return;
    }
    struct hero temphero;
    //移动光标
    //fseek(f_read, sizeof(struct hero) * 3, SEEK_SET);
    fseek(f_read, -(long)sizeof(struct hero) * 1, SEEK_END);//sizeof的返回值是无符号的 需强转为long类型
    rewind(f_read);//将光标置到文件开头
    fread(&temphero, sizeof(struct hero), 1, f_read);
    printf("姓名:%s,年龄:%d\n", temphero.name, temphero.age);//鲁班 444
    fclose(f_read);
}
int main04()
{
    //test01();
    //test02();
    //test03();
    //test04();
    test05();
    return 0;
}

5.文件读写的注意事项

#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<time.h>

static void test01()
{
    FILE* file = fopen("f:/a.txt", "r");

    if (file == NULL)
    {
        return;
    }
    char ch;

#if 0
    while (!feof(file))
    {
        ch = fgetc(file);

        //滞后性
        if (feof(file))
        {
            break;
        }
        printf("%c", ch);
    }
#endif
    char ch;
    while ((ch = fgetc(file)) != EOF)
    {
        printf("%c", ch);
    }
    fclose(file);
}
//注意事项2
struct person
{
    char* name;//不要将指针写入到文件中
    int age;
};
int main05()
{
    test01();
    return 0;
}

6.配置文件的读写

#pragma  once
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>

struct ConfigInfo
{
	char key[64]; //索引值
	char value[64]; //实值
};


//获取有效行数
int getFileLine(char* fileName);

//判断当前行是否有效
int isValidLine(char* str);

//解析文件
void parseFile(char* filePath, int lines, struct ConfigInfo** configInfo);

//根据索引值 获取 实值 
char* getInfoByKey(char* key, struct ConfigInfo* configInfo, int line);

//释放信息
void freeSpace(struct ConfigInfo* configInfo);
#include "config.h"

//获取有效行数
int getFileLine(char* fileName)
{
	FILE* file = fopen(fileName, "r");
	if (file == NULL)
	{
		return -1;
	}

	char buf[1024] = { 0 };
	int lines = 0;
	while (fgets(buf, 1024, file) != NULL)
	{
		//如果是有效行 才统计
		if (isValidLine(buf))
		{
			lines++;
		}

	}
	fclose(file);

	return lines;
}

//判断当前行是否有效
int isValidLine(char* str)
{
	if (str[0] == ' ' || str[0] == '\0' || strchr(str, ':') == NULL)
	{
		return 0; //无效数据 都返回假
	}
	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");

	char buf[1024] = { 0 };
	int index = 0;
	while (fgets(buf, 1024, file) != NULL)
	{
		//解析数据  有效数据才解析
		// heroName:aaaa\n
		if (isValidLine(buf))
		{
			memset(info[index].key, 0, 64);
			memset(info[index].value, 0, 64);

			char* pos = strchr(buf, ':'); //pos代表冒号所在位置

			strncpy(info[index].key, buf, pos - buf); //将key截取到 结构体中 最后-1的原因是不需要截取换行符
			strncpy(info[index].value, pos + 1, strlen(pos + 1) - 1);

			/*printf("key =  %s\n", info[index].key);
			printf("value =  %s", info[index].value);*/
			index++;

		}

		memset(buf, 0, 1024);

	}


	*configInfo = info;
}


//根据索引值 获取 实值 
char* getInfoByKey(char* key, struct ConfigInfo* configInfo, int line)
{
	for (int i = 0; i < line; i++)
	{
		if (strcmp(key, configInfo[i].key) == 0)
		{
			return configInfo[i].value;
		}
	}
	return NULL;
}


//释放信息
void freeSpace(struct ConfigInfo* configInfo)
{
	if (configInfo != NULL)
	{
		free(configInfo);
		configInfo = NULL;
	}

}
#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include "config.h"

struct Person
{
	char a;
	int b;
};

void test01()
{
	char* filePath = "./config.txt";

	int line = getFileLine(filePath);

	printf("文件的有效行数为:%d\n", line);

	struct ConfigInfo* pArray = NULL;

	parseFile(filePath, line, &pArray);

	//测试 根据key 访问value
	printf("heroId = %s\n", getInfoByKey("heroId", pArray, line));
	printf("heroName = %s\n", getInfoByKey("heroName", pArray, line));
	printf("heroAtk = %s\n", getInfoByKey("heroAtk", pArray, line));
	printf("heroDef = %s\n", getInfoByKey("heroDef", pArray, line));
	printf("heroInfo = %s\n", getInfoByKey("heroInfo", pArray, line));


	//释放内存
	freeSpace(pArray);
	pArray = NULL;


	//文件加密 codeFile( sourceFile ,  destFile )
	// #  35  转为 short
	// 0000 0000 0010 0011   << 4
	// 0000 0010 0011 0000   
	// 1000 0000 0000 0000    |

	// 1000 0010 0011 0000  + 0000 ~ 1111 随机数  rand()%16     0~ 15
	// 1000 0010 0011 1010


	//解密  decodeFile ( sourceFile ,  destFile )
	// 1000 0010 0011 1010  <<1 
	// 000  0010 0011 10100  >> 5
	// 0000 0000  0010 0011
}

int main() 
{
	test01();
	system("pause");
	return EXIT_SUCCESS;
}
原文链接:,转发请注明来源!