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

Adres elementu tablicy charów

Object Storage Arubacloud
–1 głos
361 wizyt
pytanie zadane 23 maja 2018 w C i C++ przez Agnes Użytkownik (990 p.)
Jak wyświetlić adres któregoś z elementów tablicy charów i czy w ogóle da się to zrobić? Zapis typu &tab[4] nie spodowuje przecież pobrania adresu czwartego elementu tablicy tab.

Czy zapis &tab wyświetli adres pierwszego elementu tablicy tab?
komentarz 23 maja 2018 przez j23 Mędrzec (194,920 p.)

Zapis typu &tab[4] nie spodowuje przecież pobrania adresu czwartego elementu tablicy tab.

A sprawdzałeś?

komentarz 23 maja 2018 przez Agnes Użytkownik (990 p.)
Tak. Spowoduje to wyświetlenie całego napisu zaczynając od czwartego znaku.

3 odpowiedzi

0 głosów
odpowiedź 28 maja 2018 przez mokrowski Mędrzec (155,460 p.)
wybrane 28 maja 2018 przez Agnes
 
Najlepsza
#include <iostream>
#include <cstddef>

int main() {
    char tab[] = "Ala ma kota.";
    std::size_t tab_len = sizeof(tab) / sizeof(*tab);
    for(std::size_t i = 0; i < tab_len; ++i) {
        std::cout << tab[i] << ": " << static_cast<void *>(tab + i) << '\n';
    }
    
}


1. Standard języka C i C++ gwarantuje że arytmetyka wskaźników w tablicy pracuje poprawnie dla zakresu od pierwszego elementu w tablicy (indeks zerowy) do ostatniego elementu w tablicy plus 1 (indeks jednego elementu za tablicą czyli typowo jej długość). Nie gwarantuje natomiast że uzyskasz jakikolwiek sensowny wynik dla indeksu -1 czy -10. Tam może nie być czytelnych danych lub nawet pamięci.

2. static_cast<X> nie oznacza "dodania static" a prostą czyli statyczną zamianę jednego typu w inny. Uprzywilejowany jest tu void * (void gwiazdka) bo zawsze da się na niego zamienić statycznie inny typ i zawsze będzie to informacją dla jakichkolwiek wyprowadzeń na konsolę że chodzi Ci o "surowy adres w pamięci". 

3. Standard nie gwarantuje że typ int jest tak samo przechowywany jak char na danej platformie systemowej. Może mieć nawet dedykowaną przestrzeń pamięci oraz sposoby wykonywania dostępu do niego.

4. Bezpieczeństwo rzutowania w C++ wynika ze ścisłej kontroli jakie rodzaje konwersji są dopuszczalne w każdym z przypadków rzutowania. Tu masz opisy tych rodzajów rzutowań: http://en.cppreference.com/w/cpp/language/explicit_castLepiej jest robić takie rzutowania bo jasno pokazujesz intencję działania. Rzutowanie w stylu C jest brutalne i pozbawione kontroli oraz trudne do odszukania w dużym projekcie. Tak więc stosowanie printf(...) tylko do wyświetlenia adresu (i tylko to ma usprawiedliwiać jego użycie) jest co najmniej ... "nieeleganckie". Odpowiednikiem rzutowania w stylu C jest reinterpret_cast<X> dla C++.

5. Wskaźnik przechowuje informację o typie danych na jakie wskazuje. Tak dzieje się dla int * czy char * ale już nie dla void * . Sam void * mówi wyraźnie "pozbawiam się typu i interesuje mnie wyłącznie adres". Sam typ void jest oznaczeniem... braku typu. Z racji przechowywania typu, dodanie 1 do np. int *, powoduje przeskoczenie na adres w którym leżałaby następna wartość o zakres adresów wystarczających do pomieszczenia typu int. Dla char *, masz gwarancję że będzie to następny adres bo typ char mapowany jest zawsze na 1 bajt.

