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

Wskaźniki - czy kurs pana Mirosława Zelenta zawiera błędy merytoryczne ?

Object Storage Arubacloud
0 głosów
1,839 wizyt
pytanie zadane 29 grudnia 2016 w C i C++ przez Gajsu Nowicjusz (120 p.)

 W filmiku 10 kursu C++ pana Mirosława Zelenta odnośnie wskaźników jako przykład zastosowania było skrócenie czasu operacji wykonywanych na sporych tablicach, gdyż komputer nie musi sprawdzać sam adresu w spisie treści magistrali adresowej. Jako przykład takiego programu był podany program obliczający czas działania pętli, w której program odczytywał wartość zmiennej z tabeli, a następnie zapisywał wartość w każdej z elementów tabeli. Stworzone zostały dwie takie pętle. Jedna wykorzystywała wskaźniki do wykonania odczytu i zapisu, a druga nie. Logiczne jest więc że czas obliczeń dla pętli bez wskaźników powinien być dłuższy, a pętli wykorzystującej wskaźniki krótszy. Po skompilowaniu i uruchomieniu ku mojemu zdziwieniu występuje zależność odwrotna ! Pętla ze wskaźnikami wykonuje się dłużej. Pobrałem również kod udostępniony przez autora filmiku (Myślałem, że może ja gdzieś się pomyliem). Ale działa on tak samo. 

Opisałem problem na innym forum o C++ bo nie spocznę dopóki tego nie rozwiąże.
Otrzymałem odpowiedź/

Cytuję: "kurs Zelenta znam od dawna. To co on mówi jest nieprawdziwe, wskaźniki niczego nie przyspieszą, a uzasadnienie "komputer nie musi sprawdzać sam adresu w spisie treści magistrali adresowej" jest nonsensowne."

Jako uzasadnienie otrzymałem log tego co zrobi kompilator dla pierwszej i dla drugiej pętli
Oto co wyszło:

1pętla:

movl %eax, %esi
movl $50, %eax
.p2align 4,,10
L4:
movl %eax, -200(%ebx,%eax,4)
addl $1, %eax
cmpl $20000050, %eax
jne L4

2ga pętla:


movl %eax, %esi
movl $50, %eax
.p2align 4,,10
L5:
movl %eax, -200(%ebx,%eax,4)
addl $1, %eax
cmpl $20000050, %eax
jne L5

Nie trzeba być geniuszem ani znać asemblera aby zobaczyć, że obydwie pętle to dokładnie te same pętle!
Kto więc się myli czy pan Zelent i przyspieszenie programu wskaźnikami to bzdury ? Przez ten problem nie mogę iść dalej z kursem na youtubie bo nie mam pewności o jakości tych kursów. Może zawierają więcej bzdur ?
Pozdrawiam.

Wklejam kod programu pana Zelenta:

#include <iostream>
#include <time.h>
#include <cstdlib>

using namespace std;

int ile;
clock_t start, stop;
double czas;

int main()
{
    cout << "Ile liczb w tablicy: ";
    cin >> ile;
    
    //dynamiczna alokacja tablicy
    int * tablica = new int[ ile ];
    
    //zacznij odliczac czas
    start = clock();
    
    //wczytywanie liczb do tablicy
    for( int i = 0; i < ile; i++ )
    {
        tablica[ i ] = i;
        tablica[ i ] += 50;
    }
    
    stop = clock();
    
    czas =( double )( stop - start ) / CLOCKS_PER_SEC;
    cout << "Czas zapisu (bez wskaznika): " << czas << " s" << endl;
    
    delete[] tablica;
    
    //ponowna alokacja tablicy
    tablica = new int[ ile ];
    int * wskaznik = tablica;
    
    //zacznij odliczac czas
    start = clock();
    
    //wczytywanie liczb do tablicy
    for( int i = 0; i < ile; i++ )
    {
        * wskaznik = i;
        * wskaznik += 50;
        wskaznik++;
    }
    
    stop = clock();
    
    czas =( double )( stop - start ) / CLOCKS_PER_SEC;
    cout << "Czas zapisu (ze wskaznikiem): " << czas << " s";
    
    delete[] tablica;
    
    return 0;
}

 

