[freebsd] printf в clang

7 views
Skip to first unread message

sp...@itl.ua

unread,
Dec 30, 2021, 4:15:49 PM12/30/21
to fre...@uafug.org.ua


Приветствую сообщество.

Подскажите, а это тянет на баг, или никто ничего в таком случае не обещал?

Есть код:

int64_t bv;
long cid;
bv = ( (int64_t) 1 << 33) + 3;
cid = 111;
printf("%ld %ld\n", bv, cid);

clang резонно выдает варнинг о несоответствии типа первого аргумента (%ld вместо правильного %lld).
А вот вывод этой программы менее ожидаем:

3 2

Первое число ожидаемо - младшие 4 байта от bv.
А второе, вместо значения cid - старшие 4 байта от bv.

clang version 10.0.1.

Valentin Nechayev

unread,
Dec 30, 2021, 4:39:14 PM12/30/21
to sp...@itl.ua, fre...@uafug.org.ua
hi,

Thu, Dec 30, 2021 at 21:15:36, spell wrote about "[freebsd] printf в clang":

> int64_t bv;
> long cid;
> bv = ( (int64_t) 1 << 33) + 3;
> cid = 111;

> printf("%ld %ldn", bv, cid);

Тут наверно должно было быть \n в конце форматной строки.

> clang резонно выдает варнинг о несоответствии типа первого аргумента (%ld вместо правильного %lld).

Компиляция в 32 битах, я так понимаю? Потому что в 64 проблем не
видно.

> А вот вывод этой программы менее ожидаем:
>
> 3 2
>
> Первое число ожидаемо - младшие 4 байта от bv.
> А второе, вместо значения cid - старшие 4 байта от bv.
>
> clang version 10.0.1.

Именно что если в 32 битах, то всё логично, потому что:
1. При передаче в переменном наборе аргументов (как printf) происходит
расширение всех целочисленных типов, которые у́же int, к int, а более
широкие не меняются.
2. Соглашение о вызове предусматривает передачу всех аргументов на
стеке. Соответственно в позиции начала переменной области аргументов
укладывается: 03 00 00 00 02 00 00 00 (bv) 6f 00 00 00 (cid).
3. printf согласно формату %ld извлекает long, который в этом режиме
32 бита (равен int). Извлекается 03 00 00 00 (значение 3).
Второй %ld извлекает 02 00 00 00 (значение 2).

В 64 битах проблемы не будет, потому что:

1. Первые 6 аргументов передаются в регистрах, включая переменные
аргументы (! - из-за этого va_arg c компанией заметно усложняется).
Форматная строка будет в rdi, bv - в rsi, cld - в rdx.
2. long и long long одинаково имеют 64 бита ширины и передаются
соответственно в полную ширину регистра.

Хорошо, что оно ворнинги пишет :))


-netch-
_______________________________________________
freebsd mailing list
fre...@uafug.org.ua
http://mailman.uafug.org.ua/mailman/listinfo/freebsd

sp...@itl.ua

unread,
Dec 31, 2021, 3:20:52 AM12/31/21
to Valentin Nechayev, fre...@uafug.org.ua
30 декабря 2021 г., 23:39, "Valentin Nechayev" <ne...@netch.kiev.ua> написал:

> hi,
>
> Thu, Dec 30, 2021 at 21:15:36, spell wrote about "[freebsd] printf в clang":
>
>> int64_t bv;
>> long cid;
>> bv = ( (int64_t) 1 << 33) + 3;
>> cid = 111;
>> printf("%ld %ldn", bv, cid);
>
> Тут наверно должно было быть \n в конце форматной строки.