6. Typ tablicowy (czyli np: int tab[12]; ) zobowiązuje do trzymania 12 wartości typu int. Jednocześnie jest wskaźnikiem na pierwszy element. Jeśli zrobisz na nim + 1, z definicji samego typu, przeskoczy o "12 elementów typu int". Jeśli chodzi o adresy będzie to: 12 razy sizeof(int). Otrzymasz więc adres tuż za tablicą. W świetle tego co pisałem w pkt. 1, jest to poprawny adres, obliczenia na adresach będą wykonane poprawnie ale nie oznacza to że możesz pod tym adresem coś zapisać bezkarnie bo skąd wiesz co w nim umieścił kompilator?

Mam nadzieję że teraz jasne. Odpowiedzi na takie pytania znajdziesz w dowolnej książce dla początkujących do C lub C++.

komentarz 28 maja 2018 przez Agnes Użytkownik (990 p.)

Prawie jasne.

Rzutowanie w stylu C jest brutalne i pozbawione kontroli oraz trudne do odszukania w dużym projekcie. Tak więc stosowanie printf(...) tylko do wyświetlenia adresu (i tylko to ma usprawiedliwiać jego użycie) jest co najmniej ... "nieeleganckie".

Nie widzę związku przyczynowo - skutkowego między tymi zdaniami. Przecież takie zastosowanie printfa() ma właśnie umożliwić uniknięcie jakiegokolwiek rzutowania.

komentarz 28 maja 2018 przez mokrowski Mędrzec (155,460 p.)

https://isocpp.org/wiki/faq/input-output#iostream-vs-stdio 

Użycie printf(...) tylko do tego celu, pozostawia niejasność do do intencji (skąd czytający ma wiedzieć że tylko w tym celu zostało użyte). Ja raczej doszukiwał bym się "problemów z wydajnością iostream które uczyniły koniecznym użycie printf(...)". Jawne rzutowanie będzie zawsze lepsze niż niejawne przekształcenia. Zresztą autorytatywność źródła (isocpp) powinna przemawiać na korzyść tej wykładni.

+1 głos
odpowiedź 23 maja 2018 przez monika90 Pasjonat (22,940 p.)
edycja 23 maja 2018 przez monika90

Tak, da się to zrobić, ale trzeba rzutować char* na void*, bo operator << argumenty typu char* wyświetla jako łańcuchy znaków. Przykład rzutowania:

std::cout << static_cast<const void*>(&tab[4]) << '\n';

Czy zapis &tab wyświetli adres pierwszego elementu tablicy tab?

Tak, ale należy pamiętać że wskaźnik to nie tylko adres ale też typ, &tab to adres całej tablicy, jest to ten sam adres, co &tab[0], ale typ jest inny. Dla całej tablicy jest to typ char (*)[n] a dla pojedynczego elementu char*

komentarz 23 maja 2018 przez Agnes Użytkownik (990 p.)

@monika90,

 

Tu nie można użyć prostszego rzutowania takiego jak to: (void*)&tab[4]; ?

 

komentarz 23 maja 2018 przez monika90 Pasjonat (22,940 p.)
Można użyć (void*), ale static_cast jest lepsze.
komentarz 24 maja 2018 przez Agnes Użytkownik (990 p.)
Dlaczego? Co by się stało gdyby rzutować np. na int* zamiast na void*?
komentarz 24 maja 2018 przez Agnes Użytkownik (990 p.)
A gdyby użyć printf() zamiast std:: cout to nie byłoby trzeba rzutować i można by zastosować po prostu zapis &tab[4] i wtedy wyświetlony będzie adres czwartego elementu tablicy tab?
komentarz 27 maja 2018 przez monika90 Pasjonat (22,940 p.)
edycja 27 maja 2018 przez monika90
static_cast jest bezpieczniejsze od rzutowania w stylu C ponieważ pozwala tylko na niektóre konwersje. Rzutowanie w stylu C pozwala na nazbyt wiele. static_cast lepiej też oddaje intencję programisty. Jak widzę rzutowanie w stylu C w kodzie to nie wiem po co zostało użyte, może oznaczać cokolwiek.