komentarz 29 grudnia 2016 przez Gajsu Nowicjusz (120 p.)

Natomiast u mnie 

komentarz 29 grudnia 2016 przez unknown Nałogowiec (39,560 p.)
Porównywanie wyników nie ma sensu. Wszystko zależy od kompilatora, jego wersji, użytych optymalizacji i wielu innych rzeczy. Poza tym dzięki magii(czytaj: optymalizacjom) współczesnych kompilatorów, skok wydajności jaki można uzyskać, jest tak mały, że nie ma sensu się nim przejmować.
komentarz 29 grudnia 2016 przez JAKUBW Nałogowiec (33,470 p.)
Racja, ale mimo to jest to wiedza obowiązkowa, bo mają też wiele innych zastosowań. Po prostu trzeba je znać.
komentarz 29 grudnia 2016 przez niezalogowany
U mnie wskaźniki są szybsze na komputerze z systemem 32 bit, a wolniejsze na 64 (dla małych danych np 100mln mniej więcej takie same)  :D
komentarz 5 czerwca 2018 przez monika90 Pasjonat (22,940 p.)
Tablica nie jest wskaźnikiem, tak samo jak zmienna typu int nie jest zmienną typu float.

Nazwa tablicy też nie jest wskaźnikiem.

3 odpowiedzi

+1 głos
odpowiedź 29 grudnia 2016 przez Evelek Nałogowiec (28,960 p.)

Gdy tworzymy tablicę dynamiczną, czyli jej rozmiar podajemy dopiero w trakcie działania programu, zmienne w tablicy trafiają na stertę. Widocznie poruszając się po stercie wskaźniki robią to szybciej. smiley Ja bym to rozumiał tak: W tej pętli:

for( int i = 0; i < ile; i++ )
    {
        tablica[ i ] = i;
        tablica[ i ] += 50;
    }

program za każdym razem musi odszukać daną komórkę tablicy, odszukać jej adres i przypisać do niej odpowiednią wartość. Potem wraca do "punktu początkowego", aby z niego zacząć szukanie następnej komórki.

Zaś tutaj:

for( int i = 0; i < ile; i++ )
    {
        * wskaznik = i;
        * wskaznik += 50;
        wskaznik++;
    }

Poruszamy się po adresach komórek tablicy co 4 bajty w jednym kierunku, nie wracamy do punktu początkowego.

Można to zobrazować w taki sposób: Masz dom i 10 metrów od niego płot. Pod płotem masz zasadzić 5 kwiatków. Pierwsza pętla to taki sposób: idziesz pod płot, sadzisz kwiatek, wracasz do domu, idziesz pod płot, sadzisz kwiatek, wracasz do domu. itd. Druga pętla to: idziesz pod płot, sadzisz kwiatek, sadzisz kwiatek, sadzisz kwiatek........ idziesz do domu. I tutaj myślę, że widać tą różnicę w czasie.

Ja to tak bym tłumaczył, można mnie zlinczować jak coś źle. laugh

+1 głos
odpowiedź 29 grudnia 2016 przez NaviFox Dyskutant (8,950 p.)
edycja 29 grudnia 2016 przez NaviFox
K&R mówi że wskaźnik będzie na ogół bardziej wydajny niż tablica jednak w dzisiejszych czasach nie jest tak dzięki odpowiednio zoptymalizowanym kompilatorom, end of story. Dodatkowo kursy MZ należy traktować jako wstęp do programowania a nie platformę do wylewania łez. Jeśli nie wiesz czy chcesz programować spokojnie sięgnij po te kursy ale to tylko prolog, resztę musisz ogarnąć sam bez trzymania za rączkę i nie bój się chłopczyku, nic Ci się nie stanie jak przerobisz cały kurs, główka Ci od tego nie wybuchnie :).
komentarz 29 grudnia 2016 przez Gajsu Nowicjusz (120 p.)

