Подтвердить что ты не робот

Спецификаторы 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-битные (или что-то еще!) смещения, если это необходимо, с минимальными изменения.

4b9b3361

Ответ 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() вам нужно будет прочитать временный объект и затем назначить.

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