Спецификаторы Typedefs и printf
Обычное использование typedefs заключается в том, чтобы позволить "типу" переменной передать лучшее представление о переменной цели без переопределения структуры хранилища за ней.
Однако я также вижу typedefs как способ изменения структуры хранилища для класса переменных за один раз.
Например, если я определяю
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-битные (или что-то еще!) смещения, если это необходимо, с минимальными изменения.
Ответы
Ответ 1
Это сложный бизнес, но <inttypes.h>
от 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
В вашем коде вы создаете строки формата, используя макросы 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
и компилируется на обоих.)
Ответ 2
Если вы посмотрите на мой более ранний вопрос о inttypes.h, вы можете увидеть, как системные спецификаторы формата могут использоваться совместно с typedefs (через #define
) до создайте специальные спецификации печати для своих пользовательских типов.
Ответ 3
Другим решением является преобразование в и intmax_t
для подписанных типов и uintmax_t
для неподписанных типов. Например:
printf("%ju\n", (uintmax_t)n);
будет работать правильно, если n
имеет любой неподписанный тип.
Для функций *scanf()
вам нужно будет прочитать временный объект и затем назначить.
(Предполагается, что ваша библиотека времени выполнения поддерживает эти функции.)