Я хочу понять, почему приведенный ниже код на самом деле работает, а не дает ошибку сегмента. Один из моих коллег показал мне это, и я был просто удивлен.
Может кто-нибудь объяснить и указать мне хорошие ссылки, которые помогут мне понять это?
struct Test {
int __in;
int __in1;
};
int main()
{
struct Test* t = NULL;
int i = &(t->__in1) + 4;
std::cout << i << std::endl;
}
arun@arun-desktop:~/Code$ g++ -fpermissive -g test8.cc
test8.cc: In function ‘int main()’:
test8.cc:11:24: warning: invalid conversion from ‘int*’ to ‘int’ [-fpermissive]
arun@arun-desktop:~/Code$ ./a.out
20
arun@arun-desktop:~/Code$
Вы получите ошибку сегментации, только если попытаетесь получить доступ к недействительной памяти. Ваш код просто выполняет арифметику указателя, настраивая указатель на Test
чтобы получить указатель на один из его членов, а не читать или писать в цель указателя.
Это все еще неопределенное поведение. Не делайте этого дома, дети.
(Кроме того, не используйте зарезервированные имена лайк __in1
, И не используйте -fpermissive
разрешить бессмысленные преобразования, подобные этому: система типов поможет вам.)
struct Test {
int __in;
int __in1;
};
unsigned int fun ( void )
{
struct Test* t=NULL;
unsigned int i = (unsigned int)(&(t->__in1)) + 4;
return(i);
}
unsigned int fun2 ( void )
{
struct Test t;
unsigned int i = (unsigned int)(&(t.__in1)) + 4;
return(i);
}
Я немного изменил ваш код, частично чтобы помочь с предупреждением / ошибкой. В первом случае у вас есть указатель, у которого нет памяти, поэтому у него нет элементов. Вы указали на NULL. Вам нужно указать на что-то отличное от нуля (таким образом, математика равна NULL или нулю плюс смещение 4 плюс 4), но это не решит проблему.
Во втором случае за этим стоит некоторая память, и в стеке размещается фактическая структура, выделенная компилятором. Итак, я получаю это:
00000000 <fun>:
0: e3a00008 mov r0, #8
4: e12fff1e bx lr
00000008 <fun2>:
8: e24dd008 sub sp, sp, #8
c: e28d0008 add r0, sp, #8
10: e28dd008 add sp, sp, #8
14: e12fff1e bx lr
Поэтому есть надежда, что подобный код даст вам адрес, который вы могли бы использовать.
Когда вы строите свою программу с соответствующими типами приведений и параметров командной строки, чтобы обойти ошибки, она также выдаст 8.
Я не вижу ничего плохого в выполнении математики адресов, как это, не для этой структуры, но я вижу некоторые варианты использования для такой вещи. Вы должны быть в состоянии получить адрес элемента в структуре, и вы должны быть в состоянии сделать адресную математику с этим адресом, в такой математике нет ничего противозаконного.
Вот, например, попробуйте это (просто немного погуглил и нашел другой вопрос stackoverflow):
//g++ -std=c++11 ptr.c -o ptr
#include <iostream>
#include <cstdint>
struct Test {
int __in;
int __in1;
};
int main()
{
struct Test t;
intptr_t i = (intptr_t)(&(t.__in1))-(intptr_t)(&t) + 4;
std::cout << i << std::endl;
}
и результат получился как 8 на моей машине … понимаю, что нет никаких причин, почему на вашей машине должно быть то же самое, вы никогда не должны полагаться на то, как компилятор конструирует структуры и их размеры.