Nikt tu nie zamierza wylewać łez ani narzekać. Po prostu chciałem dojść dlaczego u mnie operacje z wykorzystaniem wskaźników trwają dłużej niż na filmiku z kursem. Ekspertem żadnym nie jestem bo dopiero zacząłem naukę :) Rozumiesz, że jeśli mi mój program pokazuje coś odwrotnego niż jest w kursie to wprowadza to wątpliwości :
 

+1 głos
odpowiedź 29 grudnia 2016 przez Sinnley Stary wyjadacz (12,810 p.)

Jest też więcej błędów. NP;

for (int i=0; i<ile; i++)
    {
        cout<<(int)tablica<<endl;
        tablica++;
    }

    delete [] tablica;
    tablica=NULL;

Po skompilowaniu program scrashuje. Powód tego jest taki, że linijka "delete [] tablica" oznacza mniej więcej - usuń tablicę zaczynającą się w adresie równym x, gdzie x reprezentuje wskaznik na poczatek tej tablicy. Jak pewnie słyszałeś, bo wydaje mi się, że MZ o tym mówił, nazwa tablicy jest jednocześnie adresem jej pierwszego elementu.

W pętli jednak występuje przesunięcie wskaźnika o ile miejsc w prawo, wskaźnik więc pod sam koniec znajduje się na samym końcu tablicy, nie na początku. Nie można więc usunąć tej tablicy w sposób pokazanych na odcinku.

Zamiast tego należałoby albo zrobić coś takiego:

int * beginptr = tablica; // robimy wskaznik, ktory bedzie trzymał wartość początku tablicy.

for (int i=0; i<ile; i++) // petla rusza wskaznikiem tablica
    {
        cout<<(int)tablica<<endl;
        tablica++;
    }

    tablica = beginptr; // wskaznik tablica znowu znajduje sie na poczatku
    delete [] tablica; // dopiero teraz mozemy usunac tablice w ten sposob.
    tablica=NULL;

Inna opcja to po prostu nie ruszac wskaznikiem, a jedynie wskazywac na wartosc na jakiej znajdowalby sie wskaznik po przesunieciu w ten sposób.

for (int i=0; i<ile; i++)
    {
        cout<<(int)(tablica + i)<<endl; // wypisujemy wartość dla wskaznika przesunietego o i, ale go nie przesuwamy.
    }

    delete [] tablica;
    tablica=NULL;

 

Generalnie ten odcinek wypada wyjątkowo słabo. Mam wrażenie, że autor trochę ślepo przekonwertował dość kiepski wykład na film, link do wykładu znajduje się na jego stronie: Wyklad

 

Co do samego kursu, ma on swoje wady i zalety. Najzdrowsze podejście moim zdaniem to uznanie tego kursu C++ nie jako kursu języka, a kursu zrozumienia podstawowych zagadnień obecnych w programowaniu.
Zawiera on wiele złych praktyk, jak np. konsekwentnie używane zmienne globalne.

Mogę cię pocieszyć, że o ile odcinek o wskaźnikach jest dość kiepski i lepiej szukać zrozumienia tego tematu gdzie indziej, to inne filmiki dość dobrze tłumaczą pewne aspekty. Należy jednak zaznaczyć, że tłumaczą to co tłumaczą, bo nie pojawia się tam szereg ważnych rzeczy. Przykładowo w odcinku o konstruktorach i destruktorach nie ma nic o liście inicjalizującej, ani konstruktorach kopiujących. Nie warto też brać się za te odcinki robienia aplikacji okienkowych, bo są przedstawiane na podstawie przestarzałego programu o małych możliwościach.

Nie oznacza to jednak, że kursu nie warto obejrzeć, bo warto. Zawsze to trochę rozjaśnienia sprawy, ale na pewno należy to jeszcze poszerzyć innym, większym kursem, a już najlepiej książką i oczywiście masą praktyki.

