LINUX.ORG.RU

printf ( «%x\n», -15 );


0

0

В программе на си есть команды:

char I=-15;
printf ( «%x\n», I );

Я ожидал, что printf выведет на экран «f1», вместо этого получил «fffffff1».

Это ошибка, или так и предусмотрено стандартом си?

Сколько реально байт занимает переменная I: 1 или 4?

Что сделать, чтобы 1- и 2-байтные переменные выводились в шестнадцатеричном виде в виде 2- и 4-значных чисел соответственно?

Заранее спасибо.

★★★★★

Сколько реально байт занимает переменная I: 1 или 4?

Переменная занимает теоретически 1 байт (на практике минимум 2).

Это ошибка, или так и предусмотрено стандартом си?

Это не ошибка. Скорее всего gcc сам приводит типы. Но вообще так писать нельзя. Потому что по стандарту компилятор не обязан ничего делать с printf, так что должен вывести какой-то мусор.

Что сделать, чтобы 1- и 2-байтные переменные выводились в шестнадцатеричном виде в виде 2- и 4-значных чисел соответственно?

Для 2-байтных — %hx. Для 1-байтных — можно сделать так:

printf("%h", (unsigned char)I);

(собственно этот вариант и для short подходит.

vkos ★★ ()

Перед вызовом происходит конвертация в int.
Если по умолчанию (зависит от архитектуры, но чаще) char == signed char,
то получим что видим, т.ч. можно считать по стандарту.
Если использовать unsigned char, то получим желаемое.

io ★★ ()
Ответ на: комментарий от vkos

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

Хотя, может, в данном случае просто преобразовывает вне зависимости от printf, но всё равно так лучше не писать.

vkos ★★ ()
Ответ на: комментарий от vkos

> Переменная занимает теоретически 1 байт (на практике минимум 2).

Зачем нужен второй байт? Как он располагается в массивах и структурах?

Пример. Есть массив char s[50].
Будут ли гарантированно совпадать s[4] , *(s+4) и (char)(((int*)s)[1]) ?
Будут ли s+4 и &s[4] указывать на один адрес?

Архитектура AMD64.

Нет, я такую программу не пишу. Я её читаю :)

Но вообще так писать нельзя.


Почему?

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


Не понял. В мане написано, что для %x можно давать любое целое число.

printf(«%hx», (unsigned char)I);


Спасибо, работает.

question4 ★★★★★ ()
Ответ на: комментарий от io

> Перед вызовом происходит конвертация в int.

Рад слышать, что только перед вызовом.

Если использовать unsigned char, то получим желаемое.


Спасибо. Сработало даже с

unsigned char I=-15;
printf(«%x», I);

question4 ★★★★★ ()
Ответ на: комментарий от Zodd

> Чего ты хочешь вывести-то?

Выше написал. «f1» для char I=-15.

question4 ★★★★★ ()
Ответ на: комментарий от question4

>Зачем нужен второй байт? Как он располагается в массивах и структурах?

В массивах/структурах всё нормально, а вот там где «автоматические» переменные будет как минимум два байта (может четыре, должно зависеть от компилятора). Из-за устройства стека x86. Команды push/pop с байтами не работают

vkos ★★ ()
Ответ на: комментарий от question4

>Почему?

Потому что возникают подобные вопросы у читающих код..

vkos ★★ ()
Ответ на: комментарий от vkos

> там где «автоматические» переменные будет как минимум два байта ... Из-за устройства стека x86.

Спасибо.

question4 ★★★★★ ()
Ответ на: комментарий от question4

>Пример. Есть массив char s[50].

Будут ли гарантированно совпадать s[4] , *(s+4) и (char)(((int*)s)[1]) ?

Будут ли s+4 и &s[4] указывать на один адрес?

s[4] == *(s + 4)

s + 4 == &s[4];

(char)(((int*)s)[1]) - зависит от размера int

hired777 ()
Ответ на: комментарий от question4

Она и занимает строго один байт. Остальные просто уходят на выравнивание.

satanic-mechanic ()
Ответ на: комментарий от question4

имхо, вот более корректный вариант:

#include <stdio.h>
#define sbyte(n) *((unsigned char *)(&n))
int main(int argc, char** argv)
{
	
    unsigned char I= -15;
    printf ( "%02x\n", sbyte(I));
	return 0;
}

elipse ★★★ ()

Сколько чуши насоветывали, писец просто. Никто маны не читает? Единственный правильный вариант печатать char так: «%hhx», short соответствено так: «%hx».

Reset ★★★★★ ()
Ответ на: комментарий от Reset

