int и char представлены внутри с помощью одних и тех же битов gcc? - PullRequest
2 голосов
/ 18 марта 2012

Я играл с юникод-символами (без поддержки wchar_t) просто для удовольствия.Я использую только обычный тип данных char.Я заметил, что при печати их в шестнадцатеричном виде они отображали полные 4 байта вместо одного байта.

Напримеррассмотрим этот файл c:

#include <stdio.h>
#include <stdlib.h>

int main(void)
{
    char *s = (char *) malloc(100);
    fgets(s, 100, stdin);
    while (s && *s != '\0') {
            printf("%x\n", *s);
            s++;
    }
    return 0;
}

После компиляции с помощью gcc и ввода в качестве символа 'cent' (hex: c2 a2) я получаю следующий вывод

$ ./a.out
¢
ffffffc2: ?
ffffffa2: ?
a: 

Так что вместо того, чтобы простопечатая c2 и a2 я получил целые 4 байта, как если бы это был тип int.

Значит ли это, что char на самом деле не имеет длину 1 байт, ascii сделал его похожим на 1 байт?

Ответы [ 3 ]

5 голосов
/ 18 марта 2012

Может быть, причина, по которой старшие три байта становятся 0xFFFFFF, требует немного большего объяснения?

Верхние три байта значения, напечатанного для * s, имеют значение 0xFF из-за расширения знака.

Значение char, переданное в printf, увеличивается до int перед вызовом printf.

Это связано с поведением Си по умолчанию.

При отсутствии signed или unsigned компилятор может по умолчанию интерпретировать char как signed char или unsigned char. Он последовательно один или другой, если явно не изменен с параметром командной строки или прагмой. В этом случае мы можем видеть, что это signed char.

При отсутствии дополнительной информации (прототипы или отливки) C передает:

  • int, поэтому char, short, unsigned char unsigned short преобразуются в int. Он никогда не пропускает символ, неподписанный символ, подписанный символ как один байт, он всегда пропускает int.
  • unsigned int соответствует размеру int, поэтому значение передается без изменений

Компилятор должен решить, как преобразовать меньшее значение в int.

  • signed значения: старшие байты int являются знаками, расширенными от меньшего значения, которое фактически копирует верхний знаковый бит вверх для заполнения int. Если старший бит меньшего значения со знаком равен 0, старшие байты заполнены 0. Если старший бит меньшего значения со знаком равен 1, старшие байты заполнены 1. Следовательно printf ("% x", * s ) печатает ffffffc2
  • unsigned значения не расширены знаком, старшие байты int 'дополняются нулями'

Следовательно, причина в том, что C может вызывать функцию без прототипа (хотя компилятор обычно предупреждает об этом)

Так что вы можете написать и ожидать, что это запустится (хотя я надеюсь, что ваш компилятор выдаст предупреждения):

/* Notice the include is 'removed' so the C compiler does default behaviour */
/* #include <stdio.h> */

int main (int argc, const char * argv[]) {
    signed char schar[] = "\x70\x80";
    unsigned char uchar[] = "\x70\x80";

    printf("schar[0]=%x schar[1]=%x uchar[0]=%x uchar[1]=%x\n", 
            schar[0],   schar[1],   uchar[0],   uchar[1]);
    return 0;
}

Это печатает:

schar[0]=70 schar[1]=ffffff80 uchar[0]=70 uchar[1]=80

Значение char интерпретируется моим компилятором (gcc) для Mac как signed char, поэтому компилятор генерирует код для подписи расширенного char до int перед вызовом printf.

Если значение знака со знаком имеет установленный старший (знаковый) бит (\ x80), преобразование в знак int расширяет значение char. Расширение знака заполняет верхние байты (в данном случае еще 3 байта, чтобы получить 4 байта int) единицами, которые печатаются printf как ffffff80

Если значение знака со знаком имеет верхний бит (знак), очищенный (\ x70), преобразование в int все еще расширяет значение char. В этом случае знак равен 0, поэтому расширение знака заполняет старшие байты нулями, которые printf печатает как 70

В моем примере показан случай, когда значение равно unsigned char. В этих двух случаях значение не является расширенным знаком, поскольку оно равно unsigned. Вместо этого они расширяются до int с 0 дополнением. Может показаться, что printf печатает только один байт, потому что три соседних байта значения будут равны 0. Но он печатает весь int, бывает, что это значение 0x00000070 и 0x00000080, потому что значения unsigned char были преобразованы в int без расширения знака.

Вы можете заставить printf печатать только младший байт целого числа, используя подходящее форматирование (% hhx), поэтому это правильно печатает только значение в исходном символе:

/* Notice the include is 'removed' so the C compiler does default behaviour */
/* #include <stdio.h> */

int main (int argc, const char * argv[]) {
    char schar[] = "\x70\x80";
    unsigned char uchar[] = "\x70\x80";

    printf("schar[0]=%hhx schar[1]=%hhx uchar[0]=%hhx uchar[1]=%hhx\n", 
           schar[0],   schar[1],   uchar[0],   uchar[1]);
    return 0;
}

Это печатает:

schar[0]=70 schar[1]=80 uchar[0]=70 uchar[1]=80

потому что printf интерпретирует% hhx для обработки int как unsigned char. Это не меняет того факта, что символ был расширен до int перед вызовом printf. Это только способ сообщить printf, как интерпретировать содержимое int.

В некотором смысле, для signed char *schar значение %hhx выглядит немного вводящим в заблуждение, но формат '% x' все равно интерпретирует int как unsigned, и (с моим printf) формат не существует выведите hex для знаковых значений (ИМХО это было бы сбивающим с толку).

К сожалению, ISO / ANSI / ... не публикуют свободно наши стандарты языков программирования, поэтому я не могу указать на спецификацию, но поиск в Интернете может привести к рабочим черновикам. Я не пытался их найти. Я бы порекомендовал «C: Справочное руководство» Сэмюэля П. Харбисона и Гая Л. Стила в качестве более дешевой альтернативы документу ИСО.

НТН

4 голосов
/ 18 марта 2012

Нет.printf - функция переменного аргумента, аргументы функции переменного аргумента будут преобразованы в int.И в этом случае символ был отрицательным, поэтому знак расширяется.

1 голос
/ 18 марта 2012

%x сообщает printf, что значение для печати является беззнаковым int.Таким образом, он переводит char в unsigned int, при необходимости расширяя знак, а затем распечатывает полученное значение.

...