Ошибка компиляции вида cannot convert 'wchar_t*' to 'char*' или кракозябры вместо русского текста после конвертации — типичные симптомы того, что преобразование wchar_t в char выполнено простым приведением типов, а не корректной функцией перекодирования. Прямой каст (char)wide_char обрезает старший байт широкого символа и работает только для ASCII-диапазона, а всё остальное превращается в мусор.
Проблема в том, что wchar_t и char — это не просто «широкий» и «узкий» варианты одного типа. Широкий символ обычно занимает 2 байта (Windows) или 4 байта (Linux), а обычный char — один байт, и между ними существует ещё и разница в кодировках. В этой статье разберём все рабочие способы конвертации: от классических функций C до современных подходов C++, с примерами кода и типичными ошибками.
Почему wchar_t и char несовместимы напрямую
Тип char хранит один байт и исторически используется для строк в кодировках вроде ASCII, Windows-1251 или UTF-8. Тип wchar_t задуман для «широких» символов: на Windows это 16-битный тип, соответствующий UTF-16, а на Linux — 32-битный, соответствующий UTF-32. Именно из-за этой разницы размеров и представлений простое приведение типов не работает.
Когда вы пишете char c = (char)wch;, компилятор молча отбрасывает старшие байты. Для латинской буквы 'A' результат будет корректным, а для символа 'Я' или иероглифа — нет. Ни один символ вне диапазона 0–127 не переживёт побайтовое приведение wchar_t к char без потери данных.
- 🔤 wchar_t на Windows — 2 байта, кодировка UTF-16 (LE)
- 🐧 wchar_t на Linux/macOS — 4 байта, кодировка UTF-32
- 📄 char-строки — чаще всего UTF-8 или локальная однобайтовая кодировка
- ⚙️ Прямой каст — работает только для ASCII (коды 0–127)
Способ 1: wcstombs и wctomb — классика стандартной библиотеки C
Самый переносимый способ из стандарта C — функции wcstombs() и wctomb(), объявленные в заголовке <stdlib.h>. Они конвертируют широкую строку в многобайтовую с учётом текущей локали, установленной через setlocale(). Без вызова setlocale(LC_ALL, "") функции работают в минимальной локали «C» и корректно обрабатывают только ASCII.
#include <stdio.h>#include <stdlib.h>
#include <locale.h>
#include <wchar.h>
int main() {
setlocale(LC_ALL, "");
const wchar_t *wstr = L"Привет, мир!";
size_t needed = wcstombs(NULL, wstr, 0) + 1;
char buffer = (char)malloc(needed);
wcstombs(buffer, wstr, needed);
printf("%s\n", buffer);
free(buffer);
return 0;
}
Обратите внимание на двухпроходную схему: сначала вызываем wcstombs с NULL в качестве буфера, чтобы узнать требуемый размер, затем выделяем память и конвертируем. Это защищает от переполнения буфера — типичной ошибки при работе со строками фиксированной длины.
💡Всегда вызывайте setlocale(LC_ALL, "") до wcstombs — иначе многобайтовые символы не будут распознаны, и функция вернёт (size_t)-1.