Rzutowanie na int* nie daje gwarancji że wartość wskaźnika (czyli adres) nie ulegnie zmianie podczas rzutowania. Jest tak ponieważ wskaźniki char* mogą mieć inną budowę wewnętrzną niż wskaźniki int*. Poza tym, rzutowanie char* do int* wymaga reinterpret_cast - static_cast nie da rady.

Tak, printf nie wymaga rzutowania: std::printf("%p\n", &tab[4]);
komentarz 28 maja 2018 przez Agnes Użytkownik (990 p.)

Co rozumiesz przez "pozwala na zbyt wiele"? Ogólnie słyszałam, że rzutowanie w stylu C jest ryzykowne, zastanawia mnie dlaczego.

A static_cast różni się od rzutowania w stylu C tym, że nie dość, że zmienna jest rzutowana na inny typ to jeszcze zyskuje przydomek static? Czy jeszcze czymś?

"Mogą mieć"? Od czego zależy, czy wskaźniki typu char* mają inną budowę wewnętrzną niż wskaźniki typu int*?

Więc chyba łatwiej jest wyświetlać taki adresy printfem. Ale std::printf()? To jest to samo, co "zwykły" printf(), czy to jakieś różne rodzaje?

–1 głos
odpowiedź 23 maja 2018 przez niezalogowany

Witam

Spróbuj:

char tab[5];

cout<<&tab;      // Spowoduje wypisanie adresu elementu 0, ktrótko mówiąc tab[0]
cout<<&tab+1;  // Teraz wyświetli adres elementu 1, i analogicznie dalej :D

 

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

Aha, myślałem, że odnosisz się do wskaźnika void*. Tak, indeks zero oznacza, że adres nie jest przesunięty.

komentarz 28 maja 2018 przez Agnes Użytkownik (990 p.)
edycja 29 maja 2018 przez Agnes

Skro to zachodzi dla tablic każdego typu, to spytam, żeby się upewnić:

int tab[3] = { 1, 2, 3 };
	
for (int i = 0; i < 3; i++) cout << &tab[i] << " ";

cout << endl;
	
for (int i = 0; i < 3; i++) cout << &tab[i] + 5 << " ";

O ile bajtów w drugiej instrukcji przesunięte zostaną te adresy? O 5*sizeof(tab[3]) każdy? Ten index się odnosił do tej 5 w tym przypadku i o to chodziło, a nie o index konkretnego elementu tablicy?

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

O 5 intów, czyli 5 * sizeof(int) bajtów.

 

O 5*sizeof(tab[3])

Nie, ponieważ operator [] ma wyższy priorytet od &. Czyli zapis &tab[i] + 5 można zapisać tak tab + i + 5.

komentarz 29 maja 2018 przez Agnes Użytkownik (990 p.)

Jeśli to się odnosi ogólnie do tablic to analogicznie do tego:

4 * sizeof(char[5])

Nie powinno być 5 * sizeof(int[3])?

komentarz 29 maja 2018 przez j23 Mędrzec (194,920 p.)

Nie, przecież pisałem Ci dlaczego. Odpowiedz sobie na pytanie, jakiego typu wskaźnik zwróci &tab[i].

Podobne pytania

0 głosów
1 odpowiedź 115 wizyt
0 głosów
1 odpowiedź 288 wizyt
pytanie zadane 17 grudnia 2022 w C i C++ przez Stahious Nowicjusz (150 p.)
0 głosów
1 odpowiedź 99 wizyt
pytanie zadane 10 maja 2018 w C i C++ przez Agnes Użytkownik (990 p.)

92,576 zapytań

141,426 odpowiedzi

319,650 komentarzy

61,961 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!

...