2015-03-03 12 views
33

समस्या

में डिफ़ॉल्ट तर्क segfaults के लिए खाली ब्रेस-initializers के साथ std :: नक्शा तर्क मुझे प्रयोक्ता की ओर एक बग रिपोर्ट पुस्तकालय में एक segfault मैं विकसित रिपोर्टिंग मिला है।जीसीसी

दोषपूर्ण कोड के न्यूनतम उदाहरण है:

#include <map> 
#include <string> 
#include <iostream> 

void f(std::map<std::string, std::string> m = {}) 
{ 
     std::cout << m.size() << "\n"; 
     for (const auto& s: m) { 
       std::cout << s.first << "->" << s.second <<"\n"; 
     } 
} 

int main() 
{ 
     f(); 
} 

जब जीसीसी के साथ संकलित (मैं 4.8.2 और 4.7.3 परीक्षण किया) इसे सही ढंग से 0 कंटेनर के आकार के रूप प्रिंट, लेकिन पाश अंदर segfaults (सभी पर क्रियान्वित नहीं किया जाना चाहिए जो जा सकती है)।

void f(std::map<std::string, std::string> m = std::map<std::string, std::string>{}) 

रूप में अच्छी तरह से काम करता है map को कॉपी करना::

void f(std::map<std::string, std::string> mx = {}) 
{ 
     auto m = mx; 
     std::cout << m.size() << "\n"; 
     for (const auto& s: m) { 
       std::cout << s.first << "->" << s.second <<"\n"; 
     } 
} 

const std::map<...>& पैरामीटर बदलने

समाधान

हालांकि, मैं समस्या का घोषणा बदलकर ठीक कर सकते हैं यह भी काम करता है।

जीसीसी 4.9.1 ठीक काम करता है।

क्लैंग भी संकलित करता है और कोड को ठीक करता है।

(तब भी जब जीसीसी 4.8.2 में नाकाम रहने के रूप में एक ही libstdC++ का प्रयोग करके) कार्य करना उदाहरण: http://coliru.stacked-crooked.com/a/eb64a7053f542efd

प्रश्न

नक्शा निश्चित रूप से समारोह के अंदर वैध राज्य में नहीं है (विवरण bellow)। यह एक जीसीसी (या libstdC++) बग की तरह लग रहा है, लेकिन मुझे यकीन है कि मैं कुछ बेवकूफ गलती यहाँ नहीं बना रहा हूं होना चाहता हूँ। यह एक बग कम से कम 2 मुख्य संस्करण के लिए जीसीसी में रहना होता था पर विश्वास करना मुश्किल है।

तो मेरा सवाल यह है: क्या डिफ़ॉल्ट std::map पैरामीटर गलत (और मेरे कोड में बग) शुरू करने का तरीका है या यह stdlibc++ (या gcc) में एक बग है?

मैं कामकाज की तलाश नहीं कर रहा हूं (जैसा कि मुझे पता है कि कोड काम करने के लिए क्या करना है) एप्लिकेशन में एकीकृत होने पर, अपमानजनक कोड कुछ कंप्यूटरों पर ठीक निष्पादित करता है (यहां तक ​​कि जब gcc 4.8.2 के साथ संकलित किया जाता है) कुछ नहीं करता है।

विवरण

मैं इसे का उपयोग कर संकलन:

gdb से
g++-4.8.2 -g -Wall -Wextra -pedantic -std=c++11 /tmp/c.cpp -o /tmp/t 

पश्व-अनुरेखन:

#0 std::operator<< <char, std::char_traits<char>, std::allocator<char> > (__os=..., __str=...) at /usr/src/debug/sys-devel/gcc-4.8.2/build/x86_64-pc-linux-gnu/libstdc++-v3/include/bits/basic_string.h:2758 
#1 0x0000000000400f36 in f (m=std::map with 0 elements) at /tmp/c.cpp:9 
#2 0x0000000000400fe0 in main() at /tmp/c.cpp:15 

/tmp/सी।सीपीपी:

AddressSanitizer: SEGV on unknown address 0xffffffffffffffe8 

यह लगता है nullptr - 8 तरह

valgrind पता चलता है::

