Ciągi C++: [] VS. *

Zastanawialiśmy się, jaka jest różnica między zadeklarowaniem zmiennej z [] lub * ? Tak to widzę:

char *str = new char[100];
char str2[] = "Hi world!";

.. powinno być główną różnicą, chociaż nie jestem pewien, czy możesz zrobić coś takiego jak

char *str = "Hi all";

.. ponieważ wskaźnik powinien odnosić się do statycznego członka, który nie wiem, czy może?

W każdym razie, to co mnie tak naprawdę wkurza, to poznanie różnicy między:

void upperCaseString(char *_str) {};
void upperCaseString(char _str[]) {};
Więc, byłabym wdzięczna, gdyby ktoś mógł powiedzieć mi różnicę? Mam przeczucie. że oba mogą być zestawione tak samo, z wyjątkiem niektórych szczególnych przypadków?

Ty

Author: Johannes Schaub - litb, 2008-11-21

6 answers

Przyjrzyjmy się temu (poniżej, zauważ, że char const i const char są takie same w C++):

Literały łańcuchowe i znak *

"hello" jest tablicą 6 znaków const: char const[6]. Jak każda tablica, może ona przekształcić w sposób niejawny wskaźnik do pierwszego elementu: char const * s = "hello"; dla kompatybilności z kodem C, C++ pozwala na jedną inną konwersję, która w przeciwnym razie byłaby źle uformowana: char * s = "hello"; usuwa const!. Jest to wyjątek, aby umożliwić kompilację kodu C-owskiego, ale jest on przestarzały, aby zrobić char * wskaż na literalny łańcuch znaków. Więc co mamy dla char * s = "foo";?

"foo" -> array-to-pointer -> char const* -> qualification-conversion -> char *. Literał Łańcuchowy jest tylko do odczytu i nie zostanie przydzielony na stosie. Możesz swobodnie zrobić do nich wskaźnik i zwrócić go z funkcji BEZ:).

Inicjalizacja tablicy za pomocą literału Łańcuchowego

Czym jest char s[] = "hello";? To jest Cała inna rzecz. Które utworzy tablicę znaków i wypełni it with the String "hello". Literalność nie jest wskazywana. Zamiast tego jest kopiowany do tablicy znaków. A tablica jest tworzona na stosie . Nie można prawomocnie zwrócić wskaźnika do niego z funkcji.

Typy parametrów tablicy.

Jak sprawić, by Twoja funkcja zaakceptowała tablicę jako parametr? Po prostu deklarujesz swój parametr jako tablicę:

void accept_array(char foo[]); 
Ale pomijasz rozmiar. Właściwie każdy rozmiar by to zrobił, bo jest po prostu ignorowany: Standard mówi że parametry zadeklarowane w ten sposób zostaną przekształcone na takie same jak
void accept_array(char * foo);

Excursion: Tablice Wielowymiarowe

Zastąp char dowolnym typem, łącznie z samymi tablicami:

void accept_array(char foo[][10]);

Przyjmuje tablicę dwuwymiarową, której ostatni wymiar ma rozmiar 10. pierwszy element wielowymiarowej tablicy jest jej pierwszą podzbiorem następnego wymiaru! Przemienimy to. Będzie to ponownie wskaźnik do pierwszego elementu. Tak więc, w rzeczywistości zaakceptuje wskaźnik do tablicy 10 znaków: (Usuń [] w nagłówku, a następnie po prostu zrób wskaźnik do typu, który widzisz w głowie):

void accept_array(char (*foo)[10]);

Ponieważ tablice domyślnie konwertują się na wskaźnik do pierwszego elementu, możesz po prostu przekazać w nim tablicę dwuwymiarową (której ostatni rozmiar to 10) i to zadziała. W rzeczywistości tak jest dla dowolnej tablicy n-wymiarowej, w tym przypadku specjalnym n = 1;

Podsumowanie

void upperCaseString(char *_str) {}; 

I

void upperCaseString(char _str[]) {};

