Спецификаторы Typedefs и printf

Обычное использование typedefs заключается в том, чтобы позволить «типу» переменной передать лучшее представление о цели переменной, не переопределяя структуру хранилища за ней.

Тем не менее, я также вижу typedefs как способ изменить структуру хранилища для classа переменных за один раз.

Например, если я определяю

typedef uint32_t my_offset_t 

и имеют переменные типа my_offset_t , переключение базы кода с uint32_t на char или uint64_t так же просто, как изменение одной строки и перекомпиляция (при условии, что я использовал sizeof а не жестко-кодированные размеры), за исключением случая printf / scanf .

Есть ли способ поменять формат-спецификаторы в соответствии с типом каким-то простым способом, без функций-оболочек вокруг printf / scanf , if-elses или ifdefs?

Спасибо!

Для всех, кого это интересует, я модифицирую LKM, который использовал 16-битные смещения для работы с 32-битными смещениями, но хочу, чтобы он мог перейти к 64-битным (или что-то еще!) Смещениям при необходимости с минимальными изменениями.

Это сложный бизнес, но от C99 и более поздних показывает, как это сделать.

Для каждого из ваших определенных типов вам необходимо предоставить соответствующий набор макросов «PRI» и «SCN», стараясь избегать стандартизованного пространства имен.

Например, вы можете использовать XYZ в качестве префикса для проекта и создать:

 XYZ_PRIx_my_offset_t XYZ_PRId_my_offset_t XYZ_PRIX_my_offset_t XYZ_PRIu_my_offset_t XYZ_PRIo_my_offset_t 

и эквиваленты SCN. Кроме того, вы определяете их с точки зрения эквивалентных макросов для базового типа, поэтому:

 #define XYZ_PRIx_my_offset_t PRIx32 #define XYZ_PRId_my_offset_t PRId32 #define XYZ_PRIX_my_offset_t PRIX32 #define XYZ_PRIu_my_offset_t PRIu32 #define XYZ_PRIo_my_offset_t PRIo32 

В коде вы создаете строки формата, используя macros XYZ_PRIx_my_offset_t :

 printf("Offset: 0x%.8" XYZ_PRIX_my_offset_t "\n", my_offset_value); 

Если впоследствии вам нужно изменить все на 64-битное, вы соответствующим образом отредактируете typedef и макроопределения, а остальная часть кода останется неизменной. Если вы действительно осторожны, вы можете приблизиться к совершенно без изменений.

Убедитесь, что вы скомпилированы как в 32-битной, так и в 64-разрядной системах с множеством предупреждений. GCC не будет предупреждать о не-проблемах на вашей текущей платформе, но они могут появиться на другом. (Я просто исправил код, который был чистым на 64-битном, но нечистом для 32-битного, теперь он использует макрос, такой как XYZ_PRId_int4 вместо %d и компилируется на обоих.)

Если вы посмотрите на мой предыдущий вопрос о inttypes.h, вы можете увидеть, как системные спецификаторы формата могут использоваться совместно с typedefs (через #define ), чтобы создавать специализированные спецификаторы печати для ваших пользовательских типов.

Другим решением является преобразование в и из intmax_t для подписанных типов и uintmax_t для неподписанных типов. Например:

 printf("%ju\n", (uintmax_t)n); 

будет корректно работать, если n имеет любой неподписанный тип.

Для функций *scanf() вам нужно будет прочитать временный объект и затем назначить.

(Это предполагает, что ваша библиотека времени выполнения поддерживает эти функции.)