C: gcc неявно преобразует подписанный символ в неподписанный и наоборот? - PullRequest
9 голосов
/ 01 сентября 2011

Я пытаюсь узнать, что C застрял с размерами данных в данный момент.

Посмотрите на этот фрагмент кода:

#include <stdio.h>
#include <limits.h>

int main() {
    char a = 255;
    char b = -128;
    a = -128;
    b = 255;
    printf("size: %lu\n", sizeof(char));
    printf("min: %d\n", CHAR_MIN);
    printf("max: %d\n", CHAR_MAX);
}

Вывод printf:

size: 1
min: -128
max: 127

Как это возможно? Размер символа составляет 1 байт, и символ по умолчанию, кажется, подписан (-128 ... 127). Итак, как я могу присвоить значение> 127 без предупреждения о переполнении (которое я получаю, когда пытаюсь назначить -128 или 256)? Gcc автоматически конвертирует в unsigned char? И потом, когда я присваиваю отрицательное значение, оно конвертируется обратно? Почему это так? Я имею в виду, что вся эта неявность не облегчит понимание.

EDIT:

Хорошо, это ничего не конвертирует:

char a = 255;
char b = 128;
printf("%d\n", a);    /* -1 */
printf("%d\n", b);    /* -128 */

Так что он начинает считать снизу вверх. Но почему компилятор не выдает мне предупреждение? И почему так, когда я пытаюсь назначить 256?

Ответы [ 3 ]

8 голосов
/ 01 сентября 2011

См. 6.3.1.3/3 в Стандарт C99

... новый тип подписан, и значение не может быть представлено в нем;либо результат определяется реализацией, либо генерируется определяемый реализацией сигнал.

Итак, если вы не получаете сигнал (если ваша программа не останавливается), прочитайте документацию для вашего компилятора.чтобы понять, что он делает.


gcc документирует поведение (в http://gcc.gnu.org/onlinedocs/gcc/Integers-implementation.html#Integers-implementation) как

  • Результат или сигналВозникает путем преобразования целого числа в целочисленный тип со знаком, когда значение не может быть представлено в объекте этого типа (C90 6.2.1.2, C99 6.3.1.3).

Для преобразования втип ширины N, значение уменьшается по модулю 2 ^ N, чтобы быть в пределах диапазона типа;сигнал не подается.

4 голосов
/ 01 сентября 2011

как я могу присвоить значение> 127

Результатом преобразования целочисленного значения вне диапазона в целочисленный тип со знаком является либо результат, определенный реализацией, либо реализация-определенный сигнал (6.3.1.3/3).Таким образом, ваш код является допустимым C, он просто не имеет одинакового поведения во всех реализациях.

без получения предупреждения о переполнении

Решать GCC должен полностьюстоит ли предупреждать или нет о действительном коде.Я не совсем уверен, каковы его правила, но я получаю предупреждение за инициализацию signed char с 256, но не с 255.Я предполагаю, что это потому, что предупреждение для кода типа char a = 0xFF обычно не требуется программисту, даже если char подписан.Существует проблема переносимости, связанная с тем, что тот же код на другом компиляторе может вызвать сигнал или привести к значению 0 или 23.

-pedantic включает предупреждение об этом (спасибо, pmg), что имеет смысл, поскольку -pedantic предназначен для помощи в написании переносимого кода.Или, возможно, не имеет смысла, поскольку, как указывает R .., это выходит за рамки простого перевода компилятора в режим стандартного соответствия.Однако на странице руководства для gcc сказано, что -pedantic включает диагностику, требуемую стандартом.Это не так, но на странице руководства также написано:

Некоторые пользователи пытаются использовать -pedantic для проверки программ на предмет строгого соответствия ISO C.Вскоре они обнаруживают, что он делает не совсем то, что им нужно: он находит некоторые практики, отличные от ISO, но не все - только те, для которых ISO C требует диагностики, и некоторые другие, для которых диагностика была добавлена.

Это заставляет меня задуматься о том, что такое "практика, отличная от ISO", и подозревать, что char a = 255 является одним из тех случаев, для которых диагностика была специально добавлена.Конечно, «не-ISO» означает больше, чем просто вещи, для которых стандарт требует диагностики, но gcc, очевидно, не заходит так далеко, чтобы диагностировать весь не строго соответствующий код такого типа.

Я также получаюпредупреждение для инициализации int с ((long long)UINT_MAX) + 1, но не с UINT_MAX.Похоже, что по умолчанию gcc последовательно дает вам первую степень 2 бесплатно, но после этого он думает, что вы допустили ошибку.

Используйте -Wconversion, чтобы получить предупреждение обо всех этих инициализациях, включаяchar a = 255.Остерегайтесь, это даст вам множество других предупреждений, которые вы можете или не хотите.

вся эта неявность не облегчит понимание

Вы будетеЯ должен обсудить это с Деннисом Ричи.C слабо типизирован в отношении арифметических типов.Все они неявно преобразуются друг в друга с различными уровнями плохого поведения, когда значение выходит за пределы диапазона в зависимости от используемых типов.Опять же, -Wconversion предупреждает об опасных.

Есть и другие конструктивные решения в C, которые означают, что слабость очень важна, чтобы избежать громоздкого кода.Например, тот факт, что арифметика всегда выполняется по крайней мере в int, означает, что char a = 1, b = 2; a = a + b включает в себя неявное преобразование из int в char, когда результат сложения присваивается a.Если вы используете -Wconversion, или если у C вообще не было неявного преобразования, вы должны написать a = (char)(a+b), что не было бы слишком популярно.В этом отношении char a = 1 и даже char a = 'a' являются неявными преобразованиями из int в char, поскольку в C нет литералов типа char.Так что, если бы не все эти неявные преобразования, либо разные другие части языка должны были бы быть разными, иначе вам пришлось бы полностью засорять ваш код приведениями.Некоторые программисты хотят строгой типизации, что достаточно справедливо, но вы не получаете ее в C.

0 голосов
/ 01 сентября 2011

Простое решение :

см. Char со знаком может иметь значение от -128 до 127 так что теперь, когда вы назначаете 129 любому значению символа, это займет 127 (это действительно) + 2 (это дополнительно) = -127
(дать символу = 129 и вывести его значение равно -127)

посмотрите, что регистр char может иметь значение как ... 126127, -128, -127, -126 ...- 1,0,1,2 ....

которое вы когда-нибудь назначите окончательное значение, придет из этого расчета ... !!

...