2009-09-14 9 views
6

Czytałem gdzieś, że jeśli chcesz, aby funkcja C/C++ zwróciła tablicę znaków (w przeciwieństwie do std :: string), musisz zwrócić const char * zamiast char *. Może to spowodować awarię programu.Może/Dlaczego używanie znaku * zamiast znaku stałego * w typie zwrotnym powoduje awarie?

Czy ktoś mógłby wyjaśnić, czy to prawda, czy nie? Jeśli to prawda, dlaczego zwraca znak * z tak niebezpiecznej funkcji? Dziękuję Ci.

+6

main() powinien zawsze zwracać int! http://users.aber.ac.uk/auj/voidmain.shtml – nmuntz

Odpowiedz

14

Jeśli masz funkcję zwracającą „literały ciągów znaków” to musi powrócić const char *. Nie muszą one być przydzielane do sterty przez malloc, ponieważ są one kompilowane do sekcji samego pliku wykonywalnego tylko do odczytu.

Przykład:

const char* errstr(int err) 
{ 
    switch(err) { 
     case 1: return "error 1"; 
     case 2: return "error 2"; 
     case 3: return "error 3"; 
     case 255: return "error 255 to make this sparse so people don't ask me why I didn't use an array of const char*"; 
     default: return "unknown error"; 
    } 
} 
+0

Należy również zauważyć, że są one kompletne, a inne zmienne mogą wskazywać identyczną lokalizację. Nie powinno to jednak stanowić problemu, jeśli const jest szanowana. –

2

Jeśli znak * zostanie przydzielony na stosie, zwrócisz zwisający wskaźnik. W przeciwnym razie, dopóki dopasuje prototyp funkcji i deklaracja pasuje do wartości zwracanej, powinieneś być w porządku.

11

To, co zostało powiedziane, to , a nie prawda.

Zwrócenie numeru const char * może poprawić semantykę funkcji (tj. Nie zadzieraj z tym, co ci daję), ale zwrócenie numeru char * jest całkowicie poprawne.

Jednak w obu przypadkach musi upewnij się, że zwróci char * lub const char * że została przydzielona na stercie w my_function (tj przydzielona za pomocą malloc lub new), w przeciwnym wypadku, gdy my_function powroty, pamięć dla [const] char * zostanie zwolniony, a uzyskasz dostęp do nieprawidłowego wskaźnika.

I wreszcie ty musi pamiętać free lub delete się [const] char * który został zwrócony do ciebie raz skończysz z nim, albo będzie wyciek pamięci. Czy C/C++ nie jest tak świetnym językiem?

Więc w C, to masz

const char *my_function() { 
    const char *my_str = (const char *)malloc(MY_STR_LEN + 1); // +1 for null terminator. 
    /* ... */ 
    return my_str; 
} 

int main() { 
    const char *my_str = my_function(); 
    /* ... */ 
    free(my_str); 
    /* ... */ 
    return 0; 
} 
+1

Tego typu rzeczy, dlatego lubię std :: string C++ (nawet jeśli jest to złe zadanie hackowe definicji klasy ciągów) i inteligentne wskaźniki . Wystarczy przejść przez tyle bólu w C. –

+0

@DavidThornley odpowiedniej dokumentacji zmniejsza ten ból do niemal zera. – Qix

1

prostu zmieniając kod zwrotny nie będzie powodować awarię. Jeśli jednak zwracany ciąg jest statyczny (na przykład return "AString"), powinieneś zwrócić const char *, aby upewnić się, że kompilator wykryje próbę modyfikacji tej pamięci, co prawdopodobnie spowoduje awarię. Z pewnością możesz używać rzutów i podobnych, aby obejść kontrolę kompilatora, ale w takim przypadku musisz wykonać pracę, aby spowodować awarię.

+0

Dziękuję. Czy mówisz, że jeśli zwrócisz literalny ciąg znaków (np. Zwrócisz "To jest test";), typem zwracanym musi być const char *? Jakie jest niebezpieczeństwo, jeśli typem zwracanym jest char *? – Andy

+0

Jeśli ciąg nie zostanie zmodyfikowany, nie ma niebezpieczeństwa, ale jeśli osoba dzwoniąca próbuje zmodyfikować ciąg, aplikacja ulegnie awarii. Jeśli typem powrotu jest 'const char *', to kompilator nie pozwoli ci go zmodyfikować. –

+0

Dziękuję. Prawdopodobnie o tym mówią. Kolejny aspekt C, którego nie znałem (i trzy okrzyki na std :: string są świetne!). – Andy

4

Zwykle nie jest to problemem, ale są rzeczy, które należy wziąć pod uwagę. Zazwyczaj jest to kwestia stałej stałości, co oznacza śledzenie tego, co można zmienić, a czego nie.

Jeśli zwracasz podwójny ciąg znaków, jest to const char *, a traktowanie go jak cokolwiek innego jest zaproszeniem na kłopoty. Zmiana takiego ciągu jest niezdefiniowanym zachowaniem, ale zazwyczaj powoduje awarię programu lub zmianę tego łańcucha, niezależnie od tego, do czego się odnosi.

Jeśli zwrócisz tablicę znaków na stosie (tj. Zmienną lokalną wywołanej funkcji), zniknie, a wskaźnik nie wskaże w ogóle nic konkretnego, prawdopodobnie ze złymi wynikami w pewnym momencie.

Jeśli wywoływana funkcja zwraca coś, co już jest const char *, to zmiana na char * wymaga rzutowania. Co więcej, jeśli zamierzasz to zmienić, musisz mieć pewność, że można to zmienić. Zazwyczaj lepiej jest zachować go jako const char *.

Nie ma problemu z natychmiastowym powrocie pamięci przydzielonej z malloc() lub new, ale masz problem własności: jaką funkcję powinien free()/delete to, kiedy i co robisz uwagę możliwe kopii? Tu właśnie świecą inteligentne wskaźniki C++.