今天在项目中需要使用到结构体数据成员的偏移量计算,可以使用下面的方法来进行巧妙的获取,注意区别数据成员的赋值操作哦;
# 1. 结构体指针为0时,数据成员赋值会内存访问崩溃
# 2. 结构体指针为0时,数据成员可以内存访问并计算偏移量
- 源代码:
```cpp
#include "stdafx.h"
#if defined (BUILD_AMBARELLA_AMBACV_DRV) && defined (BUILD_AMBARELLA_CAVALRY_DRV)
#error "Can not enable ambacv and cavalry at the same time"
#elif defined (BUILD_AMBARELLA_AMBACV_DRV)
#define CAVALRY_DEV_NODE "/dev/ambacv"
#else
#define CAVALRY_DEV_NODE "/dev/cavalry"
#endif
typedef struct
{
int nHasMm2S; /* Has transmit channel */
int nHasS2Mm; /* Has receive channel */
int nInitialized; /* Driver has been initialized */
int nHasSg;
int nTxNumChannels;
int nRxNumChannels;
int nMicroDmaMode;
int nAddrWidth; /**< Address Width */
}stuXAxiDma;
/*****************************************************************************/
/** 1.5 chip common part, audio interface, depend on DMA interface
******************************************************************************/
#define VAROFFSET(type, mem) ((unsigned long)(&((type *)0)->mem))
#define GET_STRUCT_ENTRY(ptr, type, mem) ((type *)((char *)ptr - VAROFFSET(type, mem)))
/*
*callback function
*Check interrupt status and assert s2mm flag
*/
typedef struct
{
int* pBdTxChainBuffer;
int* pBdRxChainBuffer;
int nDmaDevId;
int nS2mmIntrId;
int nMm2sIntrId;
int nBdCount;
char cBdNum; // 注意未对齐32位系统占4个字节
void(*s2mmFinishCb)(void*);
void(*mm2sFinishCb)(void*);
stuXAxiDma axiDma;
}stuChipAudioChannel;
static void dmaInterruptHandler(void* pCallBackRef)
{
// 进行指针转换
stuXAxiDma* pXAxiDmaPtr = (stuXAxiDma *)pCallBackRef;
printf("dmaInterruptHandler::pCallBackRef=[%p]\n", pCallBackRef);
//注意堆的特性,向上生长,地址变小
//stuChipAudioChannel *p_audio_channel = (stuChipAudioChannel *)((char *)ptr - ((int)&((stuChipAudioChannel*)0)->axiDma));
stuChipAudioChannel* pAudioChannel = GET_STRUCT_ENTRY(pCallBackRef, stuChipAudioChannel, axiDma);
printf("dmaInterruptHandler::pAudioChannel=[%p]\n", pAudioChannel);
}
int _tmain(int argc, _TCHAR* argv[])
{
// 申请内存给指针ptr赋值
stuXAxiDma* ptr = new stuXAxiDma();
//指针ptr设置为空((void*)0)
//ptr = NULL;
// 设置指针ptr的数据成员nAddrWidth = 0
ptr->nAddrWidth = 0; // 内存访问会报错
// 只是访问取数据成员变量没问题
int nAddrWidthOffset = (int)&(ptr->nAddrWidth);
// 能够直接取出偏移量来
int nBdTxChainBufferOffset = ((int)(&((stuChipAudioChannel*)0)->pBdTxChainBuffer));
// nBdTxChainBufferOffset=0
printf("_tmain::nBdTxChainBufferOffset=[%d]\n", nBdTxChainBufferOffset);
int nDmaDevIdOffset = ((int)(&((stuChipAudioChannel*)0)->nDmaDevId));
printf("_tmain::nDmaDevIdOffset=[%d]\n", nDmaDevIdOffset);
int nS2mmFinishCbOffset = ((int)(&((stuChipAudioChannel*)0)->s2mmFinishCb));
printf("_tmain::nS2mmFinishCbOffset=[%d]\n", nS2mmFinishCbOffset);
int nAxiDmaOffset = ((int)(&((stuChipAudioChannel*)0)->axiDma));
printf("_tmain::nAxiDmaOffset=[%d]\n", nAxiDmaOffset);
// 申请堆内存给pXAxiDma变量
stuXAxiDma* pXAxiDma = new stuXAxiDma;
dmaInterruptHandler(pXAxiDma);
return 0;
}
```
- 输出结果:
# 3. 原理解释:
- 在结构体成员变量前面加上取地址符号&后表示取的是`nAddrWidth`字段的地址,而不是引用该字段内容,因此不会产生段错误。
- 如果`ptr`为空,直接取该成员变量的内容或者更新、设置该内存的值时会发生段错误。