简体   繁体   English

结构数组成员的指针转换问题

[英]Pointer casting problem with struct array member

I've run across this source in a legacy code base and I don't really know why exactly it behaves the way it does. 我已经在旧版代码库中遇到了这个问题,但我真的不知道为什么它的行为方式完全正确。

In the following code, the pData struct member either contains the data or a pointer to the real data in shared memory. 在下面的代码中, pData结构成员包含共享内存中的数据或指向实际数据的指针。 The message is sent using IPC ( msgsnd() and msgrcv() ). 使用IPC( msgsnd()msgrcv() )发送消息。 Using the pointer casts (that are currently commented out), it fails using GCC 4.4.1 on an ARM target, the member uLen gets modified. 使用指针强制转换(当前已被注释掉),在ARM目标上使用GCC 4.4.1失败,成员uLen被修改。 When using memcpy() and everything works as expected. 使用memcpy() ,一切都会按预期工作。 I can't really see what is wrong with the pointer casting. 我真的看不到指针转换有什么问题。 What is wrong here? 怎么了

typedef struct {
    long mtype;
    unsigned short uRespQueue;
    unsigned short uID;
    unsigned short uLen;
    unsigned char pData[8000];
} message_t;

// changing the pointer in the struct
{
    unsigned char *pData = <some_pointer>;
#if 0
    *((unsigned int *)pMessage->pData) = (unsigned int)pData;
#else
    memcpy(pMessage->pData, &pData, sizeof(unsigned int));
#endif
}

// getting the pointer out
{
#if 0
    unsigned char *pData; (unsigned char *)(*((unsigned int *)pMessage->pData));
#else
    unsigned char *pData;
    memcpy(&pData, pMessage->pData, sizeof(int));
#endif
}

I suspect it's an alignment problem and either GCC or the processor is trying to compensate. 我怀疑这是对齐问题,并且GCC或处理器都在尝试补偿。 The structure is defined as: 该结构定义为:

typedef struct {
    long mtype;
    unsigned short uRespQueue;
    unsigned short uID;
    unsigned short uLen;
    unsigned char pData[8000];
} message_t;

Assuming normal alignment restrictions and a 32-bit processor, the offsets of each field are: 假设正常对齐限制和32位处理器,则每个字段的偏移量为:

mtype         0   (alignment 4)
uRespQueue    4   (alignment 2)
uID           6   (alignment 2)
uLen          8   (alignment 2)
pData         10  (alignment 1)

On all but the most recent versions of the ARM processor, memory access must be aligned on the ARM processor and with the casting: 在除最新版本的ARM处理器上,所有的内存访问必须在ARM处理器上和以下类型进行对齐:

*((unsigned int *)pMessage->pData) = (unsigned int)pData;

you are attempting to write a 32-bit value on a misaligned address. 您正在尝试在未对齐的地址上写入32位值。 To correct the alignment, the address appears to have truncated the LSB's of the address to have the proper alignment. 为了更正对齐方式,该地址似乎已截断了地址的LSB,以具有正确的对齐方式。 Doing so happened to overlap with the uLen field causing the problem. 这样做恰巧与uLen字段重叠,从而导致了问题。

To be able to handle this correctly, you need to make sure that you write the value to a properly aligned address. 为了能够正确处理此问题,您需要确保将值写入正确对齐的地址。 Either offset the pointer to align it or make sure pData is aligned to be able to handle 32-bit data. 偏移指针以使其对齐,或者确保pData已对齐以能够处理32位数据。 I would redefine the structure to align the pData member for 32-bit access. 我将重新定义结构以对齐pData成员以进行32位访问。

typedef struct {
    long mtype;
    unsigned short uRespQueue;
    unsigned short uID;
    unsigned short uLen;
    union { /* this will add 2-bytes of padding */
        unsigned char *pData;
        unsigned char  rgData[8000];
    };
} message_t;

The structure should still occupy the same amount of bytes since it has a 4-byte alignment due to the mtype field. 由于mtype字段,该结构具有4个字节的对齐方式,因此该结构仍应占用相同数量的字节。

Then you should be able to access the pointer: 然后,您应该能够访问指针:

unsigned char *pData = ...;
/* setting the pointer */
pMessage->pData = pData;

/* getting the pointer */
pData = pMessage->pData;

The point here is the code "int header = ( ((int )(txUserPtr) - 4))" Illustration of UserTypes and struct pointer casting is great of help! 关键是代码“ int header =(( ((int )(txUserPtr)-4))”)UserTypes和struct指针转换的说明很有帮助!

typedef union UserTypes
{
    SAUser           AUser;
    BUser            BUser;
    SCUser           CUser;
    SDUser           DUser;
} UserTypes;

typedef struct AUser
{
    int              userId;
    int              dbIndex;
    ChannelType      ChanType;
 } AUser;
typedef struct AUser
{
    int              userId;
    int              dbIndex;
    ChannelType      ChanType;
 } AUser;

typedef struct BUser
{
    int              userId;
    int              dbIndex;
    ChannelType      ChanType;
 } BUser;

typedef struct CUser
{
    int              userId;
    int              dbIndex;
    ChannelType      ChanType;
 } CUser;

typedef struct DUser
{
    int              userId;
    int              dbIndex;
    ChannelType      ChanType;
 } DUser;

//this is the function I want to test

void Fun(UserTypes * txUserPtr)
{

   int header = (*((int*)(txUserPtr) - 4));

   //the problem is here
   //how should i set incoming pointer "txUserPtr" so that 
   //Fun() would skip following lines.
   // I don't want to execute error()

        if((header & 0xFF000000) != (int)0xAA000000)
        {
            error("sth error\n");
        }
   /*the following is the rest */ 
}

That is a very nasty thing to do (the thing that's compiled out). 这是一件很讨厌的事情(编译出来的事情)。 You're trying basically to hack the code, and instead of using the data copy in the message (in the provided 8000 bytes for it), you try to put a pointer, and pass it through IPC. 您基本上是在尝试破解代码,而不是使用消息中的数据副本(为其提供的8000个字节),而是尝试放置一个指针,并将其传递给IPC。

The main issue is sharing memory between processes. 主要问题是在进程之间共享内存。 Who knows what happens to that pointer after you send it? 发送该指针后,谁知道该指针发生了什么? Who knows what happens to the data it points to? 谁知道指向的数据会发生什么? That's a very bad habbit to send out a pointer to data that is not under your control (ie: not protected/properly shared). 发送一个指向不受您控制的数据的指针(即:不受保护/正确共享)是非常糟糕的习惯。

Another thing that might happen, and is probably what you're actually talking about, is the alignment. 对齐可能会发生的另一件事,也可能是您实际上正在谈论的问题。 The array is of char 's, the previous member in the struct is short , the compiler might attempt packing them. 该数组是char的数组,结构中的前一个成员很short ,编译器可能会尝试打包它们。 Recasting char[] to int * means that you take memory area and represent it as something else, without telling the compiler. char[]重铸为int *意味着您无需告知编译器即可占用内存区域并将其表示为其他内容。 You're stomping over the uLen by the cast. 您在演员uLen踩踏uLen

memcopy is the proper way to do it. memcopy是执行此操作的正确方法。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM