четвер, 13 травня 2010 р.

Універсальний тип даних для вказівника (C/C++ pointer)

Вирішував нещодавно цікаву задачу: як правильно здійснити приведення типу для вказівника типу (void *)? Якщо на 32-бітних системах розмір вказівника рівний розміру типу даних int, то в 64-бітних системах усе залежить від реалізації. Наприклад, тип int може бути як 32-бітним (Microsoft Win64, більшість UNIX-подібних систем), так і 64-бітним (якщо система використовує модель даних ILP64 або SILP64). Аналогічна ситуація і з типом long: він 64-бітний на більшості систем, проте є системи, де його розмірність рівна 32 біт. Детальніше про це можна прочитати тут:
http://en.wikipedia.org/wiki/64-bit#Specific_C-language_data_models.

Отже, задача звелася до того, що слід мати універсальний тип даних, розмірність якого завжди відповідає розмірності вказівника. Як виявилося, в UNIX-подібних системах існують спеціальні типи даних, спроектовані для операцій з вказівниками, такі як ptrdiff_t, intptr_t, uintptr_t.
Тип ptrdiff_t призначений зберігання результатів віднімання вказівників і є знаковим.
Тип intptr_t є його синонімом.
Тип uintptr_t є беззнаковим цілочисельним типом і здатен безпечно зберігати у собі вказівник незалежно від розрядності платформи.

Отже, для моєї задачі потрібен був тип uintptr_t. Слід було лише виявити, для якої платформи який заголовний файл (header) слід включити, щоб використати цей тип.
Ось список заголовних файлів відповідно для системи:
GNU/Linux - stdint.h;
Solaris - inttypes.h;
AIX - inttypes.h;
IRIX - додадковий заголовний файл не потрібен;
HP-UX - sys/wsio.h.

Відповідно, універсальний код для включення типу uintptr_t:

#if defined(__hpux)
#include <sys/wsio.h>
#else
# if defined(__sun) || defined(_AIX)
#include <inttypes.h>
# else
# ifndef __sgi
#include <stdint.h>
# endif
# endif
#endif