Не понимает поведение компилятора

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

Может кто-нибудь объяснить и указать мне хорошие ссылки, которые помогут мне понять это?

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$

2

Решение

Вы получите ошибку сегментации, только если попытаетесь получить доступ к недействительной памяти. Ваш код просто выполняет арифметику указателя, настраивая указатель на Test чтобы получить указатель на один из его членов, а не читать или писать в цель указателя.

Это все еще неопределенное поведение. Не делайте этого дома, дети.

(Кроме того, не используйте зарезервированные имена лайк __in1, И не используйте -fpermissive разрешить бессмысленные преобразования, подобные этому: система типов поможет вам.)

8

Другие решения

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 на моей машине … понимаю, что нет никаких причин, почему на вашей машине должно быть то же самое, вы никогда не должны полагаться на то, как компилятор конструирует структуры и их размеры.

2

По вопросам рекламы ammmcru@yandex.ru
Adblock
detector