2010-11-08 28 views
54

W C++ jaka jest różnica (jeśli jest) między używaniem char i char [1].Różnica między char i char [1]

przykładów:

struct SomeStruct 
{ 
    char x; 
    char y[1]; 
}; 

Czy te same powody naśladowania dla unsigned char?

+18

Ilość błędnych odpowiedzi na to pytanie jest zdumiewająca. +1 za to, że pytasz o coś, co ludzie często nie rozumieją. –

+2

Przeczytałem to pytanie i prawie w tym momencie setki klawiatur wściekle pisząc, by jako pierwsza odpowiedziały. – Anthony

+2

@Billy, @Duracell: Niestety większość programistów C i C++ tak naprawdę nie rozumie tablic. Z jednej strony jest to nieco zaskakujące, biorąc pod uwagę ich fundamentalne znaczenie; z drugiej strony rozpad tablic i sposób, w jaki działa indeksowanie, ułatwia początkującym myślenie, że tablice są tym samym co wskaźniki. –

Odpowiedz

39

Główną różnicą jest tylko składnia, której używasz, aby uzyskać dostęp do jednego znaku.

Przez "dostęp" mam na myśli, działać na nim za pomocą różnych operatorów w języku, z których większość lub wszystkie robią różne rzeczy po zastosowaniu do char w porównaniu z tablicą char. To sprawia, że ​​brzmi to tak, jakby x i y były prawie całkowicie inne. Jeśli tak, to oba składają się z "jednego znaku", ale ten znak został przedstawiony w zupełnie inny sposób.

Może to spowodować inne różnice, na przykład może wyrównać i podparć konstrukcję w różny sposób, zgodnie z tym, którego używasz. Ale wątpię, że tak będzie.

Przykładem operatora różnic jest to, że znak jest przypisywany i tablica nie jest:

SomeStruct a; 
a.x = 'a'; 
a.y[0] = 'a'; 
SomeStruct b; 
b.x = a.x; // OK 
b.y = a.y; // not OK 
b.y[0] = a.y[0]; // OK 

Jednak fakt, że nie jest przypisywane y nie zatrzymuje SomeStruct być przypisane:

b = a; // OK 

Wszystko to niezależnie od typu, char lub nie. Obiekt typu i tablica tego typu o rozmiarze 1 są prawie takie same, jeśli chodzi o to, co jest w pamięci.

Na marginesie, istnieje kontekst, w którym robi wielką różnicę, którą "używasz" z char i char[1], a która czasami pomaga mylić ludzi z myślą, że tablice są naprawdę wskazówkami. Nie był to przykład, ale jako parametr funkcji:

void foo(char c);  // a function which takes a char as a parameter 
void bar(char c[1]); // a function which takes a char* as a parameter 
void baz(char c[12]); // also a function which takes a char* as a parameter 

Liczby podane w deklaracjach bar i baz są całkowicie ignorowane przez język C++. Najwidoczniej ktoś w pewnym momencie uznał, że programistom będzie przydatna jako forma dokumentacji, wskazując, że funkcja baz spodziewa się, że argument wskaźnika wskaże pierwszy element tablicy złożonej z 12 znaków.

W barach i bazach, c nigdy nie ma typu tablicy - wygląda na typ tablicy, ale nie jest, jest to po prostu wyjątkowa składnia o tym samym znaczeniu co char *c. Dlatego umieszczam znaki cudzysłowu na "użytek" - w rzeczywistości tak naprawdę nie używasz char[1], tylko tak to wygląda.

+9

+1, Jestem zmęczony wszystkimi odpowiedziami mówiącymi, że 'y' jest wskaźnikiem. – casablanca

+4

Ładna i przejrzysta odpowiedź. Inną rzeczą, która będzie inna, jest inicjalizacja z konstruktorami (jeśli miałby ją dodać), co niestety nie działa w obecnym C++ z tablicami (innymi niż wartościowanie-inicjowanie ich). –

+3

Główna różnica to tak naprawdę * typ * przypisany przez system typów. 'x' jest typu' char', a 'y' jest typu' char [1] '. Wiem, że to brzmi jak stwierdzenie oczywistości, ale to naprawdę główna różnica. Nie możesz przekazać 'y' do funkcji, która oczekuje na przykład' char' jako parametru. Ale ostatecznie zarówno x, jak i y zwrócą tę samą wartość po przekazaniu do operatora 'sizeof' i prawdopodobnie będą reprezentowane w ten sam sposób w pamięci. Również "y" może ulec degradacji do wskaźnika, co oznacza, że ​​możesz przekazać go do czegoś takiego jak "strcpy", ale nie możesz tego zrobić z 'x' –

11

Jeśli faktycznie widziałeś konstrukt char y[1] jako ostatniego członka struct w kodzie produkcyjnym, to jest całkiem prawdopodobne, że napotkasz wystąpienie struct hack.

Ta krótka tablica jest stand-inem dla prawdziwej, ale zmiennej długości tablicy (pamiętaj, że przed c99 nie było czegoś takiego w standardzie c). Programista zawsze przypisywałby takie struktury do sterty, upewniając się, że alokacja była wystarczająco duża dla faktycznego rozmiaru macierzy, której chciał użyć.

+6

A jeśli zobaczysz strukturę hack w kodzie C++, martw się ... –

+0

Steve: O ile ten kod C++ nie wymaga rozmowy z kodem C. Na przykład kod C++ zużywający Win32APIs –

+0

@Billy: oczywiście, jeśli hack pojawia się w strukturze zdefiniowanej przez niektóre API C, to jest w porządku. Domyślam się, że hack jest nadal "w C++", w tym sensie, że nagłówek staje się C++, gdy tylko "# include" to w programie C++, więc w porządku, to wyjątek od mojej reguły. –

3

Oprócz notacyjnych różnic w użyciu podkreślanych przez Steve'a, char [1] można przekazać np. template <int N> void f(char(&a)[N]), gdzie char x = '\0'; f(&x); nie pasuje. Rzetelne przechwytywanie wielkości argumentów tablicowych jest bardzo wygodne i uspokajające.

Może to również sugerować coś innego: albo faktyczna długość może być dłuższa (jak wyjaśniono przez dmckee), albo że treść jest logicznie ciągiem ASCIIZ (który w tym przypadku jest pusty) lub tablicą postacie (które mają jeden element). Jeśli struktura była jedną z kilku powiązanych struktur (np. Wektor matematyczny, w którym rozmiar tablicy był argumentem szablonu lub kodowanie układu pamięci potrzebnej do operacji we/wy), to jest całkowicie możliwe, że pewne podobieństwo z innymi polami gdzie tablice mogą być większe, sugerowałoby preferencję dla tablicy jednoliterowej, umożliwiając uproszczenie kodu pomocniczego i/lub bardziej uniwersalne zastosowanie.

Powiązane problemy