Wskaźniki różnica między int *px, a (int*)x

0

Jak w tytule, nie rozumiem różnicy miedzy tymi zapisami. Wytlumaczylby ktoś?

5

To pierwsze wygląda jak deklaracja/definicja wskaźnika, a to drugie jak rzutowanie x na wskaźnik do int. Bez kontekstu ciężko więcej powiedzieć.

0

int *ptr = NULL - deklarujesz

cast.
ptr = ( int* )adres_podzielny_przez_int_size ( z % ma być zero )
Z tą wielkością adresu to sobie zapamiętaj lepiej.

1
ksh napisał(a):

ptr = ( int* )adres_podzielny_przez_int_size ( z % ma być zero )
Z tą wielkością adresu to sobie zapamiętaj lepiej.

https://wandbox.org/permlink/9ZCUKM8Tsv6VXJWd

#include <iostream>
using namespace std;

int main()
{
    struct __attribute__((packed)) {  char ch1; int value1; char ch2; int value2; char ch3; int value3; char ch4; int value4; }  S={}; 
    cout<<((size_t)&S.value1)%(sizeof(int))<<endl;
    cout<<((size_t)&S.value2)%(sizeof(int))<<endl;
    cout<<((size_t)&S.value3)%(sizeof(int))<<endl;
    cout<<((size_t)&S.value4)%(sizeof(int))<<endl;
    return 0;
}

Wynik:

1
2
3
0
3
_13th_Dragon napisał(a):
ksh napisał(a):

ptr = ( int* )adres_podzielny_przez_int_size ( z % ma być zero )
Z tą wielkością adresu to sobie zapamiętaj lepiej.

https://wandbox.org/permlink/9ZCUKM8Tsv6VXJWd

#include <iostream>
using namespace std;

int main()
{
    struct __attribute__((packed)) {  char ch1; int value1; char ch2; int value2; char ch3; int value3; char ch4; int value4; }  S={}; 
    cout<<((size_t)&S.value1)%(sizeof(int))<<endl;
    cout<<((size_t)&S.value2)%(sizeof(int))<<endl;
    cout<<((size_t)&S.value3)%(sizeof(int))<<endl;
    cout<<((size_t)&S.value4)%(sizeof(int))<<endl;
    return 0;
}

Wynik:

1
2
3
0

Na x86 działa na ARM będzie crash. Standard definiuje to jako Undefined Behavior.

0
MarekR22 napisał(a):

Na x86 działa na ARM będzie crash. Standard definiuje to jako Undefined Behavior.

Nie ma żadnego krasza.

pi@raspberrypi:~ $ g++ test.cpp
pi@raspberrypi:~ $ ./a.out
1
2
3
0
pi@raspberrypi:~ $ uname -a
Linux raspberrypi 5.15.76-v8+ #1597 SMP PREEMPT Fri Nov 4 12:16:41 GMT 2022 aarch64 GNU/Linux
pi@raspberrypi:~ $ 

Może na poziomie architektury procesora jest takie ograniczenie, by czytanie inta z pamięci było wyrównane. Ale kompilator sobie radzi wypluwając odpowiednie instrukcje, by to działało w sposób przezroczysty, nawet jeśli z narzutem na wydajność.
Pierwsze słyszę, by standard C++ wymagał alignowanych intów.

4
Azarien napisał(a):

Pierwsze słyszę, by standard C++ wymagał alignowanych intów.

Nie znalazłem akurat nic o C++ konkretnie, ale w C jest takie wymaganie - ten post wyjaśnia:
https://blog.quarkslab.com/unaligned-accesses-in-cc-what-why-and-solutions-to-do-it-properly.html

Plus, odniesienie do miejsca w standardzie które to określa:

Still according to the C standard, converting a pointer from a type to another without respecting this alignement rule is undefined behavior (http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf page 74, 7).

Ponadto, tamten blogpost daje też możliwy powód dlaczego u Ciebie wszystko działa:

It's actually so bad that the ARM Debian kernel has a mode to catch unaligned access and handle them properly [

0
enedil napisał(a):

Ponadto, tamten blogpost daje też możliwy powód dlaczego u Ciebie wszystko działa:

Nie zupełnie, ten kod będzie działać pod każdym systemem, i nie ma w nim żadnego UB dopóki nie próbuje jakiegoś int'a' do/z tej struktury wpisać/odczytać.
Sama inicjalizacja za pomocą ={} to jedynie wypełnia całą strukturę zerami, więc nadal nie sięgam po te int'y

0
_13th_Dragon napisał(a):

i nie ma w nim żadnego UB dopóki nie próbuje jakiegoś int'a' do/z tej struktury wpisać/odczytać.

