结构体嵌套二级指针
struct Teacher{
char* name;
char** student;
}
结构体偏移量
头文件
#include<stddef.h>
中的宏函数offsetof,可以获取成员距离首地址的偏移量(下面的代码就能拿到a2的首地址)
struct A{
char a1;
int a2;
}
void test(){
struct A a={'b', 20};
(char*)&a+offsetof(struct A, a2);
}
单个结构体示例
#include<stdio.h>
#include<stddef.h>
struct A{
char a1;
int a2;
};
void test(){
struct A a={'b', 20};
printf("A.a2:%d\n", *(int*)((char*)&a+offsetof(struct A, a2)));
}
int main(){
test();
return 0;
}
运行结果
结构体嵌套
希望获取struct C里面的b的值
#include<stdio.h>
#include<stddef.h>
struct C{
char a;
double b;
};
struct B{
int a;
char b;
struct C c;
};
void test(){
struct B b={'a', 20, 30, 3.14};
int off1=offsetof(struct B, c);
int off2=offsetof(struct C, b);
printf("%f\n", *(double*)(((char *)&b+off1)+off2));
printf("%d\n", &(b.c.b));
printf("%f\n", ((struct C *)((char *)&b + off1))->b);
}
int main(){
test();
return 0;
}
运行结果
文件操作
文件概念
文件是磁盘一段命名的存储区。
fopen()如果成功了,会返回一个FIFL结构体。
读写文件的时候,我们到底是从哪里读的?
读写文件,最先读写的是缓冲区
文本方式和二进制方式
文件打开and读取内容
#include<stdio.h>
#include<stddef.h>
#include<stdlib.h>
void test(){
FILE *f =fopen("./test.txt","r");
if(f==NULL){
printf("打开文件失败\n");
return;
}
char ch;
while(!feof(f)){
//一个字节一个字节的读
ch=fgetc(f);
//如果读到文件结束符,则结束循环。否则会输出文件结尾符
if(feof(f)) break;
printf("%c", ch);
}
fclose(f);
f=NULL;
}
int main(){
test();
return 0;
}
运行结果
或者也可以这样写
#include<stdio.h>
#include<stddef.h>
#include<stdlib.h>
void test(){
FILE *f =fopen("./test.txt","r");
if(f==NULL){
printf("打开文件失败\n");
return;
}
char ch;
while((ch=fgetc(f))!=EOF){
printf("%c", ch);
}
fclose(f);
f=NULL;
}
int main(){
test();
return 0;
}
配置文件读写
配置文件的规则如图
只有类似 port:8080 这样的才算是有效行。上图中一共六个有效行。我们要写一个工具用于读取and解析配置文件中的参数信息。
1、定义接口 ConfigFile.h
写一个C里面的接口,如果不写下面的这个,c++没办法调用你写的这个c函数
cpp里不用写这,c语言里面要写
//防止头文件重复包含
#pragma once
//目的:为了在c++中能够调用c写的函数
#ifdef __cplusplus
extern "C"{
#endif
#ifdef __cplusplus
}
#endif
完整的接口文件
//防止头文件重复包含
#pragma once
#include<stdio.h>
struct ConfigInfo {
char key[64];
char val[128];
};
#ifdef __cplusplus
extern "C" {
#endif
//获取文件有效行数(即不包括注释,不包括空行,不包括无用的数据)
int getLines_ConfigFile(FILE *file);
//加载配置文件
void loadFile_ConfigFile(const char* filePath, char ***fileData, int *lines);
//解析配置文件
void parseFile_ConfigFile(char **fileData, int lines, struct ConfigInfo** info);
//获得指定配置信息
char* getInfo_ConfigFile(const char *key, struct ConfigInfo *info, int line);
//释放文件信息
void destoryInfo_ConfigFile(struct ConfigInfo *info);
//判断当前行是否有效
int isValid_ConfigFile(const char* buf);
#ifdef __cplusplus
}
#endif
2、ConfigFile.c
先把.h定义的函数都弄过来
#include"configFile.h"
//获取文件有效行数(即不包括注释,不包括空行,不包括无用的数据)
int getLines_ConfigFile(FILE *file);
//加载配置文件
void loadFile_ConfigFile(const char* filePath, char ***fileData, int *lines);
//解析配置文件
void parseFile_ConfigFile(char **fileData, int lines, struct ConfigInfo** info);
//获得指定配置信息
char* getInfo_ConfigFile(const char *key, struct ConfigInfo *info, int line);
//释放文件信息
void destoryInfo_ConfigFile(struct ConfigInfo *info);
//判断当前行是否有效
int isValid_ConfigFile(const char* buf);
然后开始一个一个的写函数实现
3、首先写第二个函数(加载配置文件)
//加载配置文件
void loadFile_ConfigFile(const char* filePath, char ***fileData, int *line) {
//读取文件
FILE *file = fopen(filePath, "r");
if (file == NULL) return;
int lines = getLines_ConfigFile(file);
//开辟一个数组存储char*指针
//给每行数据开辟内存
char**temp = malloc(sizeof(char*)*lines);
char buf[1024] = { 0 };
int index = 0;
//读取每行数据
while (fgets(buf, 1024, file) != NULL) {
//判断读取的是不是有效行
if (!isValid_ConfigFile(buf)) {
continue;
}
//有的有换行,有的没有,所以对于每一行有效数据,我们先收集起来,之后再去处理
//因此先 strlen(buf) + 1
temp[index++] = malloc(strlen(buf) + 1);
strcpy(temp[index], buf);
++index;
memset(buf, 0, 1024);
}
//关闭文件
fclose(file);
//将读取的数据和获取的行数存储起来
*fileData = temp;
*line = lines;
}
4、再把获取文件行数的函数写一下
//获取文件有效行数(即不包括注释,不包括空行,不包括无用的数据)
int getLines_ConfigFile(FILE *file){
char buf[1024]={0};
int lines=0;
while(fgets(buf,1024,file)!=NULL){
//判断是否为有效行
if(!isValid_ConfigFile(buf)) continue;
//清空buf
memset(buf, 0, 1024);
lines++;
}
//把文件指针重置到文件开头
fseek(file, 0, SEEK_SET);
return lines;
}
5、再写一下判断当前行是否是有效行
//判断当前行是否有效
int isValid_ConfigFile(const char* buf){
if(buf[0]=='#'|| buf[0]=='\n'|| strchr(buf,':')==NULL)
return 0;
return 1;
}
6、然后在有main函数的那个文件里进行测试
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include"config.h"
void test(){
char **fileData=NULL;
int lines=0;
loadFile_ConfigFile("./test.txt", &fileData, &lines);
printf("lines=%d\n",lines);
}
int main(){
test();
return 0;
}
7、解析获取到的配置文件中的有效行
//解析配置文件
void parseFile_ConfigFile(char **fileData, int lines, struct ConfigInfo** info){
struct ConfigInfo *myinfo=malloc(sizeof(struct ConfigInfo) *lines);
//初始化
memset(myinfo, 0, sizeof(struct ConfigInfo) *lines);
for(int i=0; i<lines; ++i){
//查找当前有效行的“:”在哪里
char *pos=strchr(fileData[i], ':');
//拷贝数据
strncpy(myinfo[i].key, fileData[i], pos-fileData[i]);
strncpy(myinfo[i].val, pos+1, strlen(pos+1)-1);
}
//释放文件信息
for(int i=0; i<lines; ++i){
if(fileData[i]!=NULL){
free(fileData[i]);
fileData[i]=NULL;
}
}
*info=myinfo;
}
8、修改main函数进行测试
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include"config.h"
void test(){
char **fileData=NULL;
int lines=0;
struct ConfigInfo *info=NULL;
//加载配置文件
loadFile_ConfigFile("./test.txt", &fileData, &lines);
printf("lines=%d\n",lines);
//解析配置文件
parserFile_ConfigFile(fileData, lines, &info);
}
int main(){
test();
return 0;
}
9、获取指定配置信息
//获得指定配置信息
char* getInfo_ConfigFile(const char *key, struct ConfigInfo *info, int line){
for(int i=0; i<line; i++){
if(strcmp(key, info[i].key)==0){
return info[i].val;
}
}
//没找到要找的key
return NULL;
}
10、检测信息提取情况
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include"config.h"
void test(){
char **fileData=NULL;
int lines=0;
struct ConfigInfo *info=NULL;
//加载配置文件
loadFile_ConfigFile("./test.txt", &fileData, &lines);
printf("lines=%d\n",lines);
//解析配置文件
parserFile_ConfigFile(fileData, lines, &info);
//获取key=ip的val
printf("IP:%s\n",getInfo_Configfile("ip", info, lines));
}
int main(){
test();
return 0;
}
11、释放文件信息
//释放文件信息
void destoryInfo_ConfigFile(struct ConfigInfo *info){
if(info==NULL) return;
free(info);
info=NULL;
}
各个文件完整版
config.h
//防止头文件重复包含
#pragma once
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
struct ConfigInfo { char key[64]; char val[128];};
#ifdef __cplusplus
extern "C"{
#endif
int getLines_ConfigFile(FILE *file);
void loadFile_ConfigFile(const char* filePath, char ***fileData, int *line);
void parseFile_ConfigFile(char **fileData, int lines, struct ConfigInfo** info);
char* getInfo_ConfigFile(const char *key, struct ConfigInfo *info, int line);
void destoryInfo_ConfigFile(struct ConfigInfo *info);
int isValid_ConfigFile(const char* buf);
#ifdef __cplusplus
}
#endif
config.c
#include"config.h"
#include<string.h>
//获取文件有效行数(即不包括注释,不包括空行,不包括无用的数据)
int getLines_ConfigFile(FILE *file){
char buf[1024]={0};
int lines=0;
while(fgets(buf,1024,file)!=NULL){
//判断是否为有效行
if(!isValid_ConfigFile(buf)) continue;
//清空buf
memset(buf, 0, 1024);
lines++;
}
//把文件指针重置到文件开头
fseek(file, 0, SEEK_SET);
return lines;
}
//加载配置文件
void loadFile_ConfigFile(const char* filePath, char ***fileData, int *line) {
//读取文件
FILE *file = fopen(filePath, "r");
if (file == NULL) return;
int lines = getLines_ConfigFile(file);
//开辟一个数组存储char*指针
//给每行数据开辟内存
char**temp = malloc(sizeof(char*)*lines);
char buf[1024] = { 0 };
int index = 0;
//读取每行数据
while (fgets(buf, 1024, file) != NULL) {
//判断读取的是不是有效行
if (!isValid_ConfigFile(buf)) {
continue;
}
//有的有换行,有的没有,所以对于每一行有效数据,我们先收集起来,之后再去处理
//因此先 strlen(buf) + 1
temp[index++] = malloc(strlen(buf) + 1);
strcpy(temp[index], buf);
++index;
memset(buf, 0, 1024);
}
//关闭文件
fclose(file);
//将读取的数据和获取的行数存储起来
*fileData = temp;
*line = lines;
}
//解析配置文件
void parseFile_ConfigFile(char **fileData, int lines, struct ConfigInfo** info){
struct ConfigInfo *myinfo=malloc(sizeof(struct ConfigInfo) *lines);
//初始化
memset(myinfo, 0, sizeof(struct ConfigInfo) *lines);
for(int i=0; i<lines; ++i){
//查找当前有效行的“:”在哪里
char *pos=strchr(fileData[i], ':');
//拷贝数据
strncpy(myinfo[i].key, fileData[i], pos-fileData[i]);
strncpy(myinfo[i].val, pos+1, strlen(pos+1)-1);
}
//释放文件信息
for(int i=0; i<lines; ++i){
if(fileData[i]!=NULL){
free(fileData[i]);
fileData[i]=NULL;
}
}
*info=myinfo;
}
//获得指定配置信息
char* getInfo_ConfigFile(const char *key, struct ConfigInfo *info, int line){
for(int i=0; i<line; i++){
if(strcmp(key, info[i].key)==0){
return info[i].val;
}
}
//没找到要找的key
return NULL;
}
//释放文件信息
void destoryInfo_ConfigFile(struct ConfigInfo *info){
if(info==NULL) return;
free(info);
info=NULL;
}
//判断当前行是否有效
int isValid_ConfigFile(const char* buf){
if(buf[0]=='#'|| buf[0]=='\n'|| strchr(buf,':')==NULL)
return 0;
return 1;
}
parse.c
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include"config.h"
void test(){
char **fileData=NULL;
int lines=0;
struct ConfigInfo *info=NULL;
//加载配置文件
loadFile_ConfigFile("./test.txt", &fileData, &lines);
printf("lines=%d\n",lines);
//解析配置文件
parserFile_ConfigFile(fileData, lines, &info);
//获取key=ip的val
printf("IP:%s\n",getInfo_Configfile("ip", info, lines));
//释放空间
destoryinfo_ConfigFile(info);
}
int main(){
test();
return 0;
}