c - 分配给结构字段时无符号短更改值

标签 c struct short

我有一个函数来解析 20 字节长的 IP 头缓冲区:

void parseIp(struct ipHeader *ip, const void *buffer)
{
    uint8_t* b = buffer;
   // memcpy(b,buffer,20);
    ip->version = (b[0] & 0xf0) >> 4;
    ip->ihl = (b[0] & 0x0f);
    ip->dscp = (b[1] & 0xfC)>>2;
    ip->ecn = (b[1] & 0x3);

    unsigned short l = (b[2] << 8) | b[3];

    printf("%d\n",l);
    ip->length = l;
    ip->identification = (b[4] << 0xFF) | b[5];
}

结构ipHeader:

struct ipHeader {
    int version;
    int ihl;
    int dscp;
    int ecn;
    unsigned short length;
    unsigned short identification;
    int flags;
    int fragment_offset;
    int time_to_live;
    int protocol;
    unsigned short header_checksum;
    unsigned char source_ip[4];
    unsigned char destination_ip[4];
};

现在代码将 l 打印为 467,这是正确的,但由于此 l 已分配给结构字段长度,因此它更改为 54017。我完全不明白发生了什么。我添加了变量 l 以确保不会发生溢出或类型转换错误,但它仍然会发生变化。

这是学校作业的一部分,所以我无法更改结构。

编辑 完整代码:

#include <stdio.h>
#include <arpa/inet.h>
#include "ipheader.h"


/* Parses the given buffer into an IP header structure.
 * 
 * Parameters:
 * ip: pointer to the IP header structure that will be filled based
 *      on the data in the buffer
 * buffer: buffer of 20 bytes that contain the IP header. */
    void parseIp(struct ipHeader *ip, const void *buffer)
    {
        uint8_t* b = buffer;
       // memcpy(b,buffer,20);
        ip->version = (b[0] & 0xf0) >> 4;
        ip->ihl = (b[0] & 0x0f);
        ip->dscp = (b[1] & 0xfC)>>2;
        ip->ecn = (b[1] & 0x3);

        unsigned short l = (b[2] << 8) | b[3];

        printf("%d\n",l);
        ip->length = l;
        ip->identification = (b[4] << 8) | b[5];
    }


/* Builds a 20-byte byte stream based on the given IP header structure
 * 
 * Parameters:
 * buffer: pointer to the 20-byte buffer to which the header is constructed
 * ip: IP header structure that will be packed to the buffer */
void sendIp(void *buffer, const struct ipHeader *ip)
{
}


/* Prints the given IP header structure */
void printIp(const struct ipHeader *ip)
{
    /* Note: ntohs below is for converting numbers from network byte order
     to host byte order. You can ignore them for now
     To be discussed further in Network Programming course... */
    printf("version: %d   ihl: %d   dscp: %d   ecn: %d\n",
            ip->version, ip->ihl, ip->dscp, ip->ecn);
    printf("length: %d   id: %d   flags: %d   offset: %d\n",
            ntohs(ip->length), ntohs(ip->identification), ip->flags, ip->fragment_offset);
    printf("time to live: %d   protocol: %d   checksum: 0x%04x\n",
            ip->time_to_live, ip->protocol, ntohs(ip->header_checksum));
    printf("source ip: %d.%d.%d.%d\n", ip->source_ip[0], ip->source_ip[1],
            ip->source_ip[2], ip->source_ip[3]);
    printf("destination ip: %d.%d.%d.%d\n", ip->destination_ip[0],
            ip->destination_ip[1],
            ip->destination_ip[2], ip->destination_ip[3]);    
}

/* Shows hexdump of given data buffer */
void hexdump(const void *buffer, unsigned int length)
{
    const unsigned char *cbuf = buffer;
    unsigned int i;
    for (i = 0; i < length; ) {
        printf("%02x ", cbuf[i]);
        i++;
        if (!(i % 8))
            printf("\n");
    }
}