Jest UB. Nie potrzebujesz zrobić odczytu z niewyrównanego wskaźnika. Wystarczy, że jest gdzieś niewyrównany wskaźnik, a to się dzieje chociażby tu: &S.value1. A jak mi nie wierzysz to pogógluj bo już mam dosyć walczyć z ludźmi co nie rozumieją zasad alignmentu i wydaje im się, że w subtelny sposób tak naprawdę mają rację.

Inna sprawa, że być może implementacja GCC nawet na ARM tutaj wygeneruje kod tak jakby UB by nie było.
Edit: z tego co rozumiem, użycie attribute packed powoduje, że GCC zapomina o tym, że powinien był dostrzec w tym UB i udaje, że wszystko git. Tak czy inaczej, w ogólnym przypadku nie jest to git.

0
enedil napisał(a):

Jest UB. Nie potrzebujesz zrobić odczytu z niewyrównanego wskaźnika. Wystarczy, że jest gdzieś niewyrównany wskaźnik, a to się dzieje chociażby tu: &S.value1. A jak mi nie wierzysz to pogógluj bo już mam dosyć walczyć z ludźmi co nie rozumieją zasad alignmentu i wydaje im się, że w subtelny sposób tak naprawdę mają rację.

Pogooglowałem, problem dotyczy unaligned access, zaś to &S.value1 to tylko liczba w rejestrze, ba tylko kompilator wie że to adres int'a a nie np char'a i w ogóle że to adres. Czyli dla kodu maszynowego wpisano do rejestru adres struktury S + jakiś tam offset czyli jakąś magiczną liczbę nic więcej.

0

Różnica między int px a (int)x polega na tym, że int px jest deklaracją zmiennej px jako wskaźnika do zmiennej typu int, natomiast (int)x jest rzutowaniem zmiennej x na typ wskaźnika do zmiennej typu int.

W praktyce oznacza to, że int px jest zmienną, która przechowuje adres innej zmiennej typu int, natomiast (int)x jest operacją, która zmienia typ zmiennej x na wskaźnik do zmiennej typu int.

Przykładowo, jeśli chcesz utworzyć wskaźnik px do zmiennej y typu int i przypisać do niego adres zmiennej y, możesz użyć następującego kodu:

int y = 5;
int *px = &y;

Jeśli natomiast chcesz zrzutować zmienną x na wskaźnik do zmiennej typu int, możesz użyć następującego kodu:

int x = 5;
int *px = (int*)x;

W obu przypadkach możesz użyć wskaźnika px do odczytania lub zmiany wartości zmiennej, do której wskazuje. Na przykład, jeśli chcesz zwiększyć wartość zmiennej y o jeden, możesz użyć następującego kodu:

(*px)++;

Ogólnie rzecz biorąc, różnica między int px a (int)x jest różnicą między deklaracją zmiennej jako wskaźnika a rzutowaniem istniejącej zmiennej na typ wskaźnika.

0

@_13th_Dragon: Jeszcze jeden zdecydował się powymądrzać, powiedz mi proszę po kiego we wskaźnik wpisujesz adres 5 o tu int x = 5; int px = (int)x; do której zmiennej ten px wskazuje?

Nie jest możliwe przypisanie adresu liczby całkowitej (np. 5) do wskaźnika typu int*. Wskaźnik musi wskazywać na adres pamięci, na którym znajduje się obiekt. Aby przypisać adres obiektu do wskaźnika, należy użyć operatora &, który zwraca adres obiektu. Na przykład, aby przypisać adres zmiennej x do wskaźnika px, należy użyć następującego kodu:

int x = 5;
int *px = &x; // px wskazuje na adres zmiennej x

Jeśli chcesz przypisać liczbę całkowitą (np. 5) do wskaźnika, musisz najpierw przypisać tę liczbę do jakiegoś obiektu, a następnie przypisać adres tego obiektu do wskaźnika. Na przykład:

int x = 5;
int *px = &x; // px wskazuje na adres zmiennej x
int y = 5;
int *py = &y; // py wskazuje na adres zmiennej y

W tym przykładzie obiekty x i y są zmiennymi typu int, które przechowują wartość 5. Wskaźniki px i py wskazują na adresy tych zmiennych i mogą być używane do odczytu lub zmiany wartości tych zmiennych.

Pamiętaj Dragon, że nie można przypisać adresu liczby całkowitej (np. 5) do wskaźnika, ponieważ liczba całkowita nie jest obiektem i nie ma adresu w pamięci. Wskaźnik musi wskazywać na adres pamięci, na którym znajduje się obiekt.

1
chomikowski napisał(a):

Nie jest możliwe przypisanie adresu liczby całkowitej (np. 5) do wskaźnika typu int*.

