2012-06-15 2 views
0

Я пытался понять приведение типов void в C. Я написал программу, чтобы попытаться создать ошибку сегментации, но я ее не получаю. Может кто-то пожалуйста, объясните мне, почемуvoid pointer typecasting

Код:

#include <stdio.h> 
typedef unsigned long U32; 
int main() 
{ 
    void *obj; 
    U32 value = 25; 
    U32* ptr; 
    printf("*(ptr)       : %lu\n", *(ptr)); 
    obj = &value; 
    ptr = &value; 
    ptr++; 
    printf("*(U32 *)(obj)     : %lu\n", *(U32 *)(obj)); 
    printf("*((U32 *)(obj) + 1)    : %lu\n", *((U32 *)(obj) + 1)); 
    printf("*(U32 *)((U32 *)(obj) + 1)  : %lu\n", *(U32 *)((U32 *)(obj) + 1)); 
    printf("*(ptr)       : %lu\n", *(ptr)); 
    return 0; 
} 

Выход:

*(ptr)       : 458998657 
*(U32 *)(obj)     : 25 
*((U32 *)(obj) + 1)    : 3215085752 
*(U32 *)((U32 *)(obj) + 1)  : 3215085752 
*(ptr)       : 3215085752 

Как я вижу это только второй Printf является законным, так как все остальные имеют в виду какой-то неинициализированного оперативной памяти и должен вызывать сегрегацию неисправности

+2

В основном вы получаете segfault при доступе к защищенной памяти. Память, к которой вы обращаетесь, может быть или не быть защищена (возможно, нет, поскольку обычно переменные распределяются последовательно, но они не обязательно должны быть вообще). – jn1kk

+1

Вам не повезло, что первый 'printf()' не сбой; ваш 'ptr' должен состоять из достаточно правильного значения указателя, чтобы не сбой, хотя это формально неопределенное поведение (и неопределенное поведение включает« появление для работы »). –

ответ

1
U32* ptr; 
    printf("*(ptr)       : %lu\n", *(ptr)); 

ptr не инициализирован, так что неопределенное поведение разыменования его,

obj = &value; 
    ptr = &value; 
    ptr++; 
    printf("*(U32 *)(obj)     : %lu\n", *(U32 *)(obj)); 

Ok, пока obj указывает на действительный U32 (и U32 должен быть неподписанный долго, как вы» повторное использование% лу в формате PRINTF)

printf("*((U32 *)(obj) + 1)    : %lu\n", *((U32 *)(obj) + 1)); 

Вы пытаетесь разыменования указателя, который указывает один U32 мимо любого действительного места, так что это неопределенное поведение.

printf("*(U32 *)((U32 *)(obj) + 1)  : %lu\n", *(U32 *)((U32 *)(obj) + 1)); 

То же, что и выше.

printf("*(ptr)       : %lu\n", *(ptr)); 

Поскольку вы увеличивается ptr, он указывает один U32 мимо ничего действительного, что приводит к непредсказуемому поведению.

Как ваш код делает то, что вызывает undefined behavior, это действительно сложно и часто невозможно рассуждать о том, что произойдет. Это не определено. Может произойти что-то неожиданное. Что-то плохое может произойти. Ничего плохого не могло произойти, и так далее. Выполнение чего-то в C, которое вы не должны делать, не означает, что это будет segfault.

0

ptr указывает на действительный объект (value), но ptr++ не указывает на объект. Таким образом, доступ к элементу *ptr после инкремента указателя является неопределенным поведением. printf из *ptr может отображать случайные данные, но также может привести к краху вашей программы.

2

UB смешно, так как поведение не определено, а не гарантировано segfault. Адрес obj + 1 - это, вероятно, другое место в стеке, которое в любом случае выделено вашей программе. Чтобы получить почти гарантированный segfault, просто снимите ссылку obj, прежде чем он когда-либо был назначен.

+0

Он делает это. – jn1kk

+0

@skynorth: Нет, он этого не делает. Сначала он говорит «obj = &value;». – Puppy

+0

О, я говорил о U32 * ptr, и он печатает его немедленно. – jn1kk