опечатка :(

>> clang резонно выдает варнинг о несоответствии типа первого аргумента (%ld вместо правильного %lld).
>
> Компиляция в 32 битах, я так понимаю? Потому что в 64 проблем не
> видно.

да.

>> А вот вывод этой программы менее ожидаем:
>>
>> 3 2
>>
>> Первое число ожидаемо - младшие 4 байта от bv.
>> А второе, вместо значения cid - старшие 4 байта от bv.
>>
>> clang version 10.0.1.
>
> Именно что если в 32 битах, то всё логично, потому что:
> 1. При передаче в переменном наборе аргументов (как printf) происходит
> расширение всех целочисленных типов, которые у́же int, к int, а более
> широкие не меняются.
> 2. Соглашение о вызове предусматривает передачу всех аргументов на
> стеке. Соответственно в позиции начала переменной области аргументов
> укладывается: 03 00 00 00 02 00 00 00 (bv) 6f 00 00 00 (cid).
> 3. printf согласно формату %ld извлекает long, который в этом режиме
> 32 бита (равен int). Извлекается 03 00 00 00 (значение 3).
> Второй %ld извлекает 02 00 00 00 (значение 2).

Так и предположила.
Собственно, вопрос - это считается за баг, или "сам виноват"?
Варнинг, конечно, это хорошо, но и обработка ситуации тоже важна.
Вот например такой код:

printf("%ld %ld\n", bv, cid, cid);

выдаст варнинг насчет лишнего аргумента, но значение cid не выведет,
хотя в стеке аргументов место для этого второго cid есть.

> В 64 битах проблемы не будет, потому что:
>
> 1. Первые 6 аргументов передаются в регистрах, включая переменные
> аргументы (! - из-за этого va_arg c компанией заметно усложняется).
> Форматная строка будет в rdi, bv - в rsi, cld - в rdx.
> 2. long и long long одинаково имеют 64 бита ширины и передаются
> соответственно в полную ширину регистра.

Спасибо, очень интересно, буду знать!

sp...@itl.ua

unread,
Dec 31, 2021, 3:32:32 AM12/31/21
to Valentin Nechayev, fre...@uafug.org.ua
31 декабря 2021 г., 10:20, sp...@itl.ua написал:

> Вот например такой код:
>
> printf("%ld %ld\n", bv, cid, cid);
>
> выдаст варнинг насчет лишнего аргумента, но значение cid не выведет,
> хотя в стеке аргументов место для этого второго cid есть.

Эм, "с прямым углом перепутала", надо было так:

printf("%ld %ld %ld\n", bv, cid);

и так реальный cid выводится.

Eugene Grosbein

unread,
Dec 31, 2021, 3:52:15 AM12/31/21
to sp...@itl.ua, fre...@uafug.org.ua
31.12.2021 4:15, sp...@itl.ua пишет:

>
>
> Приветствую сообщество.
>
> Подскажите, а это тянет на баг, или никто ничего в таком случае не обещал?

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

printf("%jd %ju\n", (intmax_t)longvalue, (intmax_t)unsignedlong);

Eugene Grosbein

unread,
Dec 31, 2021, 3:53:13 AM12/31/21
to sp...@itl.ua, fre...@uafug.org.ua
31.12.2021 15:51, Eugene Grosbein пишет:

> 31.12.2021 4:15, sp...@itl.ua пишет:
>>
>>
>> Приветствую сообщество.
>>
>> Подскажите, а это тянет на баг, или никто ничего в таком случае не обещал?
>
> Никто не обещал. Если хочется писать переносимо и с гарантией работы, проще всего делать так:
>
> printf("%jd %ju\n", (intmax_t)longvalue, (intmax_t)unsignedlong);

Пардон, очепятка, надо так:

printf("%jd %ju\n", (intmax_t)longvalue, (uintmax_t)unsignedlong);

sp...@itl.ua

unread,
Dec 31, 2021, 4:43:39 AM12/31/21
to Eugene Grosbein, fre...@uafug.org.ua
31 декабря 2021 г., 10:52, "Eugene Grosbein" <eu...@grosbein.net> написал:

> 31.12.2021 15:51, Eugene Grosbein пишет:
>
>> 31.12.2021 4:15, sp...@itl.ua пишет:
>>> Приветствую сообщество.
>>>
>>> Подскажите, а это тянет на баг, или никто ничего в таком случае не обещал?
>>
>> Никто не обещал. Если хочется писать переносимо и с гарантией работы, проще всего делать так:
>>
>> printf("%jd %ju\n", (intmax_t)longvalue, (intmax_t)unsignedlong);
>
> Пардон, очепятка, надо так:
>
> printf("%jd %ju\n", (intmax_t)longvalue, (uintmax_t)unsignedlong);

То что нужно, спасибо!

Valentin Nechayev

unread,
Dec 31, 2021, 4:48:26 AM12/31/21
to Eugene Grosbein, fre...@uafug.org.ua
hi,

Fri, Dec 31, 2021 at 15:51:50, eugen wrote about "Re: [freebsd] printf в clang":

> > Подскажите, а это тянет на баг, или никто ничего в таком случае не обещал?
>
> Никто не обещал. Если хочется писать переносимо и с гарантией работы, проще всего делать так:
>
> printf("%jd %ju\n", (intmax_t)longvalue, (intmax_t)unsignedlong);

В <inttypes.h> есть макры типа PRId64.
https://pubs.opengroup.org/onlinepubs/009696899/basedefs/inttypes.h.html
Для типов точной размерности можно их использовать.
[u]intmax_t, конечно, тоже сработает... в принципе printf и так
дорогой, это ничего существенно не добавит к его цене.


-netch-

Valentin Nechayev

unread,
Dec 31, 2021, 4:53:41 AM12/31/21
to sp...@itl.ua, fre...@uafug.org.ua
hi,

Fri, Dec 31, 2021 at 08:20:44, spell wrote about "Re: [freebsd] printf в clang":

> > Именно что если в 32 битах, то всё логично, потому что:
> > 1. При передаче в переменном наборе аргументов (как printf) происходит
> > расширение всех целочисленных типов, которые у́же int, к int, а более
> > широкие не меняются.
> > 2. Соглашение о вызове предусматривает передачу всех аргументов на
> > стеке. Соответственно в позиции начала переменной области аргументов
> > укладывается: 03 00 00 00 02 00 00 00 (bv) 6f 00 00 00 (cid).
> > 3. printf согласно формату %ld извлекает long, который в этом режиме
> > 32 бита (равен int). Извлекается 03 00 00 00 (значение 3).
> > Второй %ld извлекает 02 00 00 00 (значение 2).
>
> Так и предположила.
> Собственно, вопрос - это считается за баг, или "сам виноват"?

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

> Варнинг, конечно, это хорошо, но и обработка ситуации тоже важна.
> Вот например такой код:
>
> printf("%ld %ld\n", bv, cid, cid);
>
> выдаст варнинг насчет лишнего аргумента, но значение cid не выведет,
> хотя в стеке аргументов место для этого второго cid есть.

Да, потому что не сказано его печатать. А если будет, наоборот, лишний
%ld без параметра - выведется что-то со стека (условно, мусор).

В C++ это фиксили в основных средствах (iostreams, boost::format...) -
там есть контроль типов и параметры ввода-вывода устанавливаются во
время компиляции. В C такое как-то не завезли.


-netch-

sp...@itl.ua

unread,
Dec 31, 2021, 5:13:29 AM12/31/21
to Valentin Nechayev, Eugene Grosbein, fre...@uafug.org.ua
31 декабря 2021 г., 11:48, "Valentin Nechayev" <ne...@netch.kiev.ua> написал:

> В <inttypes.h> есть макры типа PRId64.
> https://pubs.opengroup.org/onlinepubs/009696899/basedefs/inttypes.h.html
> Для типов точной размерности можно их использовать.
> [u]intmax_t, конечно, тоже сработает... в принципе printf и так
> дорогой, это ничего существенно не добавит к его цене.

Вообще-то я была бы только за, если б типы точной размерности были сразу родные в C.
Например, для переменной-битвектора не важно, сколькибитная там система,
если в ней нужно хранить 32 флага, то незачем ее авторасширять на 64битной системе,
как это происходит, если ее тип long.
То же с членами массива, ведь от авторасширения массив распухнет вдвое.

Большое спасибо за детальное раскрытие темы.

Eugene Grosbein

unread,
Dec 31, 2021, 5:28:42 AM12/31/21
to Valentin Nechayev, fre...@uafug.org.ua
31.12.2021 16:48, Valentin Nechayev пишет:

> hi,
>
> Fri, Dec 31, 2021 at 15:51:50, eugen wrote about "Re: [freebsd] printf в clang":
>
>>> Подскажите, а это тянет на баг, или никто ничего в таком случае не обещал?
>>
>> Никто не обещал. Если хочется писать переносимо и с гарантией работы, проще всего делать так:
>>
>> printf("%jd %ju\n", (intmax_t)longvalue, (intmax_t)unsignedlong);
>
> В <inttypes.h> есть макры типа PRId64.
> https://pubs.opengroup.org/onlinepubs/009696899/basedefs/inttypes.h.html
> Для типов точной размерности можно их использовать.
> [u]intmax_t, конечно, тоже сработает... в принципе printf и так
> дорогой, это ничего существенно не добавит к его цене.

Я пробовал и так, и так. Через %jd гораздо удобнее в тех случаях, когда цена принта неважна.

Reply all
Reply to author
Forward
0 new messages