MST

星途 面试题库

面试题:C语言联合体与结构体在复杂场景下的内存布局与应用

考虑一个通信协议,其中数据包的格式定义如下: ```c struct Header { unsigned int version: 4; unsigned int type: 4; unsigned int length: 16; }; union Payload { struct { char data[100]; } normal; struct { int errorCode; char errorMsg[50]; } error; }; struct Packet { struct Header h; union Payload p; }; ``` 1. 详细分析`struct Header`的内存布局,特别是位域的分配方式。 2. 计算`union Payload`和`struct Packet`的大小,并说明理由。 3. 假设要通过网络发送`struct Packet`,如何确保在不同系统(如大端和小端系统)之间正确传输和解析数据?请给出具体的实现思路或代码示例(可使用C语言标准库函数)。
24.3万 热度难度
编程语言C

知识考点

AI 面试

面试题答案

一键面试

1. struct Header内存布局分析

  • 位域分配方式
    • version占据4位,它从Header结构体的起始字节的低4位开始存储。例如,如果Header结构体起始地址为0x1000,那么version的值就存储在0x1000这个字节的低4位。
    • type同样占据4位,紧跟在version之后,使用起始字节的高4位。所以typeversion共用第一个字节。
    • length占据16位,即2个字节。它从第二个字节开始存储,这样Header结构体总共占用3个字节。

2. 大小计算

  • union Payload大小
    • union的大小是其最大成员的大小。normal成员中data数组大小为100字节,error成员中errorCode(假设int为4字节)加上errorMsg数组(50字节)总共为54字节。所以union Payload的大小为100字节。
  • struct Packet大小
    • struct Packet包含一个struct Header和一个union Payloadstruct Header大小为3字节,union Payload大小为100字节。在C语言中,结构体的大小需要考虑内存对齐。通常结构体的大小是其最大成员大小的整数倍。这里最大成员大小为union Payload的100字节,所以struct Packet的大小为100 + 3 = 103字节,为了满足对齐要求,实际大小为104字节(因为103不是4的倍数,而104是4的倍数,假设系统以4字节对齐)。

3. 不同系统间传输和解析数据

  • 实现思路
    • 字节序转换:在发送端,将结构体中的多字节数据(如lengtherrorCode)转换为网络字节序(大端序)。在接收端,将接收到的数据转换回主机字节序。
    • 内存对齐:在发送端,确保结构体在内存中是按正确的对齐方式存储的,并且在接收端以相同的对齐方式解析。
  • 代码示例
#include <stdio.h>
#include <arpa/inet.h>
#include <string.h>

// 假设要发送的Packet
struct Header { 
    unsigned int version: 4; 
    unsigned int type: 4; 
    unsigned int length: 16; 
}; 

union Payload { 
    struct { 
        char data[100]; 
    } normal; 
    struct { 
        int errorCode; 
        char errorMsg[50]; 
    } error; 
}; 

struct Packet { 
    struct Header h; 
    union Payload p; 
}; 

// 发送函数
void sendPacket(struct Packet *packet, char *buffer) {
    // 转换length到网络字节序
    packet->h.length = htons(packet->h.length);
    // 转换errorCode到网络字节序(如果是error类型)
    if (packet->h.type == 1) { // 假设type为1表示error类型
        packet->p.error.errorCode = htonl(packet->p.error.errorCode);
    }
    // 复制数据到buffer
    memcpy(buffer, packet, sizeof(struct Packet));
}

// 接收函数
void receivePacket(char *buffer, struct Packet *packet) {
    // 从buffer复制数据到packet
    memcpy(packet, buffer, sizeof(struct Packet));
    // 转换length到主机字节序
    packet->h.length = ntohs(packet->h.length);
    // 转换errorCode到主机字节序(如果是error类型)
    if (packet->h.type == 1) {
        packet->p.error.errorCode = ntohl(packet->p.error.errorCode);
    }
}

在实际应用中,可以结合网络编程接口(如socket)来使用上述函数进行数据的发送和接收。