2015-03-03 2 views
33

Problem

bekam ich einen Fehlerbericht von Benutzern eine segfault in der Bibliothek Berichterstattung ich entwickeln.std :: map Argument mit leeren Klammer-Initialisierer für Standardargument segfaults in GCC

Das minimale Beispiel des fehlerhaften Code ist:

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

Wenn mit GCC kompiliert (I getestet 4.8.2 und 4.7.3) es richtig gedruckt 0 als Größe des Behälters, aber innerhalb der Schleife segfaults (was überhaupt nicht ausgeführt werden sollte).

Umgehungen

Allerdings kann ich beheben das Problem durch die Erklärung auf sich ändernde:

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

Kopieren der map Werke auch:

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

den Parameter auf const std::map<...>& Ändern funktioniert auch.

GCC 4.9.1 funktioniert gut.

Clang kompiliert und führt den Code auch gut aus. (Auch wenn die gleichen libstdC++ verwenden wie gcc andernfalls 4.8.2)

Arbeiten Beispiel: http://coliru.stacked-crooked.com/a/eb64a7053f542efd

Frage

Die Karte ist auf jeden Fall nicht in gültigem Zustand innerhalb der Funktion (Details unten). Es sieht aus wie ein GCC (oder libstdC++) Fehler, aber ich möchte sicher sein, dass ich hier keinen dummen Fehler mache. Es ist schwer zu glauben, dass ein solcher Fehler für mindestens 2 Hauptversionen in gcc bleiben würde.

Also meine Frage ist: Ist der Weg Standard std::map Parameter falsch (und Fehler in meinem Code) oder ist es ein Bug in stdlibc++ (oder gcc) der Initialisierung?

Ich suche keine Problemumgehungen (wie ich weiß was zu tun ist, um Code zu arbeiten) Bei der Integration in die Anwendung wird der fehlerhafte Code auf einigen Computern (auch wenn mit gcc 4.8.2 kompiliert) auf manche nicht.

Einzelheiten

ich kompilieren mit:

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

Backtrace von GDB:

#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/c.cav: 9 ist die Linie mit std::cout << ...

ASAN Berichte:

AddressSanitizer: SEGV on unknown address 0xffffffffffffffe8 

Dies scheint nullptr - 8

valgrind zeigt:

==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 

im internen Zustand der Karte Suchen Sie zeigt, dass die Code hat wirklich zum scheitern verurteilt:

std::map::begin() in libstdC++ liefert Wert von

this->_M_impl._M_header._M_parent 

aus es interne Darstellung, std::map::end() kehrt:

&this->_M_impl._M_header 

gdb zeigt:

(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 

So Wert von begin() und end() nicht gleich sind (begin() wie für leere std::map von Standard vorgeschrieben ist nullptr).

+0

FWIW, wenn ich 'std :: cout << s.first << "->" << s.second << "\ n"; 'durch' std :: cout << "kam hier \ n"; ', Das Programm druckt die Zeile einmal und hängt dann. –

+4

"Es ist schwer zu glauben, dass ein solcher Fehler in gcc für mindestens 2 Hauptversionen bleiben würde." - Sie müssen neu hier sein –

Antwort

22

Sieht aus wie dies bug was fixed in 4.8.3/4.9.0, der Bug-Report, die ein ähnliches Beispiel hat und auch seg-Fehler sagen:

Der beigefügte minimale Testfall hat die folgende Funktion mit Standard konstruierten Standardargument:

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

Der Konstruktor für foo gibt seine Adresse; Ich habe die folgende Ausgabe von einem einzigen Durchlauf: konstruiert foo @ 0x7ffff10bdb7f Standardargument ist bei 0x7ffff10bdb60

Es zeigt, dass nur 1 foo gebaut wurde, und nicht an der gleichen Adresse wie die des Standardargument. Es ist schon eine loooong Woche, aber ich kann nichts falsch mit dem Code sehen. In der realen Code, auf dem das war basiert, ein segfault auftreten wurde, wenn die destructor eines foo ausgeführt wird, die aus dem Standardargument bewegen-gebaut wurde, weil der zugrunde liegenden Speicher scheinbar uninitialised wurde.

Wir können von einer live example sehen, dass 4.9.0 dieses Problem nicht zeigt.

Wir können sehen, das war vorsätzliche Funktionalität von defect report 994 und die anschließende Auflösung N3217:

Dieser Beitrag stellt detaillierte Formulierung C++ Working Draft N3126 der aktuellen relativen Änderungen Argumente Klammer-Initialisierer für Standard zu implementieren für Funktionen, wie in N3139 „Ein unvollständigen Sprache -Feature“ von Bjarne Stroustrup vorgeschlagen, damit auch Kernthema 994 Adressierung.

Dies wird auch im Vorschlag N3139: An Incomplete Language Feature behandelt.

Interessant zu notieren, dass Visual Studio also has a bug with respect to brace-initializers as default arguments die ich denke, ist immer noch ungelöst.

+1

Danke! Das ist genau das, was ich wissen wollte und konnte nicht finden. – v154c1

Verwandte Themen