C解析:结构体

时间:2022-09-09 19:31:12

C解析:结构体

内存对齐:

平台原因:不是所有的硬件平台都能访问任意地址上的任意数据。
性能原因:如果数据放在未对齐的内存空间中,则处理器访问变量时需要做两次内存访问。

C解析:结构体
内存对齐的具体规则如下:
1、结构体各个成员变量的内存空间首地址必须是“对齐系数”和“变量实际长度”中较小者的整数倍。
2、结构体本身也需要对齐,结构体占用的总大小应该为“对齐系数”和“最大数据成员长度”中较小者的整数倍。

struct data_test {
char a;
short b;
char c[2];
double d;
char e;
int f;
char g
}data;

C解析:结构体
由于结构体本身必须满足4 [因为 4 < sizeof(doubule d) 所以4字节对齐] 的整数倍,因此实际占用空间为28字节。

注意:通过优化结构体成员的定义顺序,在同样满足内存对齐的要求下,可以大大地减少内存浪费。

类型与变量:

range是struct _Range 类型的结构体变量。

struct _Range{
const int min;
const int max;
}range;

如果在结构体定义前添加typedef,range就变成了结构体的类型。习惯类型名的首地址大写,变量名的首地址小写,这里也可以 直接省掉类型名_Range。
有了Range类型,即可同时定义一个Range类型的变量range和一个指向Range*类型的指针变量pRange.

typedef struct{
const int min;
const int max;
}Range;

首先结构体告诉编译器是如何表示数据的,但它并未让编译器为数据分配空间。

初始化:
define newRangeCheck(min, max) {(min), (max)}

Range range = newRangeCheck(0, 9);

相当于:

range.min = 0; range.max = 9;
接口与实现:

传递结构体成员,传递结构体,传递结构体的地址

struct MyStruct{
int array[10];
}st;

显然只要将结构体的地址(int*)&st作为形参。

下面还是以范围校验器为例,定义一个指向结构体的指针变量pRange,其初始化、赋值与普通指针变量是一样的:

Range *pRange = &range;

和数组不一样,结构体名并不是结构体的地址,因此要在结构体名前加上&。

range.min == (*pRange).min == pRange -> min
range.max == (*pRange).max == pRange -> max
用函数指针调用

根据依赖倒置原则,最好的方法是用函数指针隔离变化。

typedef bool (*const Validate)(void *pData, int value);
Validate validate = validateRange; validate(&range, 8);
3 bool validateRange(void *pData, int value)
4 {
5 Range *pRange = (Range *)pData;
6 return pRange -> min <= value && value <= pRange -> max;
7 }
内置函数指针:方法
1 typedef struct _RangeValidator{
2 bool (*const RangeValidate)(struct _RangeValidator *pThis, int value);
3 const int min;
4 const int max;
5 }RangeValidator;
6
7 RangeValidator rangeValidator;

接口函数:

1 bool validateRange(struct _RangeValidator *pThis, int value)
2 {
3 return pThis -> min <= value && value <= pThis -> max;
4 }

可以将RangeValidate泛化成一般的函数指针类型Validate,由于每个函数都有一个指向当前对象的pThis指针,因此特殊的结构体类型struct_RangeValidator* 被泛化为void*类型,即可接受任何类型数据的实参。

1 typedef bool(*const Validate)(void *pThis, int value);
2 typedef struct{
3 Validate validate;
4 const int min;
5 const int max;
6 }RangeValidator;

接口函数:

3 bool validateRange(void *pThis, int value)
4 {
5 RangeValidator *pRangeValidator = (RangeValidator *)pThis;
6 return pRangeValidator -> min <= value && value <= pRangeValidator -> max;
7 }
初始化:
define newRangeValidator(min, max) {(validateRange), (min), (max)}

RangeValidator rangeValidator = newRangeValidator(0, 9);

定义指针:

void * pValidator = &rangeValidator;

函数调用:
前提是pValidator指向来了确定的结构体类型。

(RangeValidator *)pValidator -> validate(pValidator, 8);

如果pValidator将指向未知的校验器:
虽然pValidator与&rangeValidator.validate的类型不一样,但他们的值相等,因此可以利用这一特性获取validateRange()函数的地址。

Validate validate = *((Validate *)pValidator);

其调用形式如下:

validate(pValidator, 8);
1 bool rangeCheck(void *pValidator, int value)
2 {
3 Validate validate = *((Validate *)pValidator);
4 return validate(pValidator, value);
5 }
嵌套结构体
结构体数组
1 typedef struct CmdEntry{
2 void (*pfuncmd)();
3 char cHelp[64];
4 }CmdEntry;
static CmdEntry cmdArray[10] = {}
CmdEntry *pCmdEntry = &cmdArray[0]; // pCmdEntry->cHelp 即是cmdArray[0].cHelp
CmdEntry *pCmdEntry = cmdArray;

那么*pCmdEntry=cmdArray[0]

cmdArray[0].cHelp = (*pCmdEntry).cHelp

获取相应函数的入口地址:

cmdArray[0].pfuncmd = &CreateFile;

调用:

cmdArray[0].pfuncmd();