了解malloc()在此上下文中的使用方式

时间:2017-09-17 11:44:55

标签: c++

我不确定这是否是社区标准的好问题(请告诉我这个问题是否有更好的方法或地点)。

我正在努力理解我在尝试学习C ++时遇到的一段代码。代码如下:

MessageHdr *msg;
size_t msgsize = sizeof(MessageHdr) + sizeof(joinaddr->addr) + sizeof(long) + 1;
msg = (MessageHdr *) malloc(msgsize * sizeof(char));

// create JOINREQ message: format of data is {struct Address myaddr}
msg->msgType = JOINREQ;
memcpy((char *)(msg+1), &memberNode->addr.addr, sizeof(memberNode->addr.addr));
memcpy((char *)(msg+1) + 1 + sizeof(memberNode->addr.addr), &memberNode->heartbeat, sizeof(long));
emulNet->ENsend(&memberNode->addr, joinaddr, (char *)msg, msgsize);
  1. 第3行的MessageHdr *投射有什么意义?
  2. 我觉得我们正在构建char[]。我们只是使用MessageHdr*来引用(指向)但我不确定为什么?不会char*成为更好的选择吗?
  3. 接收代码如下(缩短):

    int EmulNet::ENsend(Address *myaddr, Address *toaddr, char *data, int size) {
    en_msg *em;
    ...
    em = (en_msg *)malloc(sizeof(en_msg) + size);
    em->size = size;
    
    memcpy(&(em->from.addr), &(myaddr->addr), sizeof(em->from.addr));
    memcpy(&(em->to.addr), &(toaddr->addr), sizeof(em->from.addr));
    memcpy(em + 1, data, size);
    ...
    

    此时我感到困惑 - 抱歉这个模糊的问题。这是惯用的C ++吗?我觉得这可能是以更清洁的方式完成而不是绕过char[]并通过随机结构类型的指针引用它。

    我想我最终要问的是,虽然我对代码有所了解,但感觉非常不自然。这是一种有效/常见的做事方法吗?

    修改

    MessageHdr是一个结构如下:

    typedef struct MessageHdr {
        enum MsgTypes msgType;
    }MessageHdr;
    

    joinaddr是一个类intances:

    class Address {
    public:
        char addr[6];
        Address() {}
        // Copy constructor
        Address(const Address &anotherAddress);
         // Overloaded = operator
        Address& operator =(const Address &anotherAddress);
        bool operator ==(const Address &anotherAddress);
        Address(string address) {
            size_t pos = address.find(":");
            int id = stoi(address.substr(0, pos));
            short port = (short)stoi(address.substr(pos + 1, address.size()-pos-1));
            memcpy(&addr[0], &id, sizeof(int));
            memcpy(&addr[4], &port, sizeof(short));
        }
        string getAddress() {
            int id = 0;
            short port;
            memcpy(&id, &addr[0], sizeof(int));
            memcpy(&port, &addr[4], sizeof(short));
            return to_string(id) + ":" + to_string(port);
        }
        void init() {
            memset(&addr, 0, sizeof(addr));
        }
    };
    

3 个答案:

答案 0 :(得分:2)

代码真的令人困惑。我会按照我的理解尝试解释第一部分。目的绝对是创建一个(结构化)char缓冲区来发送它。这可能最初是在cc程序员中创建的。

 MessageHdr *msg;

计算生成的发送缓冲区的大小

 size_t msgsize = sizeof(MessageHdr) + sizeof(joinaddr->addr) + sizeof(long) + 1;

分配缓冲区。需要强制转换才能允许c ++编译它,否则会出错。

 msg = (MessageHdr *) malloc(msgsize * sizeof(char));

这用于在缓冲区中设置字段。由于它是MessageHdr类型,因此它将值写入缓冲区的正确位置

// create JOINREQ message: format of data is {struct Address myaddr}
msg->msgType = JOINREQ;

这些命令使用带有(MessageHdr)类型的指针算法将数据写入MessagHdr本身之外的缓冲区中。 msg + 1将跳过char * buffer中的MessageHdf的大小。

memcpy((char *)(msg+1), &memberNode->addr.addr, sizeof(memberNode->addr.addr));
memcpy((char *)(msg+1) + 1 + sizeof(memberNode->addr.addr), &memberNode->heartbeat, sizeof(long));

这将首先通过将缓冲区转换为char*来发送缓冲区,作为一组简单的字节。

emulNet->ENsend(&memberNode->addr, joinaddr, (char *)msg, msgsize);

接收代码似乎在数据中添加了地址头以进一步发送(tcp-ip like)

这将分配另一个缓冲区,其大小为en_msg标题+数据大小。

em = (en_msg *)malloc(sizeof(en_msg) + size);
em->size = size; // keeps data size in the en_msg struct

填写缓冲区的en_msg部分中的地址字段

memcpy(&(em->from.addr), &(myaddr->addr), sizeof(em->from.addr));
memcpy(&(em->to.addr), &(toaddr->addr), sizeof(em->from.addr));

并且这会从en_msg标头

之后的缓冲区中复制数据
memcpy(em + 1, data, size);

答案 1 :(得分:0)

您没有提供有关MessageHdrAddressen_msg的详细信息。但其中一些可能是struct s,而不是简单类型。

对于第一个问题: malloc返回void*,但在第3行中,已分配的内存被分配给类型为MessageHdr*的指针,因此需要将malloc的返回值转换为正确的类型。

以这种方式使用struct是很常见的,因为它提供了一种处理的简单方法,即可以说多个不同类型的变量,它们属于一起(例如Address可以是struct为端口提供了一些int变量,为主机名提供了char[]

示例:

struct Data
{
   int something;
   char somethingElse[10];
};

void* foo = malloc(100);                  // allocate 100 bytes of memory
void* bar = malloc(sizeof(struct Data));  // allocate a piece of memory with the size of struct Data

Data* data = (Data*)bar;                  // use the piece of memory
data->something = 10;                     // as Data struct
strcpy(data->something, "bla");

请注意,您当然可以以任何方式使用分配的内存。 E. g。在上面你可以做memcpy(foo, someSource, 100)将100个字节复制到分配的缓冲区中。

在C ++中,您将使用new运算符,它的工作方式略有不同。除了为给定的类分配内存之外,它还会调用类构造函数。

问题2: 您再次没有提供有关MessageHdr的详细信息。如果它不是struct,而只是e的typedef。 G。 char[10],你是对的,你可以只使用char [10] 但是,想象一下,在整个程序或库中,您需要处理" MessageHdr" (Message-Header?)一遍又一遍,每次都是一个长度为10的char数组。使用typedef可以获得以下好处:

  1. 拥有一个命名类型,其他人可能会立即识别并理解它的作用。
  2. 可以轻松更改char数组的大小,以防稍后需要更改。

答案 2 :(得分:-1)

此代码是无效的C ++代码。指针被强制转换为`(MessageHDR *),以便编译器不会抱怨此代码:

msg->msgtype=JOINREQ

但是,如果MessageHDR具有空的初始化(见下文),则此代码是未定义的行为:这是无效的C ++代码(访问对象成员超出其生命周期)。 N.M. in是评论建议你读一本书,这是最好的解决方案,如果你阅读代码,最好阅读编写良好的C ++代码:stdlibc ++,libc ++ for example。

根据c ++标准[basic.life] / 1

  

对象或引用的生命周期是对象或引用的运行时属性。据说有一个对象   非空的初始化,如果它是一个类或聚合类型,它或它的一个子对象是由a初始化的   除了一个普通的默认构造函数之外的构造函数。 [注意:由普通的复制/移动构造函数初始化   是非空的初始化。 - 结束注释]类型T对象的生命周期从以下开始:   (1.1) - 获得具有适当对齐和T型尺寸的存储,并且   (1.2) - 如果对象具有非空的初始化,则其初始化完成

所以如果MessageHDR有一个非空的初始化(这是使用C ++的点)那么[basic.life] / 6

  

在对象的生命周期开始之前但在对象将占用的存储之后   在对象的生命周期结束之后以及在对象占用的存储之前分配或   重用或释放任何指针,表示对象所在的存储位置的地址   找到的位置可以使用,但只能以有限的方式使用。对于正在建造或销毁的物体,见15.7。   否则,这样的指针指的是已分配的存储(6.7.4.2),并使用指针,就像指针一样   类型void *,定义明确。允许通过这样的指针间接,但结果左值可能只是   以有限的方式使用,如下所述。如果:[...]

,该程序具有未定义的行为      

(6.2) - 指针用于访问非静态数据成员或调用非静态成员函数   对象,o