Czy jest domyślnie podpisany lub niepodpisany?

158

W książce „Complete Reference of C” jest wspomniane, że chardomyślnie jest bez znaku.

Ale próbuję to zweryfikować za pomocą GCC, a także Visual Studio. Domyślnie przyjmuje to jako podpisane .

Który jest prawidłowy?

C Uczeń
źródło
5
Jedyną książką referencyjną C, której ufam, jest „C: A Reference Manual” Harbison & Steele ( careferencemanual.com ). Oczywiście standard jest ostatnim słowem, ale nie jest zbyt czytelny i podaje tylko najmniejsze informacje o zastosowaniach przedstandardowych i powszechnych (tj. POSIX), które są poza standardem. Harbison & Steele jest całkiem czytelny, szczegółowy i prawdopodobnie bardziej poprawny niż większość odniesień. Jednak nie jest to również samouczek, więc jeśli jesteś na początkowym etapie nauki, prawdopodobnie nie jest to świetna rzecz.
Michael Burr
15
Myślę, że książka, którą czytasz, to C: The Complete Reference autorstwa Herberta Schildta. Z recenzji tej książki ( accu.informika.ru/accu/bookreviews/public/reviews/c/c002173.htm ): Nie zamierzam polecać tej książki (zbyt wielu z was przykłada zbyt dużą wagę do moich opinii), ale Nie sądzę, by zasługiwała na taką samą potępienie, jaka została słusznie rzucona na niektóre jego inne prace. Jak mówi Michael, dużo lepszym odniesieniem jest Harbison & Steele .
Alok Singhal
Moje dwa centy tutaj: Ponieważ charmoże być bez znaku, z reguły użyj intdo odczytania wartości, używając getchar(), która może zwrócić EOF. EOFjest zwykle definiowana jako -1lub inna wartość ujemna, której przechowywanie w pliku unsignednie jest tym, czego chcesz. Oto deklaracja: extern int getchar();BTW, to zalecenie pochodzi również z książki „C: A Reference Manual”.
Maxim Chetrusca
6
Jedynym odniesieniem C, któremu ufam, jest ISO / IEC 9899: 2011 :-)
Jeff
3
@MaxChetrusca Dobra rada, ale kiepskie uzasadnienie: nawet w charprzypadku podpisanego przypadku musiałbyś użyć intdo przechowywania wartości zwracanej.
Antti Haapala

Odpowiedzi:

204

Książka jest zła. Standard nie określa, czy zwykły charjest podpisany, czy nie.

W rzeczywistości, średnia definiuje trzy różne typy: char, signed chari unsigned char. Jeśli #include <limits.h>przyjrzysz się CHAR_MIN, a następnie przyjrzysz się , możesz dowiedzieć się, czy zwykły charjest równy signedlub unsigned(jeśli CHAR_MINjest mniejszy niż 0 lub równy 0), ale nawet wtedy te trzy typy są różne, jeśli chodzi o standard.

Zwróć uwagę, że charjest to wyjątkowe w ten sposób. Jeśli deklarujesz zmienną, ponieważ intjest to 100% równoważne zadeklarowaniu jej jako signed int. Dotyczy to zawsze wszystkich kompilatorów i architektur.

Alok Singhal
źródło
1
@Alok: to samo nie jest prawdą dla niektórych innych typów danych, na przykład intoznacza signed intzawsze, prawda? Poza chartym, w jakich innych typach danych występuje ten sam błąd C?
Lazer,
8
@eSKay: tak, charto jedyny typ, który może być podpisany lub niepodpisany. intjest odpowiednikiem signed intna przykład.
Alok Singhal
28
Jest tego histeryczny, hm, historyczny powód - na początku życia C „standard” został przerzucony co najmniej dwa razy, a niektóre popularne wczesne kompilatory skończyły w ten sposób, a inne w drugą.
Hot Licks
9
@AlokSinghal: Jest również zdefiniowane przez implementację, czy pole bitowe typu intjest podpisane czy nie.
Keith Thompson
@KeithThompson dzięki za korektę. Zwykle zapominam o niektórych szczegółach dotyczących typów pól bitowych, ponieważ nie używam ich zbyt często.
Alok Singhal
67

Jak wskazuje Alok , standard pozostawia to w gestii implementacji.

Dla gcc, wartość domyślna jest podpisana, ale możesz to zmienić za pomocą -funsigned-char. uwaga: dla gcc w Android NDK, domyślną wartością jest unsigned . Możesz również wyraźnie poprosić o podpisane znaki za pomocą -fsigned-char.

