2016-10-13 10 views
2

Я не могу понять смысл компилятора GCC, который я получаю, когда пытаюсь присвоить значение void * переменной intptr_t. В частности, когда я компилирую с -std=c99 -pedantic, я получаю следующее предупреждение относительно инициализации переменной z в строке 7:Нужно ли устанавливать значение void * переменной intptr_t для явного приведения?

предупреждение: инициализации делает целое число от указателя без приведения [-Wint преобразования]

Вот исходный код:

#include <stdint.h> 

int main(void){ 
     unsigned int x = 42; 
     void *y = &x; 

     intptr_t z = y; /* warning: initialization makes integer from pointer without a cast [-Wint-conversion] */ 

     return 0; 
} 

Естественно, если я явно бросить y в intptr_t то предупреждение исчезнет. Однако я смутил, почему предупреждение присутствует для неявных преобразований, когда вся цель intptr_t заключается в преобразовании и обработке значений void *.

Из раздела 7.18.1.4 стандарта C99:

Следующий тип обозначает целочисленный тип с тем свойством, что любой действительный указатель недействительными могут быть преобразованы к этому типу, то преобразуется обратно в указатель на ничтожной, и результат будет сравнивать равна первоначальной указатель:

intptr_t

Am I искажая стандарт, или GCC просто слишком педантичен в «целое число от указателя» проверить в этом случае?

+0

Это [предыдущий вопрос] (http://stackoverflow.com/questions/9492798/using-intptr- t-вместо-void) релевантно? –

+3

Это не слишком педантично. Неявные преобразования - всего лишь достаточно распространенный источник ошибок для существования предупреждения. Подумайте, как это может стать болью при использовании разрешения перегрузки. Так что если вы действительно знать, что вы делаете, сообщить компилятору, выполнив (надеюсь, C++) стиль. – StoryTeller

+1

'intptr_t' - это нормальный целочисленный тип. Единственная гарантия заключается в том, что он может содержать всю соответствующую информацию о« void * », которая не гарантируется для других целых типов, поэтому предупреждение правильное. Почему вы хотите использовать целое число в первую очередь? Обычно это делается для низкоуровневых арифметических или битовых операций над добавлением ССГ. Тогда настоятельно рекомендуется использовать uintptr_t'. В противном случае подумайте трижды (!), Если вы действительно хотите, чтобы целое число удерживало указатель. – Olaf

ответ

3

Подведение итогов! Извините заранее за любые ошибки —, пожалуйста, оставьте мне комментарий.

В C99:

  • Любой указатель может быть преобразован в тип целого числа.
  • Вы можете сделать это, например, if you are implementing your own operating system!
  • Преобразования между указателями и целыми числами может пойти наперекосяк, поэтому они обычно не то, что вы хотите.
  • Поэтому компилятор предупреждает вас, когда вы конвертируете указатели в целые числа без кастинга. Это не слишком педантично, но чтобы спасти вас от неопределенного поведения.
  • intptr_tuintptr_t, и также во всем) просто целочисленный тип, поэтому он подвержен тем же рискам, как и любой другой преобразование указатель на целое число. Поэтому вы получаете то же предупреждение.
  • Однако, с intptr_t, вы хотя бы знаете, что преобразование из указателя не будет усекать любые биты.Таким образом, это типы использования — с явными нажатиями —, если вам действительно нужны целые значения указателей.

    • Спецификация 1, # 6 говорит, что

      ... результат реализации. Если результат не может быть представлен в целочисленном типе, то поведение не определено.

      С intptr_t, результат может быть представлен в целочисленном типе. Поэтому поведение не undefined, но только реализация определена. Это (насколько я знаю), почему эти типы безопасны для использования для получения значений из указателей.

Редактировать

Ссылка 1, ниже, является частью разделе 6.3, "Конверсии". Спецификация говорит:

Несколько операторов преобразования значений операндов от одного типа к другому автоматически. Этот подраздел определяет требуемый результат от такого неявного преобразования ...

и относится к разделу 6.5.4 для обсуждения явных слепков. Поэтому обсуждение в ссылке 1 действительно охватывает неявные слепки от любого типа указателя до intptr_t. По моим данным, неявный листинг от void * до intptr_t является законным и имеет результат, определенный реализацией. 1, 4

Что касается того, следует использовать явное приведение , gcc -pedantic думает, что должен, и должен быть веская причина! :) Я лично согласен с тем, что явное приведение более ясное. Я также думаю о том, что код должен компилироваться без предупреждений, если это вообще возможно, поэтому я бы добавил явный листинг, если бы это был мой код.

Ссылка

C99 draft (так как я не имею копию окончательной спецификации), сек. 6.3.2.3 # 5 и № 6).

Id., сек. 7.18.1.4

Id., сек. 6.3

Id., сек. 3.4.1, определяет «поведение, определяемое реализацией», как «неопределенное поведение, когда каждая реализация документирует, как делается выбор.«Подразумевается, что преобразование является законным, но результат может отличаться на одной платформе, чем на другой.

+0

Теперь я вижу из параграфов № 5 и № 6 раздела 6.3.2.3, что в преобразованиях между целыми числами и указателями существуют неотъемлемые риски (и, следовательно, 'intptr_t' и' void * '). Было бы правильным сказать, что преобразование из 'void *' в 'intptr_t' в' void * 'не подвергает риску * неопределенное поведение * (как указано в параграфе 6), поскольку' intptr_t' определяется как способный представлять результат преобразования 'void * '? –

+0

Просто для пояснения: неявное преобразование действительно для 'void *' to 'intptr_t' (хотя и с результатом, определяемым реализацией), но явное приведение часто рекомендуется, чтобы сделать код чистым. Это верно? –

+0

@VilhelmGray отредактировал – cxw