Są tak samo, jak pierwszy jest tylko wskaźnikiem do znaku. Ale zauważ, że jeśli chcesz przekazać do tego ciąg znaków (powiedzmy, że nie zmienia on argumentu), powinieneś zmienić parametr na char const* _str, aby nie robić przestarzałych rzeczy.

 41
Author: Johannes Schaub - litb,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 13:30:26

Trzy różne deklaracje pozwalają wskazywać na różne segmenty pamięci:

char* str = new char[100];

Pozwala str wskazać stertę.

char str2[] = "Hi world!";

Umieszcza łańcuch na stosie.

char* str3 = "Hi world!";

Wskazuje na segment danych.

Dwie deklaracje

void upperCaseString(char *_str) {};
void upperCaseString(char _str[]) {};

Są równe, kompilator skarży się, że funkcja ma już ciało, gdy próbujesz zadeklarować je w tym samym zakresie.

 12
Author: Timbo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 10:02:51

Zostawiłam dwa negatywne komentarze. To nie jest naprawdę przydatne, usunąłem je.

  • poniższy kod inicjalizuje wskaźnik char, wskazujący początek dynamicznie przydzielanej części pamięci (w stercie.)

char *str = new char[100];

Blok ten można uwolnić za pomocą delete [].

  • poniższy kod tworzy tablicę znaków w stosie, inicjalizowaną do wartości określonej literalnym łańcuchem znaków.

char [] str2 = "Hi world!";

Ta tablica może być modyfikowana bez problemów, co jest miłe. Więc


str2[0] = 'N';
cout << str2;

Powinien wydrukować Ni world! na standardowe wyjście, sprawiając, że niektórzy rycerze czują się bardzo niekomfortowo.

  • poniższy kod tworzy wskaźnik znakowy w stosie, wskazujący na literał Łańcuchowy ... Wskaźnik może być ponownie przypisany bez problemów, ale punktowany blok nie może być modyfikowany (jest to nieokreślone zachowanie; działa na przykład pod Linuksem.)

char *str = "Hi all";
str[0] = 'N'; // ERROR!
  • następujące dwie deklaracje

void upperCaseString(char *_str) {};
void upperCaseString(char [] _str) {};

Zobacz to samo dla mnie , a w Twoim przypadku (chcesz wstawić duży łańcuch) to naprawdę nie ma znaczenia.

Nasuwa się jednak pytanie: dlaczego używasz char * do wyrażania ciągów w C++?

 2
Author: Leonardo Herrera,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 14:56:29

Jako uzupełnienie udzielonych już odpowiedzi, powinieneś przeczytać C FAQ dotyczące tablic vs. pointers. Tak, to C FAQ, a NIE C++ FAQ, ale nie ma znaczącej różnicy między tymi dwoma językami w tej dziedzinie.

Na marginesie, unikaj nazywania zmiennych znakiem podkreślenia. Jest to Zarezerwowane dla symboli zdefiniowanych przez kompilator i bibliotekę standardową.

 0
Author: Tyler McHenry,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 13:30:28

Proszę również spojrzeć na http://c-faq.com/aryptr/aryptr2.html C-FAQ może okazać się ciekawą lekturą samą w sobie.

 0
Author: Sandeep Datta,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 16:18:25

Pierwsza opcja dynamicznie przydziela 100 bajtów.

Druga opcja przydziela statycznie 10 bajtów (9 Dla ciągu znaków + znak nul).

Twój trzeci przykład nie powinien działać-próbujesz statycznie wypełnić element dynamiczny.

Jeśli chodzi o pytanie upperCaseString(), po przydzieleniu i zdefiniowaniu ciągu C, można przez nie przechodzić przez indeksowanie tablicy lub notację wskaźnika, ponieważ tablica jest tak naprawdę wygodnym sposobem na zawinięcie arytmetyki wskaźnika w C.


(to jest prosta odpowiedź-spodziewam się, że ktoś inny będzie miał autorytatywną, skomplikowaną odpowiedź ze spec :))

 -1
Author: warren,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2008-11-21 10:01:41