==28183== Invalid read of size 8 
==28183== at 0x4ECC863: std::basic_ostream<char, std::char_traits<char> >& std::operator<< <char, std::char_traits<char>, std::allocator<char> >(std::basic_ostream<char, std::char_traits<char> >&, std::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) (in /usr/lib64/gcc/x86_64-pc-linux-gnu/4.8.2/libstdc++.so.6.0.18) 
==28183== by 0x400BD5: f(std::map<std::string, std::string, std::less<std::string>, std::allocator<std::pair<std::string const, std::string> > >) (c.cpp:9) 
==28183== by 0x400C7F: main (c.cpp:15) 
==28183== Address 0xffffffffffffffe8 is not stack'd, malloc'd or (recently) free'd 

मानचित्र की आंतरिक स्थिति को देखते हुए पता चलता है कि 9 std::cout << ...

आसन रिपोर्ट के साथ लाइन है कोड वास्तव में विफल होना है:

libstdc मेंरिटर्न

this->_M_impl._M_header._M_parent 

का मूल्य से यह आंतरिक प्रतिनिधित्व, std::map::end() रिटर्न है:

&this->_M_impl._M_header 

gdb पता चलता है:

(gdb) print m._M_t._M_impl._M_header 
$5 = {_M_color = std::_S_red, _M_parent = 0x0, _M_left = 0x7fffffffd6d8, _M_right = 0x7fffffffd6d8} 
(gdb) print &m._M_t._M_impl._M_header 
$6 = (std::_Rb_tree_node_base *) 0x7fffffffd6a8 

तो begin() और end() का मूल्य ही नहीं हैं (begin() nullptr है) खाली std::map के लिए मानक द्वारा अनिवार्य है।

+0

Fwiw, जब मैं << प्रतिस्थापित 'std :: अदालत << "-> s.first" << s.second << "\ n"; 'द्वारा std :: अदालत' << "यहाँ \ n" आया, ', कार्यक्रम एक बार लाइन को प्रिंट करता है और फिर लटकता है। –

+4

"यह विश्वास करना मुश्किल है कि इस तरह की एक बग कम से कम 2 प्रमुख संस्करण के लिए जीसीसी में रहेगी।" - आपको यहां नया होना चाहिए –

उत्तर

22

इस bug was fixed in 4.8.3/4.9.0, बग रिपोर्ट जो एक समान उदाहरण है और यह भी SEG-दोष है का कहना है कि ऐसा लगता है:

संलग्न न्यूनतम testcase है डिफ़ॉल्ट-निर्माण डिफ़ॉल्ट तर्क के साथ निम्नलिखित समारोह:

void do_something(foo f = {}) 
{  std::cout << "default argument is at " << &f << std::endl; 
} 

foo के लिए निर्माता इसका पता आउटपुट करता है; 0x7ffff10bdb60

पर 0x7ffff10bdb7f निर्माण foo @ डिफ़ॉल्ट तर्क है यह दर्शाता है कि केवल 1 foo निर्माण किया गया था, और डिफ़ॉल्ट तर्क के रूप में एक ही पते पर नहीं: मैं एक भी रन से निम्नलिखित उत्पादन मिला है। यह एक बहुत अच्छा सप्ताह रहा है, लेकिन मैं कोड के साथ कुछ भी गलत नहीं देख सकता। वास्तविक कोड में जिस पर यह आधारित था, एक fg के विनाशक को चलाने के दौरान एक segfault उत्पन्न हो रहा था जिसे डिफ़ॉल्ट तर्क से स्थानांतरित किया गया था, क्योंकि अंतर्निहित स्मृति प्रतीत होता है।

हम live example से देख सकते हैं कि 4.9.0 इस समस्या का प्रदर्शन नहीं करता है।

हम देख सकते हैं इस defect report 994 से जानबूझकर कार्यक्षमता और बाद संकल्प N3217 था:

इस पत्र प्रस्तुत विस्तृत शब्दों वर्तमान सी ++ कार्यकारी ड्राफ्ट N3126 के सापेक्ष में परिवर्तन के लिए तर्क डिफ़ॉल्ट के लिए ब्रेस-initializers लागू करने के लिए कार्य, जैसा कि N3139 में प्रस्तावित किया गया है "Bjarne Stroustrup द्वारा एक अपूर्ण भाषा फ़ीचर", जिससे मूल मुद्दे 994 को भी संबोधित किया जाता है।

यह प्रस्ताव N3139: An Incomplete Language Feature में भी शामिल है।

ध्यान दें कि Visual Studio also has a bug with respect to brace-initializers as default arguments जो मुझे लगता है कि अभी भी अनसुलझा है।

+1

धन्यवाद! यही वही है जो मैं जानना चाहता था और नहीं कर सकता था नहीं मिला – v154c1