LINUX.ORG.RU

Переносимость спецификаторов функций *printf

 ,


0

3

Есть некий код

uint32_t value;
...
printf( "FATAL: Unknown security result: %u\n", value );

Под одну архитектуру это собирается без ошибок. Под другую сборка падает с ошибкой вида

format '%u' expects argument of type 'unsigned int', but argument 2 has type 'uint32_t' {aka 'long unsigned int'}

Как быть в случаях, когда подобный код должен собираться под разные архитектуры? Заворачивать всё в препроцессор или засучить рукава и писать свою нестандартную библиотеку си?

★★★

Последнее исправление: u5er (всего исправлений: 2)

Переносомть

Кто такой, чем знаменит?

По сути проблемы - для интов точной величины есть свои костылики для printf, а ты у себя пытаешься подставить те, которые для обычных. См. inttypes.h.

Bfgeshka ★★★★★
()
Последнее исправление: Bfgeshka (всего исправлений: 3)

Fixed-width Integer Types (from <inttypes.h>):
For types like int32_t, uint64_t, etc., defined in <inttypes.h>, specific macros are provided for printf to ensure correct formatting across different systems.

  • PRIi32: For printing int32_t.
  • PRIu64: For printing uint64_t.
#include <stdio.h>
#include <inttypes.h> // Required for PRI macros

int main() {
    int32_t my_signed_int = -12345;
    uint64_t my_unsigned_long_long = 123456789012345ULL;

    // Using PRI macros for portable printing
    printf("Signed 32-bit integer: %" PRId32 "\n", my_signed_int);
    printf("Unsigned 64-bit integer: %" PRIu64 "\n", my_unsigned_long_long);

    return 0;
}
xDShot ★★★★★
()

или засучить рукава и писать свою нестандартную библиотеку си?

Если решите засучивать рукава, то лучше использовать что-то посовременнее, которое даст ещё и возможность корректной локализации (если надо) строк - я имею ввиду возможность поменять местами аргументы в переведенной строке для другого языка.

blex ★★★★
()

Если речь о MinGW, то там есть ключик, позволяющий использовать свой printf вместо системного

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

Признаюсь, я не знал об этом ужасе.

#include <stdio.h>
#include <inttypes.h>

int
main()
{
        uint32_t value = 42;
        printf("FATAL: Unknown security result: %" PRIu32 "\n", value);
        return 0;
}
beastie ★★★★★
()
Ответ на: комментарий от annulen

Если речь о MinGW

Нет. Конкретно в данном случае речь идёт про amd64 и Xtensa, но с этой проблемой сталкивался и до этого много раз. Поскольку в гугле искать не умею, то поиски ни к чему не приводили, поэтому тупо правил код и забивал. В этот раз окончательно достало и решил попросить помощи у коллективного разума.

u5er ★★★
() автор топика

Как быть в случаях, когда подобный код должен собираться под разные архитектуры?

Почитать про особенности реализации стандартной библиотеки и разных сборках библиотеки под разные архитектуры/платформы. В настройках сборки ПО сконфигурировать правильные нужные.

или засучить рукава и писать свою нестандартную библиотеку си?

Больше велосипедов богу велосипедов! Собери сам свой набор костылей (чтобы везде где тебе надо работало).

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

возможность поменять местами аргументы

printf умеет:

#include <inttypes.h>
#include <stdio.h>
#include <stdint.h>

int main(void) {
  uint32_t u32 = 0x87'56'43'21;
  int64_t i64 = 0x87'56'43'21'23'45'67'89;
  printf("i64=%2$" PRId64 "\nu32=%1$" PRIu32 "\n", u32, i64);
}
vbr ★★★★★
()

1) В конкретном случае на этот варнинг можно полностью забить - он никогда, ни при каких обстоятельствах не приведёт к поломке, максимум что может случиться - на 16-битной архитектуре обрежется при печати старшая половина переменной.

2) В рамках дефолтного printf-а есть два способа убрать варнинг и устранить даже ту мелкую опасность что описана выше:

2а) поставить %lu и тайпкастить переменную в unsigned long

2б) использовать макрос PRIu32 как выше советуют

но на мой взгляд оба способа некрасивые

3) Способ, который использую я: написал свой диалект printf-а, в нём uint8 uint16 uint32 uint64 кодируются как %HHu %Hu %Lu %LLu (это не единственная причина его писать была), а stdio.h пользоваться крайне избегаю (тоже не единственная причина).

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

которое даст ещё и возможность корректной локализации (если надо) строк - я имею ввиду возможность поменять местами аргументы в переведенной строке для другого языка.

На мой взгляд сомнительная идея объединять форматированную печать с перестановкой аргументов для локализации. Ты не представляешь, сколько лишних (и почти никогда не нужных) действий приходится делать внутри printf-а ради того, чтобы поддержать эту самую перестановку. Запутанность его логики кратно вырастает. Лучше от этого отказаться.

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

firkax ★★★★★
()
Ответ на: комментарий от firkax
  1. Способ, который использую я: написал свой диалект printf-а, в нём uint8 uint16 uint32 uint64 кодируются как %HHu %Hu %Lu %LLu (это не единственная причина его писать была), а stdio.h пользоваться крайне избегаю (тоже не единственная причина).

