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
> 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 бита ширины и передаются
> соответственно в полную ширину регистра.
Спасибо, очень интересно, буду знать!
> Вот например такой код:
>
> printf("%ld %ld\n", bv, cid, cid);
>
> выдаст варнинг насчет лишнего аргумента, но значение cid не выведет,
> хотя в стеке аргументов место для этого второго cid есть.
Эм, "с прямым углом перепутала", надо было так:
printf("%ld %ld %ld\n", bv, cid);
и так реальный cid выводится.
Никто не обещал. Если хочется писать переносимо и с гарантией работы, проще всего делать так:
printf("%jd %ju\n", (intmax_t)longvalue, (intmax_t)unsignedlong);
Пардон, очепятка, надо так:
printf("%jd %ju\n", (intmax_t)longvalue, (uintmax_t)unsignedlong);
> 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);
То что нужно, спасибо!
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-
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-
> В <inttypes.h> есть макры типа PRId64.
> https://pubs.opengroup.org/onlinepubs/009696899/basedefs/inttypes.h.html
> Для типов точной размерности можно их использовать.
> [u]intmax_t, конечно, тоже сработает... в принципе printf и так
> дорогой, это ничего существенно не добавит к его цене.
Вообще-то я была бы только за, если б типы точной размерности были сразу родные в C.
Например, для переменной-битвектора не важно, сколькибитная там система,
если в ней нужно хранить 32 флага, то незачем ее авторасширять на 64битной системе,
как это происходит, если ее тип long.
То же с членами массива, ведь от авторасширения массив распухнет вдвое.
Большое спасибо за детальное раскрытие темы.
Я пробовал и так, и так. Через %jd гораздо удобнее в тех случаях, когда цена принта неважна.