> Сколько чуши насоветывали

Совет пользоваться unsigned уже успел помочь :)

char так: «%hhx», short соответствено так: «%hx».

Спасибо.

question4 ★★★★★ ()
Ответ на: комментарий от elipse

Кстати, можно ли как-то задать, что все целые в программе (char, short и int, 1, 2 и 4 байт) — без знака, если явно не указано иное?

question4 ★★★★★ ()
Ответ на: комментарий от question4

> Кстати, можно ли как-то задать, что все целые в программе (char, short и int, 1, 2 и 4 байт) — без знака, если явно не указано иное?
добавить ключик у gcc -funsigned-char

elipse ★★★ ()
Ответ на: комментарий от question4

это надо уже конкретно смотреть в доки и спеки.
bc, sdcc, bso c, iar c , avrgcc 4.1 - их либы точно такое не умеют.
и авторы языка си тоже не описывали такие примочки.

elipse ★★★ ()
Ответ на: комментарий от question4

Ну это такой намек на передачу опций gcc через #pragma ? - ну именно unsigned-char выставить в дефолт невозможно было для gcc , если я тут не ошибаюсь и ничего не поменялось на последних версиях.

elipse ★★★ ()
Ответ на: комментарий от question4

> > (char)(((int*)s)[1]) - зависит от размера int

4 байта.

Что 4байта? На твоей системе/твоём компиляторе или везде? Если 2ой вариант, то заблуждаешься.

hired777 ()

Я ожидал, что printf выведет на экран «f1», вместо этого получил «fffffff1».

внимательно читаем википедию и не задаём больше таких вопросов

00000000 00000000 00000000 00001111 = 15
11111111 11111111 11111111 11110000 = inv(15)
00000000 00000000 00000000 00000001 = +1
11111111 11111111 11111111 11110001 = -15

Сколько реально байт занимает переменная I: 1 или 4?

#include <stdio.h>

#define SZ(x)   printf("%d: %s\n", sizeof(x), #x)

int
main()
{
        SZ(void *);
        SZ(char);
        SZ(short);
        SZ(int);
        SZ(long);
        SZ(long long);
        SZ(float);
        SZ(double);

        return 0;
}
beastie ★★★★★ ()
Ответ на: комментарий от beastie

а вообщето размеры ни где не оговорены и зависят от реализации

beastie ★★★★★ ()
Ответ на: комментарий от vkos

> >Это ошибка, или так и предусмотрено стандартом си?

Это называется «преобразования типов по умолчанию». И это предусмотренно стандартом

anonymous ()
Ответ на: комментарий от beastie

> http://en.wikipedia.org/wiki/Signed_number_representations

Спасибо, нас этому учили в школе.

Меня интересовало, в каком виде последние версии GCC представляют char (как char или int), на каком этапе оно преобразуется в 4-байтное при выводе, и как этого избежать. Уже ответили.

question4 ★★★★★ ()
Ответ на: комментарий от question4

> в каком виде последние версии GCC представляют char (как char или int)

char - всегда, как char, int - всегда, как int

на каком этапе оно преобразуется в 4-байтное при выводе

При «подстановке» аргументов в printf()

и как этого избежать

Никак.

Уже ответили.

printf(«%s\n», sizeof('a') == sizeof(int) ? «C» : «C++»);

anonymous ()
Ответ на: комментарий от ntp

>При передаче в printf происходит расширение до int
чисто по секрету, расширение происходит не только при передачи в prinf

xydo ★★ ()
Ответ на: комментарий от xydo

> расширение происходит не только при передачи в prinf

А где ещё? В любой функции, где параметры передаются через стек? Или и через регистры тоже? И когда GCC передаёт параметры через регистры? Только для внутренних функций?

question4 ★★★★★ ()
Ответ на: комментарий от xydo

Чисто по секрету: стоит по ссылке пройти и почитать врезки.

ntp ()
Ответ на: комментарий от question4

> В смысле? И как в этом отношении различаются 32- и 64-разрядные системы?

Если брать amd64 & x86 - то у них разные ABI. В amd64 большинство (пока отведённые регистры не кончатся) параметров передаются в регистрах. А в x86 - на стеке.

P.S. не нужно писать про -mregparm, это не стандартный ABI

anonymous ()
Ответ на: комментарий от anonymous

Спасибо.

-mregparm

И за это спасибо. Может, пригодится.

question4 ★★★★★ ()
Ответ на: комментарий от question4

В википедии всё разжёвано просто до неприличия, вплоть до примера у ТС.

LamerOk ★★★★★ ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.