• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

question-closed C++, miejscowa wersja operatora new

Object Storage Arubacloud
0 głosów
474 wizyt
pytanie zadane 28 marca 2018 w C i C++ przez Jakub 0 Pasjonat (23,120 p.)
zamknięte 28 marca 2018 przez Jakub 0

Witam, mam taki kod:

#include <iostream>
#include <new>

void f(){
    char buffer[100]; //tablica automatyczna o zasięgu wewnętrznym 
    
    
    int *pt = new(buffer) int{10}; //utworzenie 
    std::cout<<(void*)buffer<<std::endl; //adres (jedna z metod dostepu)
    std::cout<<*pt<<std::endl; //wartosc (jedna z metod dostepu)
}

int main(){
    f();
    
    int* s = (int*)0x6dfe68;  //&buffer[0] (popatrzcie na to z przymrużeniem oka)
    
    std::cout<< *s <<std::endl;
    *s = 45;    //normalna praca ma wskazniku 
    std::cout<< *s <<std::endl;
}



W funkcji f() dynamicznie tworzymy obiekt danych typu int pod adresem pierwszego elementu tablicy buffer (ona jest lokalna i podlega przydziałowi dynamicznemu więc ginie po zakończeniu działania bloku kodu w którym się znajduje). Do zapisania int'a potrzebujemy wykorzystać cztery pierwsze elementy tablicy buffer ( bo każdy element ma 1B a int u mnie zajmuje 4B).

Po zakończeniu działania funkcji odwołujemy się do adresu tablicy (dokładniej jej zerowego indeksu) gdzie został przydzielony dynamicznie obiekt danych. 0x6dfe68 to odpowiedni adres, wiem... funkcja f() mogła by go zwrócić ;)

Samo to że wartość zapisaną pod tym adresem można odczytać mnie nie dziwi ( bo wartości pozostają w ram'ie) . Jednak można na niej pracować:

 *s = 45;

Kiedy mamy zwykłą funkcje zwracającą adres zmiennej lokalnej to nie możemy nic pod nim grzebać bo program się wysypie ( albo kompilator nie dopuści do kompilacji czegoś takiego ). Tu na natomiast można traktować ten adres jak by był przez coś zarezerwowany. Czy wynika to z zasady działania operatora new? Czy może program działa poprawnie przypadkiem? Czy taka sytuacja jest poprawna?

A takie coś:

int* s = new((int*)0x6dfe56) int[3]{12,3,5};

std::cout<<s[0]<<std::endl;
std::cout<<s[1]<<std::endl;
std::cout<<s[2]<<std::endl;

Pod s[0] jest poprawna wartość a pod resztą głupoty... Jaki typ domyślnie jest pod tym (ogólnie przykładowym) niezarezerwowanym adresem?

Dziękuje wam bardzo za informacje i serdecznie pozdrawiam ;)

komentarz zamknięcia: już znam wytłumaczenie

1 odpowiedź

+1 głos
odpowiedź 28 marca 2018 przez j23 Mędrzec (194,920 p.)
wybrane 28 marca 2018 przez Jakub 0
 
Najlepsza

Czy wynika to z zasady działania operatora new?

Jeśli jest to adres lokalnej tablicy buffer, to odpowiedź jest prosta - to jest pamięć stosu, ona jest dostępna przez cały czas działania procesu.  new nie ma nic tutaj to rzeczy (zresztą w tym przypadku użycie placement new jest... przesadą. Zwykłe rzutowanie wystarczy).

komentarz 28 marca 2018 przez Jakub 0 Pasjonat (23,120 p.)

Dziękuje, czyli rozumiem że całe:

int *pt = new(buffer) int{10};

To normalne tworzenie "zmiennej" dynamicznej tyle że w określonej lokalizacji?

Kwestia jest taka że tu tworzymy coś już pod adresem zarezerwowanym (użytkowanym), stare dane zostaną przykryte. A co jeśli chcemy tworzyć coś dynamicznie pod adresem nie zarezerwowanym przez nic? Np wiem że przykładowy adres 0x6dfe56

gdzieś tam jest w pamięci ram i nic na niego nie wskazuje ani nie ma pod nim żadnej zmiennej, czy gdy wykorzystamy taki adres do czegoś takiego:

int* s = new((int*)0x6dfe56) int[3]{12,3,5};

To jest to błąd?

Oczywiście chodzi mi tu o czysto teorytyczne zrozumienie a nie że takie coś już chce koniecznie stosować już w praktyce

1
komentarz 28 marca 2018 przez j23 Mędrzec (194,920 p.)

