Co robią pojedyncze cudzysłowy w C ++, gdy są używane z wieloma znakami?

279

Jestem ciekawy tego kodu:

cout << 'test'; // Note the single quotes.

daje mi wyjście 1952805748.

Moje pytanie: czy wyjście ma adres w pamięci czy coś takiego?

klarowność
źródło
10
Zwróć uwagę, że rzeczywistą wartością jest implementacja zdefiniowana stackoverflow.com/questions/3960954/c-multicharacter-literal
FireAphis

Odpowiedzi:

283

To dosłowny znak złożony z wielu znaków. 1952805748jest 0x74657374, który rozkłada się jako

0x74 -> 't'
0x65 -> 'e'
0x73 -> 's'
0x74 -> 't'

Edytować:

Standard C ++, §2.14.3 / 1 - Literały znakowe

(...) Zwykły literał znakowy zawierający więcej niż jeden znak c jest literałem wieloznakowym. Literał wieloznakowy ma typ int i wartość zdefiniowaną w implementacji.

K-ballo
źródło
11
Nie wspomniałeś, że jest to implementacja zdefiniowana.
Thomas Bonini,
2
Przypuszczam, że najśmieszniejsze w tej definicji jest to, że sizeof(int)zdefiniowano również implementację. Tak więc nie tylko zdefiniowano implementację kolejności pamięci, ale także jej maksymalną długość.
bobobobo
74

Nie, to nie jest adres. Jest to tak zwana postać wielobajtowa.

Zazwyczaj są to wartości ASCII czterech połączonych znaków.

't' == 0x74; 'e' == 0x65; 's' == 0x73; 't' == 0x74; 

Więc 0x74657374 to 1952805748.

Ale może to być także 0x74736574 na innym kompilatorze. Oba standardy C i C ++ mówią, że wartość znaków wielobajtowych jest zdefiniowana implementacja . Tak więc ogólnie jego użycie jest zdecydowanie odradzane.

pisklęta
źródło
Czy długość takiego wielobajtowego znaku jest ograniczona do 4 bajtów? Czy to reprezentuje int zapisane jako znaki?
Giorgio
2
@Giorgio: Standard mówi tylko, że jego implementacja jest zdefiniowana, bez dalszych szczegółów. W praktyce, ponieważ intna większości maszyn są 4 bajty, nie sądzę, aby warto było używać więcej niż 4 bajtów. Tak, miał to być wygodny sposób na zapisanie niektórych stałych, ale niestety różne kompilatory interpretują go inaczej, więc w dzisiejszych czasach większość stylów kodowania zniechęca do jego używania.
chys,
2
@chys: A fakt, że jest on zdefiniowany w implementacji, oznacza, że ​​nie jest nawet wymagany spójność. Kompilator zgodny może nadać wszystkim literałom wieloznakowym wartość 0, na przykład (choć byłoby to nieprzyjazne).
Keith Thompson,
2
Należy zapytać, dlaczego ta zwariowana funkcja istnieje w standardzie. Wydaje się, że jest to tak rzadki przypadek użycia, mimo to implementacja jest zdefiniowana, i można to zrobić całkiem wyraźnie za pomocą zwykłego przesuwania bitów i / lub w razie potrzeby.
Boann
1
@Boann Tak , dokładnie moje sentymenty. Ale możesz bezpiecznie używać go w przełącznikach i tak dalej, ponieważ bezpośrednie porównanie ==powinno sprawdzić
bobobobo
18

Zwykły literał znakowy, który zawiera więcej niż jeden znak c-char, jest literałem wieloznakowym. Literał wieloznakowy ma typ int i wartość zdefiniowaną w implementacji.

Zachowanie zdefiniowane w implementacji musi być udokumentowane przez implementację. na przykład w gcc można go znaleźć tutaj

Kompilator ceni znak wieloznakowy na raz stałą, przesuwając poprzednią wartość o liczbę bitów na znak docelowy, a następnie orientując się we wzorcu bitowym nowego znaku obciętego do szerokości celu postać. Ostateczny wzorzec bitowy ma typ int i dlatego jest podpisany, niezależnie od tego, czy pojedyncze znaki są podpisane, czy nie.

Sprawdź wyjaśnienia na tej stronie, aby uzyskać więcej informacji

Mouna Cheikhna
źródło
10

To naprawdę tylko ints. Są szeroko stosowane w enumie Core Audio API, na przykład w CoreAudioTypes.hpliku nagłówkowym,

enum
{
    kAudioFormatLinearPCM               = 'lpcm',
    kAudioFormatAC3                     = 'ac-3',
    kAudioFormat60958AC3                = 'cac3',
    kAudioFormatAppleIMA4               = 'ima4',
    kAudioFormatMPEG4AAC                = 'aac ',
    kAudioFormatMPEG4CELP               = 'celp',
} ;

Wiele mówi się o tym, że nie jest się „niezależnym od platformy”, ale kiedy używasz interfejsu API stworzonego dla konkretnej platformy, która dba o przenośność. Sprawdzanie równości na tej samej platformie nigdy nie zawiedzie. Te enumwartości są łatwiejsze do odczytania i faktycznie zawierają swoją tożsamość w wartości , co jest całkiem miłe.

To, co próbowałem zrobić poniżej, to owinięcie literału znaku wielobajtowego, aby można go było wydrukować (działa na Macu). Dziwne jest to, że jeśli nie zużyjesz wszystkich 4 znaków, wynik staje się błędny poniżej ...

#include <stdio.h>

#define MASK(x,BYTEX) ((x&(0xff<<8*BYTEX))>>(8*BYTEX))

struct Multibyte
{
  union{
    int val ;
    char vals[4];
  };

  Multibyte() : val(0) { }
  Multibyte( int in )
  {
    vals[0] = MASK(in,3);
    vals[1] = MASK(in,2);
    vals[2] = MASK(in,1);
    vals[3] = MASK(in,0);
  }
  char operator[]( int i ) {
    return val >> (3-i)*8 ; // works on mac
    //return val>>i*8 ; // might work on other systems
  }

  void println()
  {
    for( int i = 0 ; i < 4 ; i++ )
      putc( vals[i], stdout ) ;
    puts( "" ) ;
  }
} ;

int main(int argc, const char * argv[])
{
  Multibyte( 'abcd' ).println() ;  
  Multibyte( 'x097' ).println() ;
  Multibyte( '\"\\\'\'' ).println() ;
  Multibyte( '/*|' ).println() ;
  Multibyte( 'd' ).println() ;

  return 0;
}
Bobobobo
źródło
6
„Sprawdzanie równości na tej samej platformie nigdy nie zawiedzie”. Może. Zaktualizuj do Visual Studio xyz i ugryź się w język. Ta biblioteka podjęła straszną decyzję.
Wyścigi lekkości na orbicie
@LightnessRacesinOrbit „Uaktualnij do Visual Studio xyz i ugryź się w język”. Core Audio API to systemowy interfejs audio systemu OS X, więc nie ma to znaczenia.
Jean-Michaël Celerier
5
@ Jean-MichaëlCelerier: Fine; uaktualnij wersję OSX Clang i ugryź się w język ...
Lekkość ściga się na orbicie
1

Ten rodzaj funkcji jest naprawdę dobry, gdy budujesz parsery. Rozważ to:

byte* buffer = ...;
if(*(int*)buffer == 'GET ')
  invoke_get_method(buffer+4);

Ten kod prawdopodobnie będzie działał tylko na konkretnej endianowości i może działać w różnych kompilatorach

Ayende Rahien
źródło