2009-09-16 32 views
44

Wiem, że to całkiem idiomatyczny lub dobry styl przynajmniej w C, aby zadeklarować stałe liczbowe jako enum s zamiast s ich.Jak zadeklarować stałe ciągów w C?

/* bad style */ 
#define MAXLINE 1024 

/* good/better style */ 
enum { 
    MAX_LINE = 1024 
}; 

Czy istnieje odpowiednik reguły dla definicji stałych łańcuchowych?

/* is this good style? */ 
#define HELLO "Hello World" 

/* or is this better? */ 
const char *HELLO2 = "Howdy"; 

Co wolisz? Jeśli to możliwe, pokaż kilka wad obu metod.

Odpowiedz

70

Jest jeszcze jedna (co najmniej) droga do Rzymu:

static const char HELLO3[] = "Howdy"; 

(static - opcjonalnie - ma zapobiec jego konflikcie z innymi plikami). Wolałbym ten ponad const char*, ponieważ wtedy będziesz mógł używać sizeof(HELLO3) i dlatego nie musisz odkładać do czasu wykonania, co możesz zrobić w czasie kompilacji.

Zdefiniowane ma jednak zaletę łączenia kompilacji, chociaż (myśl HELLO ", World!") i można również sizeof(HELLO).

Ale wtedy możesz również wolisz const char* i używać go w wielu plikach, co zaoszczędziłoby ci trochę pamięci.

Krótko mówiąc - to zależy.

+0

Dziękuję za wyjaśnienie. Powodem użycia 'wyliczeń zamiast stałych makr lub' const int' dla stałych wartości całkowitych jest fakt, że jest to jedyny bezpieczny sposób deklarowania stałej liczby całkowitej, która może być przenośna jako granica tablicowa. Cóż ... Łączenie ciągów w czasie kompilacji prawie mnie sprzedało przy użyciu '# define's dla ciągów znaków, ale pozostanę przy" const char * "dla bezpieczeństwa typu, używając" statycznego ", jeśli jest to stosowne. W skrócie użyję 'wyliczenia' dla stałych całkowitych i zmiennych' const' dla całej reszty. –

+1

Tatt, sugerowałbym, żebyś nie trzymał się niczego. * Nie wszystkie stałe są równe *. Jeśli nie chcesz się łączyć, nie potrzebujesz # definicji, jeśli nie będziesz potrzebował również rozmiaru, nie obchodzi cię to wcale. To, czego kompletnie nie widzę, to to, jak "const char *" jest lepsze niż "const char []' * gdzie statyczny ma zastosowanie *. Ale potem znowu, jeśli nie potrzebujesz rozmiaru, to też nie jest gorszy ;-) –

9

Jedną z zalet (choć bardzo niewielka) definiowania ciąg stałe jest, że można łączyć je:

 
#define HELLO "hello" 
#define WORLD "world" 

puts(HELLO WORLD); 

Nie wiesz, że to naprawdę zaletą, ale jest techniką, która nie może być używany z const char *.

3

Główną wadą metody #define jest to, że ciąg jest duplikowany za każdym razem, gdy jest używany, więc możesz skończyć z wieloma kopiami w pliku wykonywalnym, zwiększając go.

+5

Wierzę, że kompilator zoptymalizuje je co najmniej tak długo, jak są używane w tym samym pliku. Tak więc nie jest to "za każdym razem, gdy jest używany", ale tak, definiowanie stałej globalnej oszczędza czasem. –

+2

to prawda, chyba że kompilator lub linker zwija kopie w jeden. Ta funkcja jest czasami nazywana "łączeniem ciągów" – Rom

-2

Za pierwszą. Wyliczenia są domyślnie liczbami całkowitymi, co oznacza, że ​​można przekazać tę wartość tylko do czegoś, co akceptuje tylko liczby całkowite.

Drugi lubię lepiej: const char * version zamiast #define. Powodem jest to, że tablice w c są wskaźnikami, co oznacza, że ​​za każdym razem, gdy chcesz użyć wersji #define, musisz utworzyć instancję tego łańcucha tylko po to, aby go użyć.

+1

tablice nie są wskaźnikami i nie ma koncepcji "tworzenia stringów", jeśli chodzi o stałą łańcuchową. A jeśli tablice były wskaźnikami, które nie znaczyłyby, że ciąg musi być utworzony, nawet było coś takiego ;-) –

+0

mówił o stałych numerycznych. Tablice są zwykle przekazywane jako referencje Przynajmniej nie potrafię tego inaczej. So. Czym jest domyślny zwrot w tym przypadku? { const char * str1 = HELLO; const char * str1 = HELLO; return (str1 == str2); } –

+0

Wartość zwracana jest zależna od implementacji, ale (zakładając, że chodziło o str2 w drugim przypisaniu) większość implementacji zwróci true - spróbuj, oba wskaźniki odwołają się do tego samego ciągu w stałym segmencie danych. Ale ten kod nie ma nic wspólnego z tablicami, jest zwykłym wskaźnikiem. Jeśli chodzi o przekazywanie tablic jako wskaźnika, nie oznacza to, że tablice * są * wskaźnikami. 'int *' i 'int [2]' są różnymi typami. I nie tylko teoretycznie - nie możesz sensownie używać wskaźnika 'sizeof()', nie możesz zwiększać tablicy, itp. –

7

Jeśli chcesz „const ciąg” jak Twoje pytanie mówi, ja naprawdę go do wersji, którą podanej w pytaniu:

/* first version */ 
const char *HELLO2 = "Howdy"; 

szczególnie, chciałbym uniknąć:

/* second version */ 
const char HELLO2[] = "Howdy"; 

Powód: Problem z drugą wersją polega na tym, że kompilator tworzy kopię całego ciągu znaków "Howdy", PLUS, który jest modyfikowalny (tak naprawdę nie jest const).

Z drugiej strony, pierwsza wersja jest ciągiem stałym dostępnym przez wskaźnik const HELLO2 i nie ma sposobu, aby ktokolwiek mógł go zmodyfikować.

+5

Myślę, że 'const char * const HELLO2' jest lepszy .. – Shawn