struct ipHeader {
    int version;
    int ihl;
    int dscp;
    int ecn;
    unsigned short length;
    unsigned short identification;
    int flags;
    int fragment_offset;
    int time_to_live;
    int protocol;
    unsigned short header_checksum;
    unsigned char source_ip[4];
    unsigned char destination_ip[4];
};

void parseIp(struct ipHeader *ip, const void *buffer);
void sendIp(void *buffer, const struct ipHeader *ip);

void printIp(const struct ipHeader *ip);
void hexdump(const void *buffer, unsigned int length);


#include <arpa/inet.h>
#include "ipheader.h"

int main()
{
    /* Feel free to modify this function to test different things */

    unsigned char bytes[] = {
        0x45, 0x00, 0x01, 0xd3, 0xda, 0x8d, 0x40, 0x00,
        0x40, 0x06, 0x8c, 0xd5, 0xc0, 0xa8, 0x01, 0x46,
        0x6c, 0xa0, 0xa3, 0x33 };

    struct ipHeader ip;

    parseIp(&ip, bytes);
    printIp(&ip);

    struct ipHeader ipfields = {
        4, // version
        28, // ihl
        4, // dscp
        0, // ecn
        htons(1500), // length
        htons(1234), // id
        1, // flags
        1024, // offset
        15, // time_to_live
        33, // protocol
        htons(0x1234), // checksum (invalid)
        {1, 2, 3, 4}, // source IP
        {5, 6, 7, 8} // destination IP
    };
    unsigned char sendbuf[20];

    sendIp(sendbuf, &ipfields);
    hexdump(sendbuf, sizeof(sendbuf));
}

最佳答案

对于给定的输入:

unsigned char bytes[] = { 0x45, 0x00, 0x01, 0xd3, 0xda, 

然后是代码:

unsigned short l = (b[2] << 8) | b[3];

产生l值为 467 .

你在你的问题中说,“因为这个 l 被分配给它更改为 54017 的结构字段长度。”。然而,事实并非如此。如果您在现有 ip->length = l; 之后立即添加一行:

printf("%d\n", ip->length);

你仍然会看到 467 .


我猜你提到的问题是你的 printIp函数打印 54017 .这是因为那个函数不打印ip->length .它打印出 ntohs(ip->length) . ntohs宏将值从 567 更改为至 54017 .

要解决此问题,请更改 printIp打印功能 ip->length ,而不是 ntohs(ip->length) .

删除另一个ntohs也从该函数调用,并删除 htons根据您对 ipfields 的定义.整数应该以 host 顺序(即 native 顺序)存储在 struct ipHeader 中。 ,并在 unsigned char 中以 n 网络顺序(即大端)存储缓冲区。


可移植性说明 1:从技术上讲,您应该使用 %hu作为这两个格式说明符 printf语句,因为参数类型是 unsigned short .

可移植性说明 2: l == 467不管int大小,与迄今为止一些评论/答案中的建议相反。但要支持 b[2] 的值大于 0x7F在具有 16 位 int 的系统上运行时, 你应该写 ((unsigned)b[2] << 8) | b[3] .

可移植性说明 3:最好使用 uint16_t而不是 unsigned short因为现在有 32 位的系统 unsigned short .如果这样做,printf 格式说明符是 "%"PRI16u您可能需要 #include <inttypes.h>

关于c - 分配给结构字段时无符号短更改值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36284195/

相关文章:

python - python struct.pack 中的位顺序

c - 读取二进制文件到-32767到32767的整数范围

c# - `short` 类型的简写?

python - Mac 操作系统, pip : specify compiler for packages containing C libraries

c - 如何以线程友好的方式读取文本文件(字典)

c - 程序自动终止,无需等待我的响应。为什么?

c++ - `struct X typedef` 与 `typedef struct X` 的含义是什么?

c++ - 访问特定结构成员时程序崩溃

c# - 不可能从物体转换到短

java - JNI 多变量