Doprawdy? https://wandbox.org/permlink/hrogTqn5VrNX64pf

#include <iostream>
using namespace std;

int main()
{
    int x=5;
    int *px=(int*)(size_t)x;
    cout<<px<<endl;
    return 0;
}

Oprócz tego właśnie to podałeś w poprzednim poście tylko bez konwersji (size_t) która jedynie zapobiega ostrzerzeniu.

Wskaźnik musi wskazywać na adres pamięci, na którym znajduje się obiekt.

Bzdura, wyżej przykład.

Jeśli chcesz przypisać liczbę całkowitą (np. 5) do wskaźnika, musisz najpierw przypisać tę liczbę do jakiegoś obiektu, a następnie przypisać adres tego obiektu do wskaźnika. Na przykład:

Widzę że nie odróżniasz wskaźnik do wartości 5 od wskaźnika o wartości 5.

Pamiętaj Dragon, że nie można przypisać adresu liczby całkowitej (np. 5) do wskaźnika, ponieważ liczba całkowita nie jest obiektem i nie ma adresu w pamięci. Wskaźnik musi wskazywać na adres pamięci, na którym znajduje się obiekt.

Pamiętaj @chomikowski, pouczyć się podstaw C/C++ przed następnym udzielaniem się na forum.

0

Zobaczmy co tak na prawde zrobiles?

W powyższym twoim kodzie rzutowanie (int*) na wartość x nie ma sensu, ponieważ nie można rzutować liczby całkowitej na wskaźnik do liczby całkowitej. Wskaźnik jest adresem pamięci, a liczba całkowita to wartość przechowywana w pamięci. Nie można zamienić jednego na drugie bez odpowiedniej operacji przepisania danych z jednej lokalizacji pamięci do drugiej.

Jeśli chcesz przechować wartość liczby całkowitej w wskaźniku, musisz najpierw zaalokować odpowiednią ilość pamięci dla tej wartości, np. za pomocą funkcji malloc(), a następnie przepisać wartość x do tej pamięci. Oto przykład kodu, który to robi:

int x = 5;

// Alokowanie pamięci dla jednej liczby całkowitej
int *px = (int*)malloc(sizeof(int));

// Przepisanie wartości x do pamięci zaalokowanej dla wskaźnika px
*px = x;

Po wykonaniu powyższego kodu wskaźnik px będzie wskazywał na lokalizację pamięci, w której została zapisana wartość x. Możesz potem odczytać tę wartość za pomocą operatora *, np. cout << *px << endl; wypisze na ekranie wartość x. Pamiętaj jednak, że po skończeniu korzystania z pamięci zaalokowanej dla wskaźnika px, należy ją zwolnić za pomocą funkcji free(), aby uniknąć wycieków pamięci. Oto przykład końcowego kodu:

int x = 5;

// Alokowanie pamięci dla jednej liczby całkowitej
int *px = (int*)malloc(sizeof(int));

// Przepisanie wartości x do pamięci zaalokowanej dla wskaźnika px
*px = x;

// Wyświetlenie wartości x za pomocą wskaźnika px
cout << "Wartosc x: " << *px << endl;

// Zwolnienie pamięci zaalokowanej dla wskaźnika px
free(px);

Powyższy kod alokuje pamięć dla liczby całkowitej, przepisuje do niej wartość x, wyświetla tę wartość za pomocą wskaźnika px i kończy korzystanie z tej pamięci poprzez jej zwolnienie. Pamiętaj, że nie należy korzystać z wskaźnika px po jego zwolnieniu, ponieważ wskazywać on będzie na pamięć, która może zostać przydzielona innemu procesowi i zmodyfikowana. Może to prowadzić do błędów i awarii programu.

Pozdrawiam cie przyjacielu i pamiętaj, nie ma sensu robić rzeczy bez sensu

0
chomikowski napisał(a):

W powyższym twoim kodzie rzutowanie (int*) na wartość x nie ma sensu, ponieważ nie można rzutować liczby całkowitej na wskaźnik do liczby całkowitej. Wskaźnik jest adresem pamięci, a liczba całkowita to wartość przechowywana w pamięci.

A jednak można i często było używane a nawet jest używane, do poczytania http://mysinski.wieik.pk.edu.pl/UPCiMCU/Przerwania%20w%20systemie.pdf

Nie można zamienić jednego na drugie bez odpowiedniej operacji przepisania danych z jednej lokalizacji pamięci do drugiej.

A jednak można, kompiluje się i odpala się, czy nie można bo ty zabroniłeś?

Dalej przykład nie dotyczący tematu, czy ty jakiegoś super tępego bota AI stosujesz do klepania tych postów?
Bo nawet działający przykład dla ciebie nie argument.