Na MSVC wartość domyślna jest podpisana, ale można ją zmodyfikować za pomocą /J.

R Samuel Klatchko
źródło
2
Ciekawe, że opis Schildta nie pasuje do zachowania MSVC, ponieważ jego książki są zwykle skierowane do użytkowników MSVC. Zastanawiam się, czy MS zmieniło w pewnym momencie domyślne?
Michael Burr,
1
Myślałem, że to nie jest zależne od kompilatora, ale od platformy. Myślałem, że znak char został pozostawiony jako trzeci typ „znakowego typu danych”, aby dostosować się do tego, co systemy w tamtym czasie używały jako znaki drukowalne.
Spidey
10
Dokumentacja GCC mówi, że jest to zależne od maszyny: " Każdy rodzaj maszyny ma domyślne ustawienie tego, jaki powinien być znak. Jest albo domyślnie jak bez znaku lub domyślnie jak znak ze znakiem. "
Deduplicator,
1
Czy możesz podać źródło swojej notatki, że w systemie Android domyślną wartością jest niepodpisany znak?
phlipsy
1
@Spidey standard C nie rozróżnia kompilatorów, platform i architektur procesorów. Po prostu grupuje je wszystkie razem w ramach „realizacji”.
plugwash
35

C99 N1256 szkic 6.2.5 / 15 „Typy” ma do powiedzenia na temat podpisu typu char:

W implementacji należy zdefiniować znak char, który ma taki sam zakres, reprezentację i zachowanie, jak znak ze znakiem lub bez znaku.

oraz w przypisie:

CHAR_MIN, zdefiniowane w <limits.h>, będzie miało jedną z wartości 0lub SCHAR_MIN, i można to wykorzystać do rozróżnienia tych dwóch opcji. Niezależnie od dokonanego wyboru, charjest typem odrębnym od dwóch pozostałych i nie jest kompatybilny z żadnym.

Michael Burr
źródło
7

Według książki The C Programming Language autorstwa Dennisa Ritchiego, która jest de facto standardową książką dla ANSI C, zwykłe znaki ze znakiem lub bez znaku zależą od maszyny, ale znaki drukowalne są zawsze dodatnie.

Ravi Rathi
źródło
9
Niekoniecznie jest tak, że znaki do druku są zawsze pozytywne. Standard C gwarantuje, że wszyscy członkowie podstawowego zestawu znaków wykonania mają wartości nieujemne.
Keith Thompson
7

Zgodnie ze standardem C podpisanie zwykłego znaku jest „zdefiniowane w implementacji”.

Ogólnie rzecz biorąc, realizatorzy wybrali ten, który był bardziej efektywny do wdrożenia w ich architekturze. W systemach x86 znak jest zwykle podpisany. W systemach z ramieniem jest zwykle niepodpisany (wyjątkiem jest Apple iOS).

plugwash
źródło
2
@plugwash Twoja odpowiedź została prawdopodobnie odrzucona, ponieważ Tim Post zgubił klucze . Poważnie jednak, nie powinieneś martwić się o jeden głos przeciw, o ile masz pewność, że Twoja odpowiedź jest prawidłowa (tak jest w tym przypadku). Kilka razy zdarzyło mi się, że moje posty zostały odrzucone bez ważnego powodu. Nie martw się tym, czasami ludzie po prostu robią dziwne rzeczy.
Kaczor Donald
1
Dlaczego podpisany znak jest bardziej wydajny na platformie x86? Jakieś źródła?
martinkunev
2

Zgodnie z „The C ++ Programming Language” Bjarne Stroustrup, char„implementacja zdefiniowana”. Może to być signed charlub w unsigned charzależności od implementacji. Możesz sprawdzić, czy charjest podpisany, czy nie, używając std::numeric_limits<char>::is_signed.

BoQ
źródło
9
To jest pytanie C. C ++ to inny język, a odwołania do C ++ nie mają związku z C.
MM
1

Teraz wiemy, że standard pozostawia to do wdrożenia.

Ale jak sprawdzić typ jest signedlub unsigned, na przykład char?

Napisałem makro, aby to zrobić:

#define IS_UNSIGNED(t) ((t)~1 > 0)

i przetestować go gcc, clangi cl. Ale nie jestem pewien, czy zawsze jest to bezpieczne w innych przypadkach.

南山 竹
źródło
Co jest nie tak ze zwykłym CHAR_MIN <0 (lub WCHAR_MIN <0 dla wchar_t)?
Öö Tiib