简体   繁体   English

了解如何在此上下文中使用malloc()

[英]Understanding how malloc() is used in this context

I'm not sure if this is a good question by community standards (let me know if there is a better way or place for this question please). 从社区标准来看,我不确定这是否是一个好问题(请告诉我是否有解决此问题的更好方法或地点)。

I'm working on understanding a piece of code which I've come across while trying to learn C++. 我正在努力理解一段尝试学习C ++时遇到的代码。 Code is as follows: 代码如下:

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. What is the point of casting to MessageHdr * in line 3? 在第3行中强制转换为MessageHdr *有什么意义?
  2. I feel like we're building a char[] . 我觉得我们正在构建char[] We're just using MessageHdr* to refer (to point) to it but I am not sure why? 我们只是使用MessageHdr*来(指向)它,但是我不确定为什么吗? Wouldn't a char* be a better choice? 一个char*会不是更好的选择?

Receiving code is as follows (shortened): 接收代码如下(缩短):

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);
...

I'm beyond confused at this point - sorry for the vague question. 在这一点上,我感到很困惑-对这个模糊的问题表示抱歉。 Is this idiomatic C++? 这是惯用的C ++吗? I feel as if this could have been done in much cleaner ways instead of passing around a char[] and referencing it via pointers of random struct types. 我觉得这本可以用更简洁的方式完成,而不是传递char[]并通过随机结构类型的指针进行引用。

I guess what I'm ultimately trying to ask is, while I kind of understand the code, it feels very unnatural. 我想我最终想问的是,虽然我有点理解代码,但感觉却很不自然。 Is this a valid/common approach of doing things? 这是一种有效/通用的做事方法吗?

EDIT 编辑


MessageHdr is a struct as follows: MessageHdr是如下结构:

typedef struct MessageHdr {
    enum MsgTypes msgType;
}MessageHdr;

joinaddr is a class intances: joinaddr是一个类实例:

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));
    }
};

The code is really confusing. 该代码确实令人困惑。 I'll try to explain the first part as I understand it. 我将尽我所能解释第一部分。 The intention definitely was to create a (structured) char buffer to send it over. 这样做的目的肯定是创建一个(结构化的)char缓冲区以将其发送出去。 This was probably initially created in c , or by a c programmer. 这可能是最初创建中c ,或由c程序员。

 MessageHdr *msg;

this calculates size of the resulting send buffer 这将计算结果发送缓冲区的大小

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

allocates the buffer. 分配缓冲区。 The cast is needed to allow c++ to compile it, otherwise it will error-out. 需要强制转换以允许c ++对其进行编译,否则它将出错。

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

This is used to set up a field in the buffer. 这用于在缓冲区中设置字段。 Since it is of Type MessageHdr, it writes the value in the correct place of the buffer 由于它是MessageHdr类型,因此它将值写入缓冲区的正确位置

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

These commands use pointer arithmetic with (MessageHdr) type to write data in the buffer beyond the MessagHdr itself. 这些命令使用(MessageHdr)类型的指针算术将数据写入MessagHdr本身以外的缓冲区。 msg + 1 will skip the size of the MessageHdf in the char* buffer. msg + 1将跳过char *缓冲区中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));

this will send the buffer by casting it to char* first, as a simple set of bytes. 这将通过将缓冲区首先转换为char* (作为一组简单的字节)来发送缓冲区。

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

The receiving code seems to add yet address header to the data to send it further (tcp-ip like) 接收代码似乎在数据中添加了地址标头以进一步发送数据(类似于tcp-ip)

This allocates another buffer with the size of the en_msg header + size of the data. 这将分配另一个缓冲区,其大小为en_msg标头的大小+数据的大小。

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

fills out address fields in the en_msg part of the buffer 填写缓冲区的en_msg部分中的地址字段

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

and this copies the data in the buffer starting just beyond the en_msg header 并且这会将数据复制到缓冲区中刚从en_msg标头开始的位置

memcpy(em + 1, data, size);

.

You didnt give details about MessageHdr , Address and en_msg . 您没有提供有关MessageHdrAddressen_msg详细信息。 But some of them might be struct s, and not simple types. 但是其中一些可能是struct ,而不是简单的类型。

For the first question: malloc returns a void* , but in line 3 the allocated memory is assigned to a pointer of type MessageHdr* , thus the return value of malloc needs to be casted to the correct type. 对于第一个问题: malloc返回void* ,但是在第3行中,分配的内存分配给类型为MessageHdr*的指针,因此malloc的返回值需要转换为正确的类型。

It is quite common to use struct s in this way, as it provides a simple way of dealing with lets say multiple variables of different type, which shall belong together (eg Address could be a struct with some int variable for the port, and a char[] for the hostname). 以这种方式使用struct是很常见的,因为它提供了一种简单的方式来处理可以说属于同一类型的多个不同类型的变量(例如, Address可以是带有一些int变量的struct ,用于端口,而a可以是char[]作为主机名)。

