Быстрое чтение памяти: "подписанный char *" vs "unsigned char *"
Часто нужно читать из памяти по одному байту за раз, как в этой наивной реализации memcpy()
:
void *memcpy(void *dest, const void *src, size_t n)
{
char *from = (char *)src;
char *to = (char *)dest;
while(n--) *to++ = *from++;
return dest;
}
Однако иногда я вижу, что люди явно используют unsigned char *
вместо просто char *
.
Конечно, char
и unsigned char
могут быть не равны. Но имеет ли значение, использую ли я char *
, signed char *
или unsigned char *
, когда вы часто читаете/записываете память?
ОБНОВЛЕНИЕ: Собственно, я полностью понимаю, что c=200
может иметь разные значения в зависимости от типа c
. Я спрашиваю здесь, почему люди иногда используют unsigned char *
вместо просто char *
при чтении памяти, например. чтобы сохранить uint32_t
в char[4]
.
Ответы
Ответ 1
Вы должны использовать unsigned char
. В стандарте C99 говорится, что unsigned char
является единственным типом, который гарантированно будет плотным (без битов заполнения), а также определяет, что вы можете скопировать любой объект (за исключением битовых полей) точно, скопировав его в массив unsigned char
, который является объектом представление в байтах.
Разумная интерпретация этого для меня заключается в том, что если вы используете указатель для доступа к объекту в виде байтов, вы должны использовать unsigned char
.
Ссылка: http://blackshell.com/~msmud/cstd.html#6.2.6.1 (из C1x draft C99)
Ответ 2
Это одна точка, где С++ отличается от C. Вообще говоря, только C
гарантирует, что доступ к необработанной памяти работает для unsigned char
; char
может
быть подписанным, а на 1 дополняющей или подписанной машине величин a -0
может быть автоматически преобразовано в +0, изменив битовый шаблон. Для
по какой-то причине (неизвестно мне) комитет С++ расширяет гарантии
поддерживая прозрачную копию (без изменения битовых паттернов) до char
, так как
а также unsigned char
; на 1 дополняющую или подписанную величину
машины, у разработчиков нет выбора, кроме как сделать простой char
без знака, чтобы избежать таких побочных эффектов. (И, конечно, большинство
программисты сегодня не интересуются такими машинами в любом случае.)
В любом случае, конечный результат заключается в том, что более старые программисты, которые приходят из C
(и, возможно, фактически работал над 1 дополнением или
знаковая машина) автоматически будет использовать unsigned char
. Это
также частое соглашение о резервировании plain char
для символьных данных
однозначно, при signed char
для очень малых интегральных значений и
unsigned char
для необработанной памяти или когда используется бит-манипуляция.
Такое правило позволяет читателю различать различные виды использования
(при условии, что это соблюдается религиозно).
Ответ 3
В вашем примере кода это не имеет значения. Но если вы хотите отобразить/распечатать значение байта, чем оно (поскольку старший бит интерпретируется по-разному), а unsigned char
кажется более подходящим
Ответ 4
Это зависит от того, что вы хотите сохранить в char.
Подписанный char дает диапазон от -127 до 127, тогда как unsigned char находится в диапазоне от 0 до 255.
Для арифметики указателя это не имеет значения.
Ответ 5
#include<stdio.h>
#include<string.h>
int main()
{
unsigned char a[4]={254,254,254,'\0'};
unsigned char b[4];
char c[4];
memset(b,0,4);
memset(c,0,4);
memcpy(b,a,4);
memcpy(c,a,4);
int i;
for(i=0;i<4;i++)
{
printf("\noriginal is %d",a[i]);
printf("\nchar %d is %d",i,c[i]);
printf("\nunsigned char %d is %d \n\n",i,b[i]);
}
}
вывод
original is 254
char 0 is -2
unsigned char 0 is 254
original is 254
char 1 is -2
unsigned char 1 is 254
original is 254
char 2 is -2
unsigned char 2 is 254
original is 0
char 3 is 0
unsigned char 3 is 0
поэтому здесь char и unsign оба имеют одинаковое значение, поэтому в данном случае это не имеет значения
Изменить
, если вы прочтете что-либо в качестве подписанного char, все же в этом случае большинство бит высокого уровня также будет скопирован, поэтому не имеет значения