приведение указателя к целочисленным проблемам с предупреждением на 64-битной арке - PullRequest
4 голосов
/ 26 июля 2011

Я пишу модуль ядра Linux, который использует экспортированный символ open_exec

struct file *open_exec(const char *name)

Возвращает указатель, и я могу проверить ошибку с помощью макроса IS_ERR:

if (IS_ERR(file))
    return file;

Во время компиляции я получаю это предупреждение:

warning: return makes integer from pointer without a cast

Это потому, что моя функция возвращает целое число. Если я попытаюсь разыграть это:

return (int) file;

Я не получаю предупреждение на моей 32-битной машине, но я получаю на моей 64-битной машине:

warning: cast from pointer to integer of different size

Это потому, что sizeof для int и указателя одинаковы на 32-битной, но они отличаются на 64-битной машине.

Как ни крути, код работает. Я просто хотел бы избавиться от предупреждения.

Как мне правильно привести указатель к целому числу и получить ожидаемое значение, не получая при этом предупреждения компилятора? Ожидаемое мной значение - это целое число, указанное в include/asm-generic/errno-base.h базы кода ядра Linux.

Так как я смотрю только на указатель, как если бы он был целым числом в случае, если IS_ERR() истинно, я могу быть уверен, что на самом деле он содержит только целочисленное значение.

Ответы [ 3 ]

8 голосов
/ 26 июля 2011

Макрос PTR_ERR() в linux/err.h, где также определено IS_ERR(), преобразует указатель, который действительно является кодом ошибки, в соответствующий тип (a long).

Вы должны использовать что-то вроде:

if (IS_ERR(file))
    return PTR_ERR(file);

Найдите в источнике существующие варианты использования PTR_ERR(), и вы увидите, что это обычный шаблон.

Для вашей функции может быть целесообразно вернуть long, а не int - но все коды ошибок должны быть представлены в int.

1 голос
/ 26 июля 2011

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

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

Это не может быть решено без дополнительных знаний о том, как это intиспользуется позже.

0 голосов
/ 26 июля 2011

Я не уверен, что понял, как иногда вы хотите вернуть число из errno-base.h, а иногда и указатель - как получающая функция сможет различать эти два значения? При равенстве, то в Linux GCC,

  • int имеет ширину 32 бита независимо от того, используете ли вы 32 или 64 бита Linux
  • указатели имеют 64-битную ширину на 64-битных архитектурах и 32-битную ширину на 32-битные архитектуры
  • long 32-битные в 32-битных архитектурах и 64-битные в 64-битных архитектуры.
  • long long всегда имеют ширину 64 бита

следовательно, в 64-битной архитектуре приведение указателя на int означает, что вы будете переводить 64-битное значение в 32-битное, и вы можете быть несколько уверены, что потеряете часть 64-битной информации из указателя - и это о чем говорит предупреждение компилятора, как вы сами указываете.

Если вы хотите привести из указателя к чему-то «анонимному», то ваш выбор должен быть long, long long или void* - с void* наиболее переносимым.

Другая альтернатива - записать это как смещение, то есть если у вас есть большая область памяти, где вы хотите «привести» к 32-битному целому числу, а затем преобразовать его в нечто подобное;

  static struct mybigbuffer *globalbuffer;
   int cast2int(void*x)
   {
       return (int)(globalbuffer-(struct mybigbuffer*)x);
   }

однако , то есть только работают, если вы знаете , что ваша память никогда не превысит 2 ^ 31 записей globalbuf и что ваши указатели Уверены, что выровнены по границам и т.д.

...