1. 数组名
printArr1(int* arr, int len);
printArr2(int(*arr)[3], int len);
printArr3(char** arr, int len);
void test() {
//默认情况下数组名指向首元素地址,当取地址后指针指向整个数组
//如果只需要打印数组元素,是否取地址都可以。但如果要操控地址时,
//指向整个数组的指针的步长不满足要求
int arr1[10];//arr1的类型为int*
int arr2[3][3] = {
{1,2,3},
{4,5,6},
{7,8,9},
};//arr2的类型为int(*)[3];
char* arr3[] = { "aaa","bbb","ccc" };//arr3的类型为char**
char** arr4[3];//arr4的类型为char***,指向一维数组
}
void test() {
typedef int(ARRAY_TYPE)[10];
typedef int(*ARRAY_POINTER_TYPE)[10];
int arr1[10];
int arr2[11];
ARRAY_TYPE* p1 = &arr1;
ARRAY_POINTER_TYPE p2 = &arr1;
int(*p3)[10] = &arr1;
}
2.结构体嵌套二级指针
struct Teacher {
char* name;
char** students;
};
int allocateSpace(struct Teacher*** t) {
if (NULL == t)
return -1;//错误码,不同错误码表示不同错误
struct Teacher** ts = malloc(sizeof(struct Teacher*) * 3);
for (int i = 0; i < 3; ++i){
//给结构体指针分配空间
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 (int j = 0; j < 4; ++j) {
ts[i]->students[j] = malloc(sizeof(char) * 64);
sprintf(ts[i]->students[j], "%s_stu_%d", ts[i]->name, j + 1);
}
}
*t = ts;
return 0;
}
void printT(struct Teacher** teachers) {
if (NULL == teachers)
return;
for (int i = 0; i < 3; ++i) {
printf("%s\n", teachers[i]->name);
for (int j = 0; j < 4; ++j) {
printf(" %s\n", teachers[i]->students[j]);
}
}
}
void freeSpace(struct Teacher** teachers) {
if (NULL == teachers)
return;
for (int i = 0; i < 3; ++i) {
if (teachers[i] == NULL)
continue;
if (teachers[i]->name != NULL) {
free(teachers[i]->name);
teachers[i]->name = NULL;
}
for (int j = 0; j < 4; ++j) {
if (teachers[i]->students[j] != NULL) {
free(teachers[i]->students[j]);
teachers[i]->students[j] = NULL;
}
}
free(teachers[i]->students);
teachers[i]->students = NULL;
free(teachers[i]);
teachers[i] = NULL;
}
free(teachers);
teachers = NULL;
}
void test() {
struct Teacher** teachers = NULL;//teachers为二级指针
int ret = 0;
ret = allocateSpace(&teachers);
if (ret < 0) {
printf("allocateSpace调用出错");
return;
}
//打印
printT(teachers);
//释放内存
freeSpace(teachers);
teachers = NULL;
}
3.结构体偏移量
#include <stddef.h>//可计算偏移量
struct A {
char a1;
int a2;
};
void test() {
struct A a = { 'b',20 };
//&a可以拿到结构体首地址
//char *改变了指针类型,即改变了步长
printf("A.a2:%d\n", *(int*)((char*)&a + offsetof(struct A, a2)));
printf("A.a2:%d\n", *((int*)&a + 1));
}
struct C {
int a;
double b;
};
struct B {
char a;
int 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);
}
4.内存字节对齐
手动设置对齐模数
//第一个元素偏移量为0
//从第二个元素开始计算偏移量
//结构体总的大小必须是内部最大成员的整数倍
struct Student {
int a;//0-3
char b;//4-7
double c;//8-15
float d;//16-19
};
void test() {
printf("%d\n", sizeof(struct Student));
}
5.文件IO
流的定义
文本流
二进制流
void test() {
FILE *fp = fopen("./test.txt", "r");
if (NULL == fp) {
printf("打开文件失败");
return;
}
char ch;
while (!feof(fp))
{
ch = fgetc(fp);
if (feof(fp))
break;//将读到的结尾标志删除
printf("%c", ch);
}
//关闭文件
fclose(fp);
fp = NULL;
}
while ((ch = fgetc(fp)) != EOF)
{
printf("%c", ch);
}
6.配置文件读写
//configFIle.h
//防止头文件重复包含
#pragma once
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <stddef.h>//可计算偏移量
struct configInfo {
char key[64];
char val[128];
};
//为了在C++中能调用C写的函数
#ifdef __cplusplus
extern "C" {
#endif
//获得文件有效行数
int gelines_configFile(FILE* fp);
//加载配置文件
void loadFile_configFile(const char* filepath, char ***fileData, int *lines);
//解析配置文件
void parseFile(char** fileData, int lines, struct configInfo** info);
//获得指定配置信息
char* getInfo(const char* key, struct configInfo* info, int line);
//释放配置文件信息
void destroyInfo(struct configInfo* info);
//判断当前行是否有效
int isValid(const char* buf);
#ifdef __cplusplus
}
#endif
//configFIle.c
#define _CRT_SECURE_NO_WARNINGS
#include "configFile.h"
//获得文件有效行数
int gelines_configFile(FILE* fp) {
char buf[1024] = { 0 };
int lines = 0;
while (fgets(buf, 1024, fp) != NULL) {
if (!isValid(buf))
continue;
memset(buf, 0, 1024);
++lines;
}
//把文件指针重置到文件开头
fseek(fp, 0, SEEK_SET);
return lines;
}
//加载配置文件
void loadFile_configFile(const char* filepath, char*** fileData, int* line) {
FILE* file = fopen(filepath, "r");
if (NULL == file)
return;
//先获取行数
int lines = gelines_configFile(file);
//给每行数据开辟内存
char** temp = malloc(sizeof(char*) * lines);
//读取数据
char buf[1024] = { 0 };
int index = 0;
while (fgets(buf, 1024, file) != NULL) {
if (!isValid(buf)) {
//如果返回false
continue;
}
temp[index] = malloc(strlen(buf) + 1);
strcpy(temp[index], buf);
index++;
//清空buf
memset(buf, 0, 1024);
}
//关闭文件
fclose(file);
file = NULL;
*fileData = temp;
*line = lines;
}
//解析配置文件
void parseFile(char** fileData, int lines, struct configInfo** info) {
struct configInfo* infos = malloc(sizeof(struct configInfo) * lines);
memset(infos, 0, sizeof(struct configInfo) * lines);
for (int i = 0; i < lines; ++i) {
//分割冒号的前后部分
char* pos = strchr(fileData[i], ':');
strncpy(infos[i].key, fileData[i], pos - fileData[i]);
strncpy(infos[i].val, pos + 1, strlen(pos + 1) - 1);
//printf("key:%s val:%s\n", infos[i].key, infos[i].val);
}
//释放文件信息
for (int i = 0; i < lines; ++i) {
if (fileData[i] != NULL) {
free(fileData[i]);
fileData[i] = NULL;
}
}
*info = infos;
}
//获得指定配置信息
char* getInfo(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;
}
return NULL;
}
//释放配置文件信息
void destroyInfo(struct configInfo* info) {
if (NULL == info)
return;
free(info);
info = NULL;
}
//判断当前行是否有效
int isValid(const char* buf) {
if (buf[0] == '#' || buf[0] == '\n' || strchr(buf, ':') == NULL) {
return 0;
}
return 1;
}
//main.c
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <stddef.h>//可计算偏移量
#include "configFile.h"
void test() {
char** fileData = NULL;
int lines = 0;
struct configInfo* info = NULL;
//加载配置文件
loadFile_configFile("./config.ini", &fileData, &lines);
//解析配置文件
parseFile(fileData, lines, &info);
//printf("lines:%d\n", lines);
printf("IP:%s\n", getInfo("ip", info, lines));
//释放配置信息内存
destroyInfo(info);
}
int main(){
test();
return 0;
}