2013-10-14 94 views
8

我一直在使用Valgrind尋找我的代碼中的內存泄漏,而沒有發現內存泄漏,一些錯誤報告所有源自於單功能/類方法:「指向未初始化的字節」Valgrind錯誤

==17043== ERROR SUMMARY: 10100 errors from 3 contexts (suppressed: 0 from 0) 
==17043== 
==17043== 100 errors in context 1 of 3: 
==17043== Syscall param socketcall.sendto(msg) points to uninitialised byte(s) 
==17043== at 0x5441DA2: send (send.c:28) 
==17043== by 0x404C2D: unix_socket::sendMsg(char, double) (in /home/joao/CloudPT/Bolsa/Webots/controllers/darwin-pi2/client) 
==17043== by 0x404F1C: unix_socket::sendVectorXd(Eigen::Matrix<double, -1, 1, 0, -1, 1> const&) (in /home/joao/CloudPT/Bolsa/Webots/controllers/darwin-pi2/client) 
==17043== by 0x401F2A: main (in /home/joao/CloudPT/Bolsa/Webots/controllers/darwin-pi2/client) 
==17043== Address 0x7feffff61 is on thread 1's stack 
==17043== Uninitialised value was created by a stack allocation 
==17043== at 0x404BE6: unix_socket::sendMsg(char, double) (in /home/joao/CloudPT/Bolsa/Webots/controllers/darwin-pi2/client) 
==17043== 
==17043== 
==17043== 100 errors in context 2 of 3: 
==17043== Syscall param socketcall.sendto(msg) points to uninitialised byte(s) 
==17043== at 0x5441DA2: send (send.c:28) 
==17043== by 0x404C2D: unix_socket::sendMsg(char, double) (in /home/joao/CloudPT/Bolsa/Webots/controllers/darwin-pi2/client) 
==17043== by 0x404E8A: unix_socket::sendVectorXd(Eigen::Matrix<double, -1, 1, 0, -1, 1> const&) (in /home/joao/CloudPT/Bolsa/Webots/controllers/darwin-pi2/client) 
==17043== by 0x401F2A: main (in /home/joao/CloudPT/Bolsa/Webots/controllers/darwin-pi2/client) 
==17043== Address 0x7feffff61 is on thread 1's stack 
==17043== Uninitialised value was created by a stack allocation 
==17043== at 0x404BE6: unix_socket::sendMsg(char, double) (in /home/joao/CloudPT/Bolsa/Webots/controllers/darwin-pi2/client) 
==17043== 
==17043== 
==17043== 9900 errors in context 3 of 3: 
==17043== Syscall param socketcall.sendto(msg) points to uninitialised byte(s) 
==17043== at 0x5441DA2: send (send.c:28) 
==17043== by 0x404C2D: unix_socket::sendMsg(char, double) (in /home/joao/CloudPT/Bolsa/Webots/controllers/darwin-pi2/client) 
==17043== by 0x404EE8: unix_socket::sendVectorXd(Eigen::Matrix<double, -1, 1, 0, -1, 1> const&) (in /home/joao/CloudPT/Bolsa/Webots/controllers/darwin-pi2/client) 
==17043== by 0x401F2A: main (in /home/joao/CloudPT/Bolsa/Webots/controllers/darwin-pi2/client) 
==17043== Address 0x7feffff61 is on thread 1's stack 
==17043== Uninitialised value was created by a stack allocation 
==17043== at 0x404BE6: unix_socket::sendMsg(char, double) (in /home/joao/CloudPT/Bolsa/Webots/controllers/darwin-pi2/client) 
==17043== 
==17043== ERROR SUMMARY: 10100 errors from 3 contexts (suppressed: 0 from 0) 

sendMsg(const char _type, const double _value),該錯誤是在指向,是unix_socket類的一部分:

//... 
typedef struct{ 
    char type;  
    double value; 
} MESSAGE; 

//... 
int unix_socket::sendMsg(const char _type, const double _value){ 
    MESSAGE msg; 
    msg.type=_type; 
    msg.value=_value; 
    int n = send(client_sock, &msg, sizeof(msg), 0); 
    if (n < 0) { 
     perror("send"); 
     return -1; 
    } 
    c_sent=msg.type; 
    v_sent=msg.value; 
    return 0; 
} 

我看不出有什麼問題。未初始化的值究竟在哪裏?或者我應該忽略Valgrind報告的錯誤?

回答

13

看那MESSAGE結構:

typedef struct{ 
    char type;  
    double value; 
} MESSAGE; 

由於數據結構對準,value的地址可能被迫對準字大小的倍數的地址。因此,幾個未使用的字節被填充在MESSAGE::type和​​之間。那些是沒有初始化的字節,因此Valgrind報告了這些字節。

作爲解決方法,您可以通過memset()強制初始化整個結構。

MESSAGE msg; 
memset(&msg, 0, sizeof(MESSAGE)); 
msg.type=_type; 
msg.value=_value; 
+1

另一種方法是將結構打包爲一個字節向量併發送該結構,然後收緊至每個元素的確切大小。以結構形式通過線路發送結構絕不是一個好主意。讀取當前機制的客戶端無法知道結構填充是什麼,因此無法知道「值」是否在1字節,2字節,4字節或甚至8字節上對齊。如果我爲此編碼,我可能會使用長度限定協議,並同時打包和解壓縮代碼以確保值正確,從而消除對齊問題。 – WhozCraig

+0

你很好,工作!但是,如果保持原樣,我可能遇到什麼缺點? – joaocandre

+0

@WhozCraig我只是使用它作爲Unix套接字在兩個不同的程序之間共享數據,它會是值得的麻煩? – joaocandre

9

雖然@timrau已經十分正確的核心問題是什麼在這裏(校準/包裝)所描述的,我不是建議的解決方案的粉絲。

您已經在您的代碼中描述了MESSAGEchardouble組成。然而,內存中的實際數據結構的大小不是sizeof(char) + sizeof(double),是核心問題。

建議的解決方案建議在填寫重要位之前簡單地清除MESSAGE結構的所有位。我遇到的問題既是語義問題,也是技術問題 - 發送數據結構的大小並不是您在代碼中建模的準確表示。換句話說,你不只是發送一個char和一個double - 你發送一個char,一個double和一些其他cruft(填充)。

我的建議是擺脫cruft並只發送你在你的代碼中建模。

有一個在C++關閉對齊和填充不直接支持,但所有的編譯器我知道提供一種簡單的機制,以數據結構對齊N字節:

#pragma pack (push, 1) 

typedef struct{ 
    char type;  
    double value; 
} MESSAGE; 

#pragma pack (pop) 

這將使MESSAGE數據結構完全是您在代碼中建模的內容,沒有填充。這樣做會使得memset變得不切實際,並且您將精確地發送sizeof(char) + sizeof(double)字節。

+0

這可能是一個基本問題,但即使該結構打包爲1字節對齊,接收端(套接字的另一側)如何知道? – joaocandre

+0

客戶端需要相同的結構定義 - 就像如果你沒有打包。 –

+0

如果我必須快速做到這一點,這是我會採取的方法。如果我必須這樣做*可移植*,我可能會花時間序列化成員。但是,這仍然值得我投票贊成,並相應地做出決定。從我所見到的情況來看,這足以滿足OP的要求。 – WhozCraig