Udostępnij przez


Literały ciągów i znaków (C++)

Język C++ obsługuje różne typy ciągów i znaków oraz udostępnia sposoby wyrażania wartości literałów każdego z tych typów. W kodzie źródłowym wyrażasz zawartość literałów znaków i ciągów przy użyciu zestawu znaków. Uniwersalne nazwy znaków i znaki ucieczki umożliwiają wyrażenie dowolnego ciągu przy użyciu tylko podstawowego zestawu znaków źródłowych. Nieprzetworzone literał ciągu pozwala uniknąć używania znaków ucieczki i może służyć do wyrażania wszystkich typów literałów ciągu. Można również tworzyć std::string literały bez konieczności wykonywania dodatkowych kroków budowy lub konwersji.

#include <string>
using namespace std::string_literals; // enables s-suffix for std::string literals

int main()
{
    // Character literals
    auto c0 =   'A'; // char
    auto c1 = u8'A'; // char
    auto c2 =  L'A'; // wchar_t
    auto c3 =  u'A'; // char16_t
    auto c4 =  U'A'; // char32_t

    // Multicharacter literals
    auto m0 = 'abcd'; // int, value 0x61626364

    // String literals
    auto s0 =   "hello"; // const char*
    auto s1 = u8"hello"; // const char* before C++20, encoded as UTF-8,
                         // const char8_t* in C++20
    auto s2 =  L"hello"; // const wchar_t*
    auto s3 =  u"hello"; // const char16_t*, encoded as UTF-16
    auto s4 =  U"hello"; // const char32_t*, encoded as UTF-32

    // Raw string literals containing unescaped \ and "
    auto R0 =   R"("Hello \ world")"; // const char*
    auto R1 = u8R"("Hello \ world")"; // const char* before C++20, encoded as UTF-8,
                                      // const char8_t* in C++20
    auto R2 =  LR"("Hello \ world")"; // const wchar_t*
    auto R3 =  uR"("Hello \ world")"; // const char16_t*, encoded as UTF-16
    auto R4 =  UR"("Hello \ world")"; // const char32_t*, encoded as UTF-32

    // Combining string literals with standard s-suffix
    auto S0 =   "hello"s; // std::string
    auto S1 = u8"hello"s; // std::string before C++20, std::u8string in C++20
    auto S2 =  L"hello"s; // std::wstring
    auto S3 =  u"hello"s; // std::u16string
    auto S4 =  U"hello"s; // std::u32string

    // Combining raw string literals with standard s-suffix
    auto S5 =   R"("Hello \ world")"s; // std::string from a raw const char*
    auto S6 = u8R"("Hello \ world")"s; // std::string from a raw const char* before C++20, encoded as UTF-8,
                                       // std::u8string in C++20
    auto S7 =  LR"("Hello \ world")"s; // std::wstring from a raw const wchar_t*
    auto S8 =  uR"("Hello \ world")"s; // std::u16string from a raw const char16_t*, encoded as UTF-16
    auto S9 =  UR"("Hello \ world")"s; // std::u32string from a raw const char32_t*, encoded as UTF-32
}

Literały ciągu nie mogą mieć prefiksu ani u8Lu, i U prefiksów, aby oznaczać wąski znak (jednobajtowy lub wielobajtowy), UTF-8, znak szeroki (UCS-2 lub UTF-16), kodowanie UTF-16 i UTF-32. Literał nieprzetworzonego ciągu może zawierać Rprefiksy , u8R, LRuRi UR dla pierwotnych odpowiedników wersji tych kodowań. Aby utworzyć wartości tymczasowe lub statyczne std::string , można użyć literałów ciągu lub nieprzetworzonych literałów ciągu z sufiksem s . Aby uzyskać więcej informacji, zobacz sekcję Literały ciągu poniżej. Aby uzyskać więcej informacji na temat podstawowego zestawu znaków źródłowych, uniwersalnych nazw znaków i używania znaków z rozszerzonych stron kodowych w kodzie źródłowym, zobacz Zestawy znaków.

Literały znaków

Literał znaku składa się z znaku stałego. Jest reprezentowana przez znak otoczony pojedynczym cudzysłowem. Istnieje pięć rodzajów literałów znaków:

  • Zwykłe literały znaków typu char, na przykład 'a'

  • Literały typu char UTF-8 (char8_t na przykład w języku C++20) u8'a'

  • Literały o szerokim znaku typu wchar_t, na przykład L'a'

  • Literały znaków UTF-16 typu char16_t, na przykład u'a'

  • Literały znaków UTF-32 typu char32_t, na przykład U'a'