Example: 例:

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");

Note that of course you could use the piece of allocated memory in any way you want. 请注意,您当然可以以任何需要的方式使用分配的内存。 E. g. 例如 in the above you could just do memcpy(foo, someSource, 100) to copy 100 bytes into the allocated buffer. 在上面,您可以执行memcpy(foo, someSource, 100)将100字节复制到分配的缓冲区中。

In C++ you would use the new operator, which works slightly different. 在C ++中,您将使用new运算符,其作用略有不同。 In addition to allocating memory for a given class, it would also call the classes constructor. 除了为给定的类分配内存外,它还将调用类的构造函数。

For question 2: Again you didn't give details about MessageHdr . 对于问题2:同样,您没有提供有关MessageHdr详细信息。 In case it is not a struct , but only a typedef to eg char[10] , you are right in that you could just use char[10] instead. 如果它不是struct ,而只是typedef,例如char[10] ,那么您可以使用char [10]代替,这是正确的。
However, imagine throughout your program or library you need to deal with "MessageHdr" (Message-Header?) over and over again, and every time it is a char array with the length 10. Using a typedef you gain the benefit of: 但是,想象一下在整个程序或库中,您需要一次又一次地处理“ MessageHdr”(Message-Header?),每次它都是一个长度为10的char数组。使用typedef,您将获得以下好处:

  1. Having a named type, which others might instantly recognize and understand what it does. 具有命名类型,其他人可能会立即识别并理解它的功能。
  2. The possibility to easily change the size of the char array in case at some later point it needs to change. 可以很容易地更改char数组的大小,以便在以后需要更改的情况下使用。

This code is invalid C++ code. 该代码是无效的C ++代码。 The pointer is cast to `(MessageHDR*) in order that the compiler does not complain about this code: 指针被强制转换为(MessageHDR *),以使编译器不会抱怨以下代码:

msg->msgtype=JOINREQ

But this code is undefined behaviour if MessageHDR has vacuous initialization(see below): this is invalid C++ code (access object member out of its life-time period). 但是,如果MessageHDR具有空虚初始化,则此代码是未定义的行为(请参见下文):这是无效的C ++代码(超出其生存期的访问对象成员)。 nm in is comment propose you to read a book, this is the best solution, and if you read code, it would be better to read well written C++ code: stdlibc++, libc++ for exemple. nm中的注释建议您阅读一本书,这是最好的解决方案,并且,如果您阅读代码,则最好阅读编写良好的C ++代码:例如stdlibc ++,libc ++。


According to the c++ standard [basic.life]/1 根据c ++标准[basic.life] / 1

The lifetime of an object or reference is a runtime property of the object or reference. 对象或引用的生存期是对象或引用的运行时属性。 An object is said to have non-vacuous initialization if it is of a class or aggregate type and it or one of its subobjects is initialized by a constructor other than a trivial default constructor. 如果对象属于类或聚合类型,并且其或其子对象之一由普通的默认构造函数以外的构造函数初始化,则称该对象具有非空初始化。 [ Note: Initialization by a trivial copy/move constructor is non-vacuous initialization. [注意:通过简单的复制/移动构造函数进行的初始化是非空初始化。 — end note ] The lifetime of an object of type T begins when: (1.1) — storage with the proper alignment and size for type T is obtained, and (1.2) — if the object has non-vacuous initialization, its initialization is complete —结束说明]类型T的对象的生存期始于以下时间:(1.1)—获得具有类型T正确的对齐方式和大小的存储,以及(1.2)—如果对象具有非空初始化,则其初始化完成

So if MessageHDR has a non-vacuous initialization (which is the point of using C++) then [basic.life]/6 因此,如果MessageHDR具有非空初始化(这是使用C ++的关键),则[basic.life] / 6

Before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any pointer that represents the address of the storage location where the object will be or was located may be used but only in limited ways. 在对象的生存期开始之前但已分配了该对象将占用的存储空间之后,或者在对象的生存期结束之后以及该对象占用的存储空间被重用或释放之前,表示指针地址的任何指针可以使用对象将要位于的存储位置,但只能以有限的方式使用。 For an object under construction or destruction, see 15.7. 对于正在建造或销毁的物体,请参见15.7。 Otherwise, such a pointer refers to allocated storage (6.7.4.2), and using the pointer as if the pointer were of type void*, is well-defined. 否则,此类指针将引用已分配的存储空间(6.7.4.2),并且使用该指针时就像该指针的类型为void *一样,是定义良好的。 Indirection through such a pointer is permitted but the resulting lvalue may only be used in limited ways, as described below. 允许通过此类指针进行间接访问,但生成的左值只能以有限的方式使用,如下所述。 The program has undefined behavior if:[...] 该程序在以下情况下具有未定义的行为:[...]

(6.2) — the pointer is used to access a non-static data member or call a non-static member function of the object, o (6.2)—指针用于访问对象的非静态数据成员或调用对象的非静态成员函数,o

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

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