Ошибка компиляции вида 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.