Znak używany do literału znaku może być dowolnym znakiem, z wyjątkiem zastrzeżonego ukośnika odwrotnego (\), pojedynczego cudzysłowu (') lub nowego wiersza. Zastrzeżone znaki można określić przy użyciu sekwencji ucieczki. Znaki mogą być określane przy użyciu uniwersalnych nazw znaków, o ile typ jest wystarczająco duży, aby przechowywać znak.

Kodowanie

Literały znaków są kodowane inaczej na podstawie ich prefiksu.

  • Literał znaku bez prefiksu jest zwykłym literałem znaku. Wartość zwykłego literału znaku zawierającego pojedynczy znak, sekwencję ucieczki lub uniwersalną nazwę znaku, którą można przedstawić w zestawie znaków wykonywania, ma wartość równą wartości liczbowej kodowania w zestawie znaków wykonywania. Zwykły literał znaku, który zawiera więcej niż jeden znak, sekwencję ucieczki lub uniwersalną nazwę znaku, jest literałem wielorakterowym. Literał wielorakowy lub zwykły literał znaku, którego nie można przedstawić w zestawie znaków wykonywania, ma typ int, a jego wartość jest zdefiniowana przez implementację. W przypadku programu MSVC zobacz sekcję specyficzną dla firmy Microsoft poniżej.

  • Literał znaku rozpoczynający się od prefiksu L jest literałem o szerokim znaku. Wartość literału szerokoznakowego zawierającego pojedynczy znak, sekwencję ucieczki lub uniwersalną nazwę znaku ma wartość równą wartości liczbowej kodowania w zestawie znaków wykonywania, chyba że literał znaku nie ma reprezentacji w zestawie znaków wykonywania szerokoznakowych, w tym przypadku wartość jest zdefiniowana przez implementację. Wartość literału szerokoznakowego zawierającego wiele znaków, sekwencji ucieczki lub uniwersalnych nazw znaków jest definiowana przez implementację. W przypadku programu MSVC zobacz sekcję specyficzną dla firmy Microsoft poniżej.

  • Literał znaku rozpoczynający się od prefiksu u8 jest literałem UTF-8 znaków. Wartość literału UTF-8 znaków zawierającego pojedynczy znak, sekwencję ucieczki lub uniwersalną nazwę znaku ma wartość równą wartości punktu kodu ISO 10646, jeśli może być reprezentowana przez pojedynczą jednostkę kodu UTF-8 (odpowiadającą kontrolkom C0 i podstawowemu blokowi Unicode łacińskiego). Jeśli wartość nie może być reprezentowana przez pojedynczą jednostkę kodu UTF-8, program jest źle sformułowany. Literał utF-8 znaków zawierający więcej niż jeden znak, sekwencję ucieczki lub uniwersalną nazwę znaku jest źle sformułowany.

  • Literał znaku rozpoczynający się od prefiksu u jest literałem UTF-16 znaków. Wartość literału UTF-16 znaków zawierającego pojedynczy znak, sekwencję ucieczki lub uniwersalną nazwę znaku ma wartość równą wartości punktu kodu ISO 10646, jeśli może być reprezentowana przez pojedynczą jednostkę kodu UTF-16 (odpowiadającą podstawowej płaszczyźnie wielojęzycznej). Jeśli wartość nie może być reprezentowana przez pojedynczą jednostkę kodu UTF-16, program jest źle sformułowany. Literał znaku UTF-16 zawierający więcej niż jeden znak, sekwencję ucieczki lub uniwersalną nazwę znaku jest źle sformułowany.

  • Literał znaku rozpoczynający się od prefiksu U jest literałem ZNAKÓW UTF-32. Wartość literału znaków UTF-32 zawierającego pojedynczy znak, sekwencję ucieczki lub uniwersalną nazwę znaku ma wartość równą wartości punktu kodu ISO 10646. Literał znaku UTF-32 zawierający więcej niż jeden znak, sekwencję ucieczki lub uniwersalną nazwę znaku jest źle sformułowany.

Sekwencje ucieczki

Istnieją trzy rodzaje sekwencji ucieczki: proste, ósemkowe i szesnastkowe. Sekwencje ucieczki mogą być dowolną z następujących wartości:

Wartość Sekwencja ucieczki
nowy wiersz \n
ukośnik odwrotny \\
karta pozioma \t
znak zapytania ? Lub\?
pionowa karta \V
pojedynczy cudzysłów \'
Backspace \b
podwójny cudzysłów \"
powrót karetki \r
znak o wartości null \0
źródło danych formularzy \F
ósemkowy \Ooo
alert (dzwonek) \A
szesnastkowo \xhhh

Sekwencja ucieczki ósemkowej jest ukośnikiem odwrotnym, po którym następuje sekwencja od jednej do trzech cyfr ósemkowej. Sekwencja ucieczki ósemkowej kończy się w pierwszym znaku, który nie jest cyfrą ósemkową, jeśli napotkasz wcześniej niż trzecia cyfra. Najwyższa możliwa wartość ósemkowa to \377.

Sekwencja ucieczki szesnastkowej jest ukośnikiem odwrotnym, po którym następuje znak x, a następnie sekwencja co najmniej jednej cyfry szesnastkowej. Zera wiodące są ignorowane. W zwykłym lub u8-prefiksowym literału znaku najwyższa wartość szesnastkowa jest 0xFF. W literałie wieloznakowego z prefiksem L lub u jest to najwyższa wartość szesnastkowa 0xFFFF. W dosłownym literału znaku szerokiego U najwyższa wartość szesnastkowa jest 0xFFFFFFFF.

Ten przykładowy kod przedstawia kilka przykładów znaków ucieczki przy użyciu zwykłych literałów znaków. Ta sama składnia sekwencji ucieczki jest prawidłowa dla innych typów literałów znaków.

#include <iostream>
using namespace std;

int main() {
    char newline = '\n';
    char tab = '\t';
    char backspace = '\b';
    char backslash = '\\';
    char nullChar = '\0';

    cout << "Newline character: " << newline << "ending" << endl;
    cout << "Tab character: " << tab << "ending" << endl;
    cout << "Backspace character: " << backspace << "ending" << endl;
    cout << "Backslash character: " << backslash << "ending" << endl;
    cout << "Null character: " << nullChar << "ending" << endl;
}
/* Output:
Newline character:
ending
Tab character:  ending
Backspace character:ending
Backslash character: \ending
Null character:  ending
*/

Znak ukośnika odwrotnego (\) jest znakiem kontynuacji wiersza, gdy znajduje się na końcu wiersza. Jeśli chcesz, aby znak ukośnika odwrotnego był wyświetlany jako literał znaku, musisz wpisać dwa ukośniki odwrotne w wierszu (\\). Aby uzyskać więcej informacji na temat znaku kontynuacji wiersza, zobacz Fazy tłumaczenia.

specyficzne dla firmy Microsoft

Aby utworzyć wartość na podstawie wąskiego literału wielorakterowego, kompilator konwertuje znak lub sekwencję znaków między pojedynczymi cudzysłowami na wartości 8-bitowe w 32-bitowej liczbą całkowitą. Wiele znaków w literałach wypełnia odpowiednie bajty zgodnie z potrzebami z wysokiej kolejności do niskiej kolejności. Kompilator konwertuje liczbę całkowitą na typ docelowy zgodnie ze zwykłymi regułami. Na przykład aby utworzyć char wartość, kompilator przyjmuje bajt o niskiej kolejności. Aby utworzyć wchar_t wartość lub char16_t , kompilator przyjmuje wyraz o niskiej kolejności. Kompilator ostrzega, że wynik jest obcięty, jeśli jakiekolwiek bity są ustawione powyżej przypisanego bajtu lub słowa.

char c0    = 'abcd';    // C4305, C4309, truncates to 'd'
wchar_t w0 = 'abcd';    // C4305, C4309, truncates to '\x6364'
int i0     = 'abcd';    // 0x61626364

Sekwencja ucieczki ósemkowej, która wydaje się zawierać więcej niż trzy cyfry, jest traktowana jako 3-cyfrowa sekwencja ósemkowa, a następnie kolejne cyfry jako znaki w literale wielorakterowym, co może dać zaskakujące wyniki. Przykład:

char c1 = '\100';   // '@'
char c2 = '\1000';  // C4305, C4309, truncates to '0'

Sekwencje ucieczki, które wydają się zawierać znaki inne niż ósemkowe, są oceniane jako sekwencja ósemkowa do ostatniego znaku ósemkowego, a następnie pozostałe znaki jako kolejne znaki literału wielostopniowego. Ostrzeżenie C4125 jest generowane, jeśli pierwszy znak inny niż ósemkowy jest cyfrą dziesiętną. Przykład:

char c3 = '\009';   // '9'
char c4 = '\089';   // C4305, C4309, truncates to '9'
char c5 = '\qrs';   // C4129, C4305, C4309, truncates to 's'

Sekwencja ucieczki ósemkowej, która ma wyższą wartość niż \377 powoduje błąd C2022: "value-in-decimal": zbyt duży dla znaku.

Sekwencja ucieczki, która wydaje się mieć znaki szesnastkowe i nieszesnastkowe, jest obliczana jako literał wielorakowy, który zawiera sekwencję ucieczki szesnastkowej do ostatniego znaku szesnastkowego, a następnie znaki nieszesnastkowe. Sekwencja ucieczki szesnastkowej, która nie zawiera cyfr szesnastkowe, powoduje błąd kompilatora C2153: "literały szesnastkowe muszą mieć co najmniej jedną cyfrę szesnastkową".

char c6 = '\x0050'; // 'P'
char c7 = '\x0pqr'; // C4305, C4309, truncates to 'r'

Jeśli literał znaku szerokiego z prefiksem L zawiera sekwencję wielotypową, wartość jest pobierana z pierwszego znaku, a kompilator zgłasza ostrzeżenie C4066. Kolejne znaki są ignorowane, w przeciwieństwie do zachowania równoważnego zwykłego literału wielorakterowego.

wchar_t w1 = L'\100';   // L'@'
wchar_t w2 = L'\1000';  // C4066 L'@', 0 ignored
wchar_t w3 = L'\009';   // C4066 L'\0', 9 ignored
wchar_t w4 = L'\089';   // C4066 L'\0', 89 ignored
wchar_t w5 = L'\qrs';   // C4129, C4066 L'q' escape, rs ignored
wchar_t w6 = L'\x0050'; // L'P'
wchar_t w7 = L'\x0pqr'; // C4066 L'\0', pqr ignored

Sekcja specyficzna dla firmy Microsoft kończy się tutaj.

Nazwy znaków uniwersalnych

W literałach znaków i natywnych literałach ciągu (nieprzetworzonych) każdy znak może być reprezentowany przez uniwersalną nazwę znaku. Nazwy znaków uniwersalnych są tworzone przez prefiks \U , po którym następuje ośmiocyfrowy punkt kodu Unicode lub prefiks \u , po którym następuje czterocyfrowy punkt kodu Unicode. Wszystkie osiem lub cztery cyfry muszą być obecne, aby utworzyć dobrze sformułowaną uniwersalną nazwę znaku.

char u1 = 'A';          // 'A'
char u2 = '\101';       // octal, 'A'
char u3 = '\x41';       // hexadecimal, 'A'
char u4 = '\u0041';     // \u UCN 'A'
char u5 = '\U00000041'; // \U UCN 'A'

Pary zastępcze

Nazwy znaków uniwersalnych nie mogą kodować wartości w zakresie punktów kodu zastępczego D800-DFFF. W przypadku par zastępczych Unicode określ uniwersalną nazwę znaku przy użyciu , \UNNNNNNNNgdzie NNNNNNNNNN jest ośmiocyfrowym punktem kodu dla znaku. Kompilator w razie potrzeby generuje parę zastępczą.

W języku C++03 język zezwalał na reprezentację podzbioru znaków tylko przez ich uniwersalne nazwy znaków i zezwalał na niektóre uniwersalne nazwy znaków, które w rzeczywistości nie reprezentują żadnych prawidłowych znaków Unicode. Ten błąd został naprawiony w standardzie C++11. W języku C++11 zarówno literały znaków, jak i ciągów oraz identyfikatory mogą używać uniwersalnych nazw znaków. Aby uzyskać więcej informacji na temat uniwersalnych nazw znaków, zobacz Zestawy znaków. Aby uzyskać więcej informacji na temat unicode, zobacz Unicode. Aby uzyskać więcej informacji na temat par zastępczych, zobacz Pary zastępcze i znaki dodatkowe.

Literały

Literał ciągu reprezentuje sekwencję znaków, które razem tworzą ciąg zakończony wartością null. Znaki muszą być ujęte między podwójnym cudzysłowem. Istnieją następujące rodzaje literałów ciągów:

Wąskie literały ciągu

Literał wąskiego ciągu jest niefiksowanym, rozdzielonym podwójnym cudzysłowem tablicy typu const char[n], gdzie n jest długością tablicy w bajtach. Wąski literał ciągu może zawierać dowolny znak graficzny, z wyjątkiem podwójnego cudzysłowu ("), ukośnika odwrotnego (\) lub znaku nowego wiersza. Wąski literał ciągu może również zawierać sekwencje ucieczki wymienione powyżej oraz uniwersalne nazwy znaków, które mieszczą się w bajtach.

const char *narrow = "abcd";

// represents the string: yes\no
const char *escaped = "yes\\no";

Ciągi zakodowane w formacie UTF-8

Ciąg zakodowany w formacie UTF-8 jest poprzedzony prefiksem u8-cudzysłowu rozdzielanym, zakończonym wartością null tablicą typu const char[n], gdzie n jest długością zakodowanej tablicy w bajtach. Literał ciągu z prefiksem u8 może zawierać dowolny znak graficzny, z wyjątkiem podwójnego cudzysłowu (), ukośnika odwrotnego ("\) lub znaku nowego wiersza. Literał ciągu z prefiksem u8 może również zawierać sekwencje ucieczki wymienione powyżej i dowolną uniwersalną nazwę znaku.

Język C++20 wprowadza przenośny char8_t typ znaku Unicode zakodowany w formacie UTF-8(UTF-8). W języku C++20 u8 prefiksy literału określają znaki lub ciągi char8_t zamiast char.

// Before C++20
const char* str1 = u8"Hello World";
const char* str2 = u8"\U0001F607 is O:-)";
// C++20 and later
const char8_t* u8str1 = u8"Hello World";
const char8_t* u8str2 = u8"\U0001F607 is O:-)";

Literały szerokiego ciągu

Literał szerokiego ciągu to tablica o wartości null typu stała poprzedzona znakiem wchar_t "L" i zawiera dowolny znak graficzny, z wyjątkiem podwójnego cudzysłowu (), ukośnika odwrotnego ("\) lub znaku nowego wiersza. Szeroki literał ciągu może zawierać sekwencje ucieczki wymienione powyżej i dowolną uniwersalną nazwę znaku.

const wchar_t* wide = L"zyxw";
const wchar_t* newline = L"hello\ngoodbye";

char16_t i char32_t (C++11)

Język C++11 wprowadza przenośne char16_t typy znaków (16-bitowe Unicode) i char32_t (32-bitowe Unicode):

auto s3 = u"hello"; // const char16_t*
auto s4 = U"hello"; // const char32_t*

Nieprzetworzone literały ciągu (C++11)

Nieprzetworzone literały ciągu to tablica o wartości null — dowolnego typu znaku — zawierająca dowolny znak graficzny, w tym podwójny cudzysłów (), ukośnik odwrotny ("\) lub znak nowego wiersza. Nieprzetworzone literały ciągów są często używane w wyrażeniach regularnych korzystających z klas znaków oraz w ciągach HTML i ciągach XML. Aby zapoznać się z przykładami, zobacz następujący artykuł: Bjarne Stroustrup's FAQ on C++11 (Często zadawane pytania dotyczące języka Bjarne Stroustrup w języku C++11).

// represents the string: An unescaped \ character
const char* raw_narrow = R"(An unescaped \ character)";
const wchar_t*  raw_wide  = LR"(An unescaped \ character)";
const char*     raw_utf8a = u8R"(An unescaped \ character)"; // Before C++20
const char8_t*  raw_utf8b = u8R"(An unescaped \ character)"; // C++20
const char16_t* raw_utf16 = uR"(An unescaped \ character)";
const char32_t* raw_utf32 = UR"(An unescaped \ character)";

Ogranicznik to sekwencja zdefiniowana przez użytkownika zawierająca maksymalnie 16 znaków, która bezpośrednio poprzedza nawias otwierający nieprzetworzonego literału ciągu i natychmiast następuje po nawiasie zamykającym. Na przykład w R"abc(Hello"\()abc" sekwencji ogranicznika znajduje się abc wartość , a zawartość ciągu to Hello"\(. Ogranicznik umożliwia uściślanie nieprzetworzonych ciągów zawierających znaki podwójnego cudzysłowu i nawiasy. Ten literał ciągu powoduje błąd kompilatora:

// meant to represent the string: )"
const char* bad_parens = R"()")";  // error C2059

Ale ogranicznik rozwiązuje to:

const char* good_parens = R"xyz()")xyz";

Możesz skonstruować nieprzetworzone literał ciągu, który zawiera nowy wiersz (a nie znak ucieczki) w źródle:

// represents the string: hello
//goodbye
const wchar_t* newline = LR"(hello
goodbye)";

std::string literały (C++14)

std::string literały to implementacje biblioteki standardowej literałów zdefiniowanych przez użytkownika (patrz poniżej), które są reprezentowane jako "xyz"s (z sufiksem s ). Ten rodzaj literału ciągu tworzy tymczasowy obiekt typu std::string, , std::wstringstd::u32stringlub std::u16string, w zależności od określonego prefiksu. Jeśli żaden prefiks nie jest używany, jak powyżej, std::string jest generowany. L"xyz"s tworzy element std::wstring. u"xyz"s tworzy std ::u16string i U"xyz"s tworzy std::u32string.

//#include <string>
//using namespace std::string_literals;
string str{ "hello"s };
string str2{ u8"Hello World" };     // Before C++20
u8string u8str2{ u8"Hello World" }; // C++20
wstring str3{ L"hello"s };
u16string str4{ u"hello"s };
u32string str5{ U"hello"s };

Sufiks s może być również używany w literałach nieprzetworzonych ciągów:

u32string str6{ UR"(She said "hello.")"s };

std::string literały są definiowane w przestrzeni nazw std::literals::string_literals w pliku nagłówka <ciągu> . Ponieważ std::literals::string_literalsobiekty i std::literals są deklarowane jako wbudowane przestrzenie nazw, std::literals::string_literals są automatycznie traktowane tak, jakby należały bezpośrednio do przestrzeni nazw std.

Rozmiar literałów ciągu

W przypadku ciągów ANSI char* i innych kodowań jednobajtowych (ale nie UTF-8) rozmiar literału ciągu (w bajtach) jest liczbą znaków plus 1 dla znaku zerowego zakończenia. W przypadku wszystkich innych typów ciągów rozmiar nie jest ściśle powiązany z liczbą znaków. KodOWANIE UTF-8 używa maksymalnie czterech char elementów do kodowania niektórych jednostek kodu, a char16_t kodowanie wchar_t jako UTF-16 może używać dwóch elementów (w sumie czterech bajtów) do kodowania pojedynczej jednostki kodu. W tym przykładzie pokazano rozmiar literału szerokiego ciągu w bajtach:

const wchar_t* str = L"Hello!";
const size_t byteSize = (wcslen(str) + 1) * sizeof(wchar_t);

Zwróć uwagę, że strlen() rozmiar wcslen() znaku null zakończenia i nie zawiera rozmiaru znaku o wartości null, którego rozmiar jest równy rozmiarowi elementu typu ciągu: jeden bajt w char* ciągu lub char8_t* , dwa bajty wchar_t*char16_t* lub ciągi, a cztery bajty w char32_t* ciągach.

W wersjach programu Visual Studio przed programem Visual Studio 2022 w wersji 17.0 maksymalna długość literału ciągu wynosi 65 535 bajtów. Ten limit dotyczy zarówno wąskich literałów ciągów, jak i literałów szerokiego ciągu. W programie Visual Studio 2022 w wersji 17.0 lub nowszej to ograniczenie zostanie zniesione, a długość ciągu jest ograniczona przez dostępne zasoby.

Modyfikowanie literałów ciągu

Ponieważ literały ciągu (nie w tym std::string literałów) są stałymi, próba ich zmodyfikowania — na przykład str[2] = 'A'— powoduje błąd kompilatora.

specyficzne dla firmy Microsoft

W języku Microsoft C++można użyć literału ciągu, aby zainicjować wskaźnik na wartość inną niż const char lub wchar_t. To inicjowanie inne niż const jest dozwolone w kodzie C99, ale jest przestarzałe w języku C++98 i usunięte w języku C++11. Próba zmodyfikowania ciągu powoduje naruszenie dostępu, jak w tym przykładzie:

wchar_t* str = L"hello";
str[2] = L'a'; // run-time error: access violation

Kompilator może spowodować emitowanie błędu, gdy literał ciągu jest konwertowany na wskaźnik znaków inny niż const podczas ustawiania /Zc:strictStrings opcji kompilatora (Wyłącz konwersję typu literału ciągu). Zalecamy używanie kodu przenośnego zgodnego ze standardami. Dobrym rozwiązaniem jest również użycie słowa kluczowego auto do deklarowania inicjatorów literałów ciągu, ponieważ jest rozpoznawany jako poprawny typ (const). Na przykład ten przykład kodu przechwytuje próbę zapisania do literału ciągu w czasie kompilacji:

auto str = L"hello";
str[2] = L'a'; // C3892: you cannot assign to a variable that is const.

W niektórych przypadkach identyczne literały ciągów mogą być w puli, aby zaoszczędzić miejsce w pliku wykonywalnym. W puli literałów ciągu kompilator powoduje, że wszystkie odwołania do określonego literału ciągu wskazują tę samą lokalizację w pamięci, zamiast mieć każdy punkt odniesienia do oddzielnego wystąpienia literału ciągu. Aby włączyć buforowanie ciągów, użyj opcji kompilatora /GF .

Sekcja specyficzna dla firmy Microsoft kończy się tutaj.

Łączenie sąsiednich literałów ciągu

Sąsiadujące szerokie lub wąskie literały ciągów są łączone. Ta deklaracja:

char str[] = "12" "34";

jest identyczna z tą deklaracją:

char atr[] = "1234";

i do tej deklaracji:

char atr[] =  "12\
34";

Używanie osadzonych kodów ucieczki szesnastkowych w celu określenia literałów ciągów może spowodować nieoczekiwane wyniki. Poniższy przykład próbuje utworzyć literał ciągu zawierający znak ASCII 5, a następnie znaki f, i, v i e:

"\x05five"

Rzeczywisty wynik jest szesnastkowym 5F, który jest kodem ASCII dla podkreślenia, a następnie znakami i, v i e. Aby uzyskać prawidłowy wynik, możesz użyć jednej z następujących sekwencji ucieczki:

"\005five"     // Use octal literal.
"\x05" "five"  // Use string splicing.

std::string literały (i powiązane std::u8string, std::u16stringi std::u32string) mogą być łączone z operatorem + zdefiniowanym dla basic_string typów. Można je również łączyć w taki sam sposób, jak sąsiadujące literały ciągu. W obu przypadkach kodowanie ciągów i sufiks muszą być zgodne:

auto x1 = "hello" " " " world"; // OK
auto x2 = U"hello" " " L"world"; // C2308: disagree on prefix
auto x3 = u8"hello" " "s u8"world"z; // C3688, disagree on suffixes

Literały ciągu z uniwersalnymi nazwami znaków

Literały ciągu natywnego (nieprzetworzonego) mogą używać uniwersalnych nazw znaków do reprezentowania dowolnego znaku, o ile nazwa znaku uniwersalnego może być zakodowana jako co najmniej jeden znak w typie ciągu. Na przykład nazwa znaku uniwersalnego reprezentująca znak rozszerzony nie może być zakodowana w wąskim ciągu przy użyciu strony kodowej ANSI, ale może być zakodowana w wąskich ciągach na niektórych stronach kodu wielobajtowego lub w ciągach UTF-8 lub w szerokim ciągu. W języku C++11 obsługa unicode jest rozszerzana przez char16_t* typy ciągów i char32_t* , a język C++20 rozszerza go na char8_t typ:

// ASCII smiling face
const char*     s1 = ":-)";

// UTF-16 (on Windows) encoded WINKING FACE (U+1F609)
const wchar_t*  s2 = L"😉 = \U0001F609 is ;-)";

// UTF-8  encoded SMILING FACE WITH HALO (U+1F607)
const char*     s3a = u8"😇 = \U0001F607 is O:-)"; // Before C++20
const char8_t*  s3b = u8"😇 = \U0001F607 is O:-)"; // C++20

// UTF-16 encoded SMILING FACE WITH OPEN MOUTH (U+1F603)
const char16_t* s4 = u"😃 = \U0001F603 is :-D";

// UTF-32 encoded SMILING FACE WITH SUNGLASSES (U+1F60E)
const char32_t* s5 = U"😎 = \U0001F60E is B-)";

Zobacz też

Zestawy znaków
Literały liczbowe, logiczne i wskaźniki
Literały zdefiniowane przez użytkownika