Есть ли хороший способ конвертировать из unsigned char* в char*?

В те дни я много читал о reinterpret_cast<> и о том, как его следует использовать (и избегать его в большинстве случаев).

Хотя я понимаю, что использование reinterpret_cast<> для приведения, скажем, unsigned char* к char*, является определенной реализацией (и, следовательно, непереносимой), кажется, нет другого способа для эффективного преобразовать одно в другое.

Допустим, я использую библиотеку, которая работает с unsigned char*, для обработки некоторых вычислений. Внутренне я уже использую char* для хранения своих данных (и я не могу изменить его, потому что это убьет щенков, если я это сделаю).

Я бы сделал что-то вроде:

char* mydata = getMyDataSomewhere();
size_t mydatalen = getMyDataLength();

// We use it here
// processData() takes a unsigned char*
void processData(reinterpret_cast<unsigned char*>(mydata), mydatalen);

// I could have done this:
void processData((unsigned char*)mydata, mydatalen);
// But it would have resulted in a similar call I guess ?

Если я хочу, чтобы мой код был легко переносимым, похоже, у меня нет другого выбора, кроме как сначала скопировать мои данные. Что-то типа:

char* mydata = getMyDataSomewhere();
size_t mydatalen = getMyDataLength();
unsigned char* mydata_copy = new unsigned char[mydatalen];
for (size_t i = 0; i < mydatalen; ++i)
  mydata_copy[i] = static_cast<unsigned char>(mydata[i]);

void processData(mydata_copy, mydatalen);

Конечно, это очень неоптимально, и я даже не уверен, что оно более переносимо, чем первое решение.

Итак, вопрос в том, что бы вы сделали в этой ситуации, чтобы получить хорошо переносимый код?


person ereOn    schedule 16.01.2011    source источник
comment
Нехорошо использовать голый char* в программе C++ для чего-либо, кроме этого конкретного приведения (между unsigned char*, char* и/или signed char*), которое само по себе очень переносимо.   -  person Öö Tiib    schedule 17.01.2011


Ответы (4)


Портативный - это практический вопрос. Таким образом, reinterpret_cast для конкретного использования преобразования между char* и unsigned char* является переносимым. Но все же я бы обернул это использование парой функций вместо того, чтобы выполнять reinterpret_cast непосредственно в каждом месте.

Не переусердствуйте с введением неэффективности при использовании языка, где почти все недостатки (включая проблему с ограниченными гарантиями для reinterpret_cast) связаны с поддержкой эффективности.

Это было бы нарушением духа языка при соблюдении буквы.

Ура и чт.

person Cheers and hth. - Alf    schedule 16.01.2011

Разница между типами char и unsigned char заключается просто в семантике данных. Это влияет только на то, как компилятор выполняет арифметические действия с элементами данных любого типа. Тип char сигнализирует компилятору, что значение старшего бита должно интерпретироваться как отрицательное, поэтому компилятор должен выполнить арифметику с дополнением до двух. Поскольку это единственное различие между двумя типами, я не могу представить сценарий, в котором reinterpret_cast <unsigned char*> (mydata) будет генерировать выходные данные, отличные от (unsigned char*) mydata. Более того, нет смысла копировать данные, если вы просто сообщаете компилятору об изменении сематики данных, т. е. о переключении со знаковой на беззнаковую арифметику.

EDIT: Хотя вышеизложенное верно с практической точки зрения, я должен отметить, что в стандарте C++ указано, что char, unsigned char и sign char — это три разных типа данных. § 3.9.1.1:

Объекты, объявленные как символы (char), должны быть достаточно большими, чтобы хранить любой элемент базового набора символов реализации. Если символ из этого набора хранится в символьном объекте, интегральное значение этого символьного объекта равно значению односимвольной литеральной формы этого символа. Реализация определяет, может ли объект char содержать отрицательные значения. Символы могут быть явно объявлены беззнаковыми или подписанными. Обычный символ, знаковый символ и беззнаковый символ — это три различных типа, которые вместе называются узкими символьными типами. Символ, знаковый символ и беззнаковый символ занимают один и тот же объем памяти и имеют одинаковые требования к выравниванию (3.11); то есть они имеют одно и то же объектное представление. Для узких типов символов все биты представления объекта участвуют в представлении значения. Для узких типов символов без знака все возможные битовые комбинации представления значения представляют числа. Эти требования не распространяются на другие типы. В любой конкретной реализации простой объект char может принимать те же значения, что и подписанный char, или неподписанный char; какой из них определяется реализацией.

person ThomasMcLeod    schedule 17.01.2011

Идите с гипсом, это нормально на практике.

Я просто хочу добавить, что это:

for (size_t i = 0; i < mydatalen; ++i)
  mydata_copy[i] = static_cast<unsigned char>(mydata[i]);

не являясь неопределенным поведением, может изменить содержимое вашей строки на машинах без арифметики с дополнением до 2. Обратное поведение было бы неопределенным.

person jpalecek    schedule 17.01.2011
comment
Нет необходимости в явном цикле, когда он уже находится в C++/STL #include <algorithm>: std::copy(mydata, mydata + mydatalen, mydata_copy). - person ephemient; 17.01.2011

Для совместимости с C типы unsigned char* и char* имеют дополнительные ограничения. Смысл в том, что такие функции, как memcpy(), должны работать, а это ограничивает свободу компиляторов. (unsigned char*) &foo должен по-прежнему указывать на объект foo. Поэтому не беспокойтесь в этом конкретном случае.

person MSalters    schedule 17.01.2011