我不确定这是否是社区标准的好问题(请告诉我这个问题是否有更好的方法或地点)。
我正在努力理解我在尝试学习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);
MessageHdr *
投射有什么意义? char[]
。我们只是使用MessageHdr*
来引用(指向)但我不确定为什么?不会char*
成为更好的选择吗?接收代码如下(缩短):
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));
}
};
答案 0 :(得分:2)
代码真的令人困惑。我会按照我的理解尝试解释第一部分。目的绝对是创建一个(结构化)char缓冲区来发送它。这可能最初是在c
或c
程序员中创建的。
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)
您没有提供有关MessageHdr
,Address
和en_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可以获得以下好处:
答案 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