char vs wchar_t

1

Zakładam ten temat, ponieważ chciałbym poznać różnice pomiędzy typem char a wchar_t, gdyż ich nie dostrzegam. Nie wiem kiedy powinienem korzystać z charów, a kiedy użyć wcharów.

Dobrze wiadomo że unsigned char przyjmuje wartości od 0 do 255 i służy do reprezentowania znaków ASCII (dobrze mówię ?). Chciałem więc zobaczyć jakie wartości zawierają wchary i w tym celu napisałem taki prosty program:

#include <iostream>
using namespace std;

int main()
{
    char ch = (char)0;
    wchar_t wch = (wchar_t)0;
    do
    {
        cout << ch << "\t|\t";
        wcout << wch << L"\t|\t" << (int)wch << endl;
        ch++; wch++;
    }
    while(wch != 0);
}

Po jego uruchomieniu wynika że poszczególne wartości charów są takie same jak wcharów, każda litera/znak są identyczne na tej samej pozycji, w dodatku z programu wynika że wchar również przyjmuje wartości do 255, ponieważ później pętla do .. while wychodzi z swojego zakresu.

Więc co nam daje tak na prawdę używanie wcharów ? Kiedy powinienem używać zwykłych charów a kiedy wchraów ? Do czego przeznaczony jest dany typ ? Chętnie usłyszę odpowiedzi na moje pytania oraz inne ciekawe rzeczy na temat tych typów.

0

Po pierwsze, nie możesz założyć, że char będzie miał wartości 0-255 lub -128-127. Po drugie, ponieważ nie można powiedzieć, czy typ char ma znak czy nie, narażasz się na UB przy przypisaniu wartości większej niż std::numeric_limits<char>::max() (i tak samo dla wchar_t)

Wracając do pytania: wchar_t jest na większym typem, przeznaczonym do trzymania znaków "szerokich" i jest najczęściej rozmiaru 2 lub 4 bajtów (w porównaniu do 1 bajtu chara). Najpopularniejsze (lub wszystkie) znaki unicode się w nim mieszczą i można je nim reprezentować, na windowsie typ ten jest używany do komunikacji z WINAPI "unicode" (U W na końcu funkcji), a char do funkcji zakończonych "A".

5

Dobrze wiadomo że unsigned char przyjmuje wartości od 0 do 255 i służy do reprezentowania znaków ASCII (dobrze mówię ?).

Wszystkie typy podstawowe przechowują liczby. char służy do przechowywania bajów i tak się składa, że wszystkie znaki ASCII się w bajcie zmieszczą i stąd taka nazwa tego typu, a nie inna. Konkretne wartości przez te typy nie są określone przez standard. Jednak można przyjąć, że (najczęściej) char ma 8 bitów na Windows i w Linuksach, a wchar_t 16 bitów na tym pierwszym i 32 bity na tym drugim.

Zdecydowanie należy od tych typów oddzielić kwestię kodowania znaków. Najlepiej patrzyć na char jako bajt. Z bajtów składają się wszystkie napisy: te w ASCII, UTF-8 czy UTF-32. I wszystkie napisy w tych kodowaniach można przechowywać w tablicy charów. Nikt ci tego nie zabroni i tak to się z resztą najczęściej robi.
Problem zaczyna się wtedy, gdy chcemy poznać postać konkretnych znaków by manipulować nimi w większym zakresie niż samo przechowywanie. W ASCII wszystkie znaki zmieszczą się w jednym bajcie i zawsze będziemy wiedzieć, że dziesiąty bajt to dziesiąty znak. W takim UTF-8 problem jest już o wiele większy, bo w jednym napisie piąty znak będzie w piątym bajcie, a w innym napisie w piętnastym. Stąd pomysł na wprowadzenie typu, który będzie na tyle duży by przechować dowolny codepoint (znak) Unicode (czy dowolnego innego "szerokiego" zestawu znaków), tak by można było trzymać pojedyncze codepointy w kolejnych wchar_t. W ten sposób zdecydowanie ułatwiamy sobie życie przy np. przy modyfikacji tych napisów.

Stąd generalnie zasada jest taka, że wchar_t używamy wtedy, gdy chcemy manipulować napisami Unicode oraz unikać go wtedy, gdy napisy traktujemy jako bajty, bo tylko je przechowujemy / przekazujemy.

1

Nie stawiamy spacji przed znakiem zapytania.

Pod Windows, char przechowuje tekst w bieżącej stronie kodowej, a wchar_t w UTF-16. Oznacza to, że niektóre znaki (egipskie hieroglify...) zajmują aż dwa wchar_t pod rząd, czyli nie jest spełniony warunek "jeden znak == jedna wartość wchar_t", choć dla praktycznie wszystkich często stosowanych znaków jest to prawda.

Najpopularniejsze (lub wszystkie) znaki unicode się w nim mieszczą i można je nim reprezentować, na windowsie typ ten jest używany do komunikacji z WINAPI "unicode" (U na końcu funkcji), a char do funkcji zakończonych "A".

Tak, przy czym funkcje z A na końcu (np. MessageBoxA) tak naprawdę konwertują tekst na UTF-16 i uruchamiają funkcje z W.
Nowe funkcje WinAPI powstają już tylko w wersji unikodowej, i bez dodanej literki (np. TaskDialog – nowy, wypaśny MessageBox).

Więc co nam daje tak na prawdę używanie wcharów ?
Dostęp do znaków wszystkich języków, nie tylko do bieżacej strony kodowej.

Kiedy powinienem używać zwykłych charów a kiedy wchraów ?
Jak najrzadziej, jak najczęściej.

Chętnie usłyszę odpowiedzi na moje pytania oraz inne ciekawe rzeczy na temat tych typów.

1 użytkowników online, w tym zalogowanych: 0, gości: 1