Co do samych wskaźników, zrozumiesz je gdy zaczną Ci być potrzebne. Na początku ciężko wyobrazić sobie ich zastosowanie, zwłaszcza, gdy słucha się o miernikach temperatury czy innych bzdurach. Po pewnym czasie jednak zdasz sobie sprawę, że po natrafieniu na konkretny problem pomysł użycia wskaźnika sama wpadnie ci do głowy. Nie nastąpi to jednak raczej wcześniej, niż przy robieniu programów obiektowo, a do tego czasu przydadzą Ci się one (wskaźniki) raczej tylko do inicjalizowania tablic o rozmiarach określanych podczas działania programu.

komentarz 29 grudnia 2016 przez unknown Nałogowiec (39,560 p.)

nazwa tablicy jest jednocześnie adresem jej pierwszego elementu.

Nie jest.

1
komentarz 29 grudnia 2016 przez Sinnley Stary wyjadacz (12,810 p.)
Może trochę źle określiłem. Nie jest adresem tylko wskaźnikiem na pierwszy element, trzyma jego adres.
komentarz 29 grudnia 2016 przez unknown Nałogowiec (39,560 p.)
Też nie jest. Nazwa tablicy to nazwa tablicy, nic więcej. Po prostu w niektórych przypadkach jest konwertowana na wskaźnik na 1. element tablicy.
komentarz 29 grudnia 2016 przez Sinnley Stary wyjadacz (12,810 p.)

Zgadza się, mówię o tym konkretnym przypadku. Np:

int d[10];
	int * ptr = &d[0];

	cout << (int)d << endl;
	cout << (int)ptr;

Oba wypisy dają ten sam adres.

Krótko mówiąc nie ważne czy zapiszemy tab[0] czy tab, dostaniemy ten sam adres.
 

komentarz 29 grudnia 2016 przez Gajsu Nowicjusz (120 p.)
To jeszcze takie małe pytanko:

Gdy zrobię dynamiczną alokację pamięci np.  int *jakas_tablica = new int [ile_szufladek];, to po usunięciu tej tablicy delete [] jakas_tablica, wskaźnik zostaje? Bo z tego co rozumiem to można później znowu zarezerwować pamięć posługując się już tylko poleceniem jakas_tablica = new int [ile_szufladek];. Co w takim razie wskazuje wskaźnik *jakas_tablica po usunięciu tablicy? Pierwsze niezajęte miejsce w pamięci ?
Pozdrawiam Wszystkich :)
komentarz 29 grudnia 2016 przez Sinnley Stary wyjadacz (12,810 p.)

Co do ostatniego pytania przyznam się, że nie jestem pewien. Wskazuje jednak inny adres niż wcześniej. Fakt faktem zapis delete[] jakas_tablica oznacza mniej wiecej "zwalniam rezerwacje na ten obszar pamięci i usuwam to co tam było. Potem mogą się tam znaleźć śmieci np:

	int * tab = new int[10];
	int * ptr = tab;

	tab[0] = 22;

	cout << tab[0] << endl; // output 22

	cout << "Adres pod wskaznikiem tab: " << tab << " Adres pod wskaznikiem ptr: " << ptr << endl; // wskazuja ten sam adres

	delete[] tab;

	cout << "Adres pod wskaznikiem tab: " << tab << " Adres pod wskaznikiem ptr: " << ptr << endl; // wskazuja inny adres, ptr wciaz wskazuje ten co poprzednio, tab wskazuje inny adres

	cout << "wartosc w starym adresie: " << *ptr; // output nie wynosi 22, w komorce pojawila sie nowa wartosc.

 

Podobne pytania

0 głosów
2 odpowiedzi 311 wizyt
0 głosów
3 odpowiedzi 3,544 wizyt
pytanie zadane 11 września 2018 w Offtop przez zirael [PL] Mądrala (5,200 p.)

92,555 zapytań

141,403 odpowiedzi

319,557 komentarzy

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

...