0

Rozsadzić wątek, sapać się do wszystkich, zachwalać kod pełen warnów.
W tagach C ale przykład dla zasady dam w C++ i w C++ będę kompilował. Bo C i C++ to przecież ten sam język.
A jak się kompiluje to znaczy, że działa.

1
ksh napisał(a):

Rozsadzić wątek, sapać się do wszystkich, zachwalać kod pełen warnów.
W tagach C ale przykład dla zasady dam w C++ i w C++ będę kompilował. Bo C i C++ to przecież ten sam język.
A jak się kompiluje to znaczy, że działa.

  1. Nikt tu nie zachwala żadnego kodu.
  2. Kompiluje się i uruchamia na 3-ch platformach i w sumie na 5-ci kompilatorach.
  3. To co pokazano w kodzie to zaprzeczenie głupiemu stwierdzeniu i tyle.
  4. Kontynuujesz rozsadzanie wątku.

No ale kto by czytał treść wątku, jest powód na kogoś posapać to do przodu oto cały @ksh

0

Otwórz sobie manual gcc poszukaj fstric-aliasing i przeczytaj co tam pisze.
Ja nie mam wiedzy aby stwierdzić kiedy kompilator postanowi optymalizować dany kawałek kodu który kompletnie zaora program.
Wyjątkowo w Twoim wypadku wiadomo bo, nie włączyłeś optymalizacji w swoim przykładzie. Masz w manualu, że gcc stosuje od -O2.Więc ten "przykład" można sobie podarować.

0
ksh napisał(a):

Otwórz sobie manual gcc poszukaj fstric-aliasing i przeczytaj co tam pisze.

Czytałem i nadał twierdzę że chodzi o access nie o trzymanie adresu w rejestrze, nie ma próby odczytu/zapisu nie ma problemu.

ksh napisał(a):

Ja nie mam wiedzy aby stwierdzić kiedy kompilator postanowi optymalizować dany kawałek kodu który kompletnie zaora program.

A jakąkolwiek masz? Widzę nawet z czytaniem masz problemu.

ksh napisał(a):

Wyjątkowo w Twoim wypadku wiadomo bo, nie włączyłeś optymalizacji w swoim przykładzie. Masz w manualu, że gcc stosuje od -O2.Więc ten "przykład" można sobie podarować.

No to włącz optymalizacje i zobacz, zanim post nasmarujesz.

@ksh - Rozsadzić wątek, sapać się do wszystkich bez przeczytania; bez wiedzy; bez zastanowienia, ot cały ty.

0

Dając bardzo wąski przykład jak avr procesory, tam czytamy po 2 bajty z pamięci jako jeden adres iterując co jeden.

jeśli nasza pamięć jest w dwóch chipach to musimy z dwóch miejsc na raz pobrać, dlatego wyrównanie daje optymalniejsze rozwiązanie, tak to trzeba pobrać z jednego chipa, z drugiego i potem scalić.
Jak weźmiecie do ręki pamięć ram to tam jest kilkanaście chipów, a nie jeden duży.

tu coś do poczytania.
https://developer.ibm.com/articles/pa-dalign/

Procesory dają możliwość nie wyrównanej pamięci możliwość odczytu, ale zwykle trochę wolniejszy.

Strony pamięci zwykle są wyrówanane i zwykle jako dobra praktyka kompilatory wyrównują dane i tak wyrówane są jako strona załadowane.

Prędkość po za chipem bardzo mocno spada, dlatego po wyjściu z cache l3, mamy prędkość zegara płyty głównej po PCI, a w ramie mamy wiele chipów, które są jednocześnie odczytywane, a dane są tam zapisaywane jednocześnie we wszystkich, co daje duży performance gdyż nie idzie tam łatwo podwyższyć zegara więc trzeba scalować horyzontalnie dodając chipy.

Tutaj od GPT odpowiedź:
Aligning memory accesses is important for a few reasons. First, it can improve the performance of your program by allowing the CPU to access memory more efficiently. When memory is properly aligned, the CPU can access it in larger chunks, which can reduce the number of memory accesses required and therefore improve the speed of your program.

Another reason to align memory is to avoid what is known as a "bus error". A bus error occurs when the CPU tries to access memory that is not properly aligned, which can cause the program to crash or produce incorrect results. This can be especially problematic in systems with a high-speed bus, such as a multi-core processor, where the memory accesses need to be carefully coordinated to avoid conflicts.

In summary, aligning memory accesses can improve the performance and reliability of your program by ensuring that the CPU can access memory efficiently and avoid bus errors.

1 użytkowników online, w tym zalogowanych: 0, gości: 1