To normalne tworzenie "zmiennej" dynamicznej tyle że w określonej lokalizacji?

Tak.

A co jeśli chcemy tworzyć coś dynamicznie pod adresem nie zarezerwowanym przez nic?

Jeśli adres nie jest zarezerwowany, to nie masz do niego dostępu. W przykładzie z pierwszego postu to działa, bo odwołujesz się do pamięci stosu, co nie zmienia faktu, że ta pamięć będzie użyta przy kolejnym wywołaniu jakiejś funkcji, więc dane zostaną zamazane.

 

To jest to błąd?

Zależy. Jeśli adres 0x6dfe56 nie jest dostępny, to tak.

 

komentarz 28 marca 2018 przez Jakub 0 Pasjonat (23,120 p.)
Dziękuje za pomoc, teraz już wiem mniej więcej jak nie używać tej wersji operatora new.
komentarz 28 marca 2018 przez j23 Mędrzec (194,920 p.)
Pisząc na Windowsa czy Linuksa praktycznie nie stosuje się odwołań do konkretnych adresów (chyba że piszesz cheaty do gry), bo one co kompilację czy co każde uruchomienie procesu mogą się zmieniać. Po to są nazwy zmiennych i funkcje przydzielające pamięć, żeby tego nie robić.
komentarz 28 marca 2018 przez Jakub 0 Pasjonat (23,120 p.)
Wiem, wiem... Napisałem że chodzi mi o czysto teorytyczną wiedze ;)
1
komentarz 29 marca 2018 przez mokrowski Mędrzec (155,700 p.)

To normalne tworzenie "zmiennej" dynamicznej tyle że w określonej lokalizacji?

Nie. Przez tę wersję new, nakazujesz mu zachować się nietypowo bez alokacji pamięci. Już ją alokowałeś tworząc bufor na stosie. Systemu od tego momentu nie interesuje skąd ten bufor masz bo "twierdzisz że wiesz lepiej". W Twoim przykładzie nie jest to także alokacja dynamiczna bo do systemu operacyjnego nie powędrowało żądanie o alokację pamięci w przestrzeni sterty (tu pomijam dla jasności dyskusję o nazewnictwie ze standardu C++). new wykonał jedynie konstrukcję obiektu umieszczając go w nakazanym buforze. Pamięć stosu jest stosunkowo "atrakcyjna" dla programów i z zasady ciągła. Jeśli wywołasz jakąś funkcję, masz dużą szansę że naruszysz pamięć która kiedyś była dostępna pod jakimś adresem na stosie bo z przestrzeni stosu pamięć jest wydzielana poprzez przestawienie adresu w jednym rejestrze (przestawienie ramki stosu).

Pamięć dynamiczna (sterty) jest dość pofragmentowana i system inaczej ją traktuje. Jeśli przeznaczony dla Ciebie obszar sterty jest za mały, jądro systemu przydzieli nowe strony pamięci. W przypadku stosu tak się nie dzieje. Jest on ograniczony dla każdego wątku do stałego rozmiaru (oczywiście można go zmienić wywołaniami systemowymi lub na etapie kompilacji).

Dlaczego alokowanie przez takie new powinno być świadome? Sprawdź program. Powiedz czy wywołany jest (zakomentowany) destruktor:

#include <iostream>

struct X {
    X() {
        std::cout << "X construct\n";
    }
    void foo() const {
        std::cout << "foo() in X\n";
    }
    ~X() {
        std::cout << "X destruct\n";
    }
};

X * newplace() {
    char p[sizeof(X)];
    X * px = new(p) X;
    // px->~X(); 
    return px;
}

int main() {
    X * p = newplace();
    p->foo();
}

Jeśli takiego działania oczekujesz i tego chcesz, powinieneś znać konsekwencje. Destruktor często wtedy trzeba wołać ręcznie.  W kodzie powyżej foo() najprawdopodobniej także zadziała ale nie masz pewności w każdym przypadku i przy każdym kodzie.

Oczywiście przykład kodu który podałem jest z gruntu patologiczny i nie należy bazować na takich technikach :)

Podobne pytania

+2 głosów
2 odpowiedzi 603 wizyt
pytanie zadane 3 kwietnia 2017 w C i C++ przez Evelek Nałogowiec (28,960 p.)
0 głosów
2 odpowiedzi 256 wizyt
pytanie zadane 9 lutego 2019 w C i C++ przez jankustosz1 Nałogowiec (35,880 p.)
0 głosów
1 odpowiedź 642 wizyt

92,624 zapytań

141,482 odpowiedzi

319,822 komentarzy

62,005 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...