Fehler mit string und const char* ?
-
Hi,
ich habe ein programm geschrieben, dass aus einer url nur den Servernamen heraussuchen und einen neuen String damit bilden soll(vereinfacht, um den Fehler zu zeigen), und bin dabei auf ein Problem gestoßen.
das folgende programm ruft in der main funktion 6 mal die funktion getSite auf mit 6 verschiedenen varianten der url. In dieser Funktion wird ein String zusammengestellt, der am ende so: "Host: www.google.at" ausschauen sollte. Um das zu überprüfen sind auch ein paar testausgaben vorhanden (der string als string, der string als c-string, der string in char und int).
Ausserdem gibt es noch die Funktion getHost die den Servernamen aus der url extrahiert.Das problem liegt jetzt darin, dass die Funktion getHost in allen 6 Fällen das selbe zurückgibt ("www.google.at"), allerdings der string tmp immer unterschiedlich (entweder "Host: www.google.at", was richtig ist, oder "Host: Host: \0gle.at", was falsch ist) aussieht.
Und genau das kann logisch betrachtet nicht sein.
Erstens können bei gleicher Rückgabe von der Funktion getHost nicht 2 verschiedene Strings entstehen und
Zweitens kann keine 0 im String sein, da das ja der Abschluss eines Strings ist.
Ausserdem (Drittens) ändert sich das Ergebnis wenn man die vorletzte Zeile vonif((st = withoutHttp.find("/")) == string::npos) return withoutHttp.c_str();auf
if((st = withoutHttp.find("/")) == string::npos) return whitoutHttp.substr(0).c_str();ändert, obwohl das auch nicht sein dürfte.
Meine Frage daher: Warum? und/oder: Was hab ich falsch gemacht?
(Getestet unter WinXP mit Dev-C++ 4.9.9.2)
Das Programm:
#include <iostream> #include <conio.h> #include <string> using namespace std; void getSite(const std::string& url); static const char* getHost(const std::string& url); int main(int argc, char *argv[]) { getSite("http://www.google.at"); getSite("www.google.at/"); getSite("http://www.google.at/"); getSite("www.google.at"); getSite("http://www.google.at/test.html"); getSite("www.google.at/test.html"); getch(); return 0; } void getSite(const std::string& url) { cout << url << endl; string tmp = "Host: "; tmp = tmp + getHost(url); cout << "-" << tmp << "-" << endl; cout << "-" << tmp.c_str() << "-" << endl; for(int i=0;i<= tmp.length();i++) cout << tmp[i] << "(" << (int)tmp[i] << ") "; cout <<endl<<endl; } static const char* getHost(const std::string& url) { string::size_type st; string withoutHttp = url; if((st = url.find("://")) != string::npos) withoutHttp = url.substr(st+3); if((st = withoutHttp.find("/")) == string::npos) return withoutHttp.c_str(); return withoutHttp.substr(0,st).c_str(); }Ausgabe:
http://www.google.at
-Host: Host: gle.at-
-Host: Host: -
H(72) o(111) s(115) t(116) :(58) (32) H(72) o(111) s(115) t(116) :(58) (32) (0) g(103) l(108) e(101) .(46) a(97) t(116) (0)www.google.at/
-Host: Host: gle.at-
-Host: Host: -
H(72) o(111) s(115) t(116) :(58) (32) H(72) o(111) s(115) t(116) :(58) (32) (0) g(103) l(108) e(101) .(46) a(97) t(116) (0)http://www.google.at/
-Host: www.google.at-
-Host: www.google.at-
H(72) o(111) s(115) t(116) :(58) (32) w(119) w(119) w(119) .(46) g(103) o(111) o(111) g(103) l(108) e(101) .(46) a(97) t(116) (0)www.google.at
-Host: www.google.at-
-Host: www.google.at-
H(72) o(111) s(115) t(116) :(58) (32) w(119) w(119) w(119) .(46) g(103) o(111) o(111) g(103) l(108) e(101) .(46) a(97) t(116) (0)http://www.google.at/test.html
-Host: Host: gle.at-
-Host: Host: -
H(72) o(111) s(115) t(116) :(58) (32) H(72) o(111) s(115) t(116) :(58) (32) (0) g(103) l(108) e(101) .(46) a(97) t(116) (0)www.google.at/test.html
-Host: Host: gle.at-
-Host: Host: -
H(72) o(111) s(115) t(116) :(58) (32) H(72) o(111) s(115) t(116) :(58) (32) (0) g(103) l(108) e(101) .(46) a(97) t(116) (0)Ausgabe, wenn man die vorletzte Zeile ändert:
http://www.google.at
-Host: www.google.at-
-Host: www.google.at-
H(72) o(111) s(115) t(116) :(58) (32) w(119) w(119) w(119) .(46) g(103) o(111) o(111) g(103) l(108) e(101) .(46) a(97) t(116) (0)www.google.at/
-Host: Host: gle.at-
-Host: Host: -
H(72) o(111) s(115) t(116) :(58) (32) H(72) o(111) s(115) t(116) :(58) (32) (0) g(103) l(108) e(101) .(46) a(97) t(116) (0)http://www.google.at/
-Host: www.google.at-
-Host: www.google.at-
H(72) o(111) s(115) t(116) :(58) (32) w(119) w(119) w(119) .(46) g(103) o(111) o(111) g(103) l(108) e(101) .(46) a(97) t(116) (0)www.google.at
-Host: Host: gle.at-
-Host: Host: -
H(72) o(111) s(115) t(116) :(58) (32) H(72) o(111) s(115) t(116) :(58) (32) (0) g(103) l(108) e(101) .(46) a(97) t(116) (0)http://www.google.at/test.html
-Host: Host: gle.at-
-Host: Host: -
H(72) o(111) s(115) t(116) :(58) (32) H(72) o(111) s(115) t(116) :(58) (32) (0) g(103) l(108) e(101) .(46) a(97) t(116) (0)www.google.at/test.html
-Host: Host: gle.at-
-Host: Host: -
H(72) o(111) s(115) t(116) :(58) (32) H(72) o(111) s(115) t(116) :(58) (32) (0) g(103) l(108) e(101) .(46) a(97) t(116) (0)ps: Ich brauch keine Vorschläge wie mein Programm funktionieren würde (das kann ich selber auch), sondern Gründe warum es nicht funktioniert (weil ich den Fehler verstehen will).
-
Die Zeiger, die du zurückgibst, werden ungültig, wenn die zugehörigen string-Objekte am Ende der Funktion zerstört werden -> Undefiniertes Verhalten, alles kann passieren.
-
nactus schrieb:
Zweitens kann keine 0 im String sein, da das ja der Abschluss eines Strings ist.
das ist eine wissenslücke: std::strings haben keine '\0' am ende, können diese sogar beliebig oft mittendrin enthalten. die regel mit der abschließenden '\0' gilt nur für sogenannten nullterminierte bytestrings (NTBS), vulgo c-strings. diese wissenlücke ist allerdings nicht so schlimm. das erklärt die verschiedenen ausgaben.
bei mir funktioniert dein programm einwandfrei. was nicht sein muss, denn du hast einen fatalen fehler drin:
#include <iostream> #include <conio.h> //ach... lieber nicht. #include <string> using namespace std; void getSite(const std::string& url); static const char* getHost(const std::string& url); int main(int argc, char *argv[]) { getSite("http://www.google.at"); getSite("www.google.at/"); getSite("http://www.google.at/"); getSite("www.google.at"); getSite("http://www.google.at/test.html"); getSite("www.google.at/test.html"); getch(); //ach... lieber nicht. return 0; //überflüssig } void getSite(const std::string& url) { //die funktion macht nun nichts aufregendes und ist in ordnung so. nur... cout << url << endl; string tmp = "Host: "; tmp = tmp + getHost(url); //... an dieser stelle passiert was, das erst weiter unten ersichtlich wird cout << "-" << tmp << "-" << endl; cout << "-" << tmp.c_str() << "-" << endl; for(int i=0;i<= tmp.length();i++) cout << tmp[i] << "(" << (int)tmp[i] << ") "; cout <<endl<<endl; } static const char* getHost(const std::string& url) //warum static? //ich vermute fast, du glaubst, das static bezieht sich auf const char*, aber das tut es nicht. { string::size_type st; string withoutHttp = url; //das führt zu dem fehler if((st = url.find("://")) != string::npos) withoutHttp = url.substr(st+3); if((st = withoutHttp.find("/")) == string::npos) return withoutHttp.c_str(); return withoutHttp.substr(0,st).c_str(); //das ist der fehler //du gibst einen zeiger auf ein NTBS zurück, der von einem objekt (string withoutHttp //zur verfügung gestellt wird, dass die funktion nicht überlebt. //withoutHttp.~string zerstört deinen c_str. und der zeiger, denn du zurückbekommst, //zeigt nur selten auf die richtige information. }[/quote]
lösung: nicht const char* zurückgeben in getHost.
-
das heißt, wenn ich den rückgabetyp von getHost in string ändere und dann withoutHttp zurückgebe, wird es nicht zerstört/freigegeben, sondern überlebt die funktion?
-
withoutHttp ist eine lokale variable und wird auf alle fälle zerstört. wenn du den rückgabetyp in std::string änderst, wird allerdings dann an der stelle, wo die funktion aufgerufen wird, eine kopie erstellt.
und das static auch weglassen.
-
Der String wird dann anständig kopiert. So wie du das gemacht hast wird nur eine Adresse kopiert und die zurückgegeben. Aber sobald die Funktion verlassen wurde ist diese Adresse ungültig.
Wenn du std::string zurück geben würdest, würde eine Kopie erzeugt und zurückgegeben. Und damit auch der richtige Inhalt angezeigt.
BR
Vinzenz
-
danke, damit ist alles geklärt