Если писать свой print, можно на макросах и генериках написать нормальный типобезопасный код без всяких модификаторов.

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

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

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)
Ответ на: комментарий от firkax

Я препятствий не вижу.

Можно сделать буквально полный аналог printf, но типобезопасный. PRINTF("arg1=%d arg2=%u arg3=%s", arg1, arg2, arg3) будет раскрываться в printf_begin(str, 3); printf_arg_int(arg1); printf_arg_unsigned(arg2); printf_arg_charptr(arg3); printf_end(), а дальше никаких технических препятствий нет реализовать всё типобезопасно без UB.

Можно сделать PRINT("arg1=", arg1, " arg2=", arg2) тоже типобезопасный.

Можно сделать PRINTF("arg1={} arg2={}", arg1, arg2).

Как угодно можно делать.

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 1)
Ответ на: комментарий от vbr

Ты с C++ не перепутал? Строковые литералы ты макросами не распарсишь никак. Да и представь что будет если строка формата - не литерал вообще, а переменная.

Что касается generic-ов, то у них заранее ограниченный набор типов и это всё придётся мусорить в хедеры (не надо этот C++-ный приём в Си тащить).

firkax ★★★★★
()
Последнее исправление: firkax (всего исправлений: 1)
Ответ на: комментарий от firkax

Ты с C++ не перепутал?

Не перепутал.

Строковые литералы ты макросами не распарсишь никак. Да и представь что будет если строка формата - не литерал вообще, а переменная.

Тебе не надо парсить строковые литералы макросами. Тебе надо передать аргументы в логику форматтера типобезопасно. Чтобы форматтер знал, когда в строке %s, а аргумент int (или аргумента вообще нет) и обработал эту ситуацию тем или иным образом, но уж точно не интерпретируя int как char* и не вытаскивая мусор со стека.

И вот variadic macros плюс generics позволяют эту информацию о реальных типах параметров доставить в логику форматтера.

Я, если что, не утверждаю, что код PRINTF("%s", (int) i) будет падать при компиляции. Конечно он будет возвращать ошибку во время выполнения (или иным способом обрабатывать эту ситуацию, например просто печатая число в виде строки).

vbr ★★★★★
()
Последнее исправление: vbr (всего исправлений: 2)

Скастуй в unsigned int или unsigned long с соответствующими модификаторами

mittorn ★★★★★
()

в общем-то компилятор правильно ругается..

причём «архитектура где ЭТО собирается без ошибок», там или старые версии компилятора или libc

uint32_t !== unsigned int ; это чертовски разные типы

если уж вы используете uint32_t то в printf надо %u32i вроде как. Или мерзкие PRIu32 как выше указали. То есть точно не %u

PS/ можно ещё свой спецификатор в printf добавить, для своего домена данных облечённых в uint32_t. Что там у вас ? кол-во объектов, счётчик, код ошибки ??

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

Если значение value в size_t поместится то будет работать нормально. Но лучше извращенные архитектуры обложить #ifdef’ами. С ними еще другие особенности будут.

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

если уж вы используете uint32_t то в printf надо %u32i вроде как.

#include <stdio.h>
#include <stdint.h>



int main( int argc, char* argv[], char* envp[] ){
	
	uint32_t i;
	
	i = 6;
	
	printf( "i is %u32i\n", i );
	
	printf( "End of programm.\n" );
	return 0;
}
$ ./main 
i is 632i
End of programm.

Ключевые слова

вроде как

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

Если значение value в size_t поместится то будет работать нормально

Вероятно на 16-битной архитектуре size_t будет занимать 16 битов и 32-битное целое в него не поместится.

Но лучше извращенные архитектуры обложить #ifdef’ами.

8086 это извращённая архитектура? Интересные новости.

vbr ★★★★★
()

Доброго времени суток !

Вот так не подойдет ?

printf( «FATAL: Unknown security result: %ju\n», (uintmax_t) value);

Так имхо, будет универсально.

Polkovnik1976
()

стдлиба си - устаревшее говно, просто не используй её, кроме как для дебага или чего-то такого, а в таком случае тебе насрать, что оно ругается, просто кастуй к тому типу, который ты знаешь про конкретную архитектуру - 32 битный или 64 битный, вот и всё.

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

Вероятно на 16-битной архитектуре size_t будет занимать 16 битов и 32-битное целое в него не поместится.

Если значение лежащее в 32 бит переменной поместится в 16 бит все будет хорошо. Например

int32_t value = 10;
uint16_t value16 = value;
printf( "%zu\n", (size_t)value );
printf( "%zu\n", (size_t)value16 );

Печатает

10
10

https://godbolt.org/z/fYdWvhnzc

8086 это извращённая архитектура? Интересные новости.

Соберите под 8086 какую нибудь не шибко маленькую прогу из линукса, узнаете.

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

Если значение лежащее в 32 бит переменной поместится в 16 бит все будет хорошо

И? А что на полуслове остановился-то? Теперь давай пример того, что будет в случае, когда значение не поместится в 16 бит.

u5er ★★★
() автор топика
Для того чтобы оставить комментарий войдите или зарегистрируйтесь.