[C++] dynamiczne alokowanie pamięci+ wskaźniki

Awatar użytkownika
alchem
Użytkownik
Użytkownik
Posty: 252
Rejestracja: 10 cze 2014, o 19:10
Płeć: Mężczyzna
Lokalizacja: Wrocław
Podziękował: 83 razy
Pomógł: 5 razy

[C++] dynamiczne alokowanie pamięci+ wskaźniki

Post autor: alchem »

Trochę poprawiłem kod, teraz wygląda tak:

Kod: Zaznacz cały

#include <iostream>
#include <cstdlib>
#include <ctime>
#include <windows.h>

void losowe_liczby (int  *wskaz, int ile, int przedzial, int a);

int main()
{
     std::cout << "Program wylosuje okreslona ilosc licz bez powtorzen"<<std::endl<<std::endl;
     int a, b, ile, przedzial;
  do{
     std::cout << "Podaj 'a' i 'b' aby okreslic przedzial [a...b]: ";
     std::cin>>a>>b;
  if(a >= b){
     std::cout<<"'a' nie moze byc wieksze badz rowne 'b'. Podaj a i b jeszcze raz."<<std::endl;
     Sleep(3000);
     system("cls");
     }
    }while(a>=b);
     przedzial=b-a;
     std::cout<<"Wybrany przez Ciebie przedzial to: ["<<a<<"..."<<b<<"]"<<std::endl<<std::endl;
     std::cout<<"Liczby maja byc bez powtorzen a wiec nie moze  byc ich wiecej niz: "<< b-a+1 <<std::endl<<std::endl;
  do{
     std::cout <<"Podaj ile liczb z tego przedzialu chcesz wylosowac: " ;
     std::cin >> ile;
  if(ile < 1 || ile > b-a+1){
     std::cout<<"Ilosc liczb do wylosowania nie moze byc mniejsza od 1 i wieksza od: "<<b-a+1<<std::endl;
     std::cout<<"Podaj ilosc liczb jeszcze raz."<<std::endl;
     Sleep(2000);
     }
    }while(ile < 1 || ile > b-a+1);
     int *tablica;
     tablica = new int [ile];
     int *wskaznik = tablica;
     losowe_liczby (wskaznik, ile, przedzial, a);
     delete [] tablica;
}

void losowe_liczby (int  *wskaz, int ile, int przedzial, int a){
     srand(time(0));
 for(int nr_indeksu = 0; nr_indeksu < ile; nr_indeksu ++){
    *wskaz = rand()%przedzial + a;
 /* 
 int stala = *wskaz;                 //zapisuje wartosc *wskaz czyli tablicy dla jakiegos indeksu do: stala
     wskaz = wskaz - nr_indeksu;         //wskaznik jest teraz  na zerowym indeksie tablicy
 for(int j = 0; j < nr_indeksu; j ++){
    if(stala == *wskaz){
     wskaz--; 
     break;
      }
     wskaz++; 
     } 
 */
     std::cout<<nr_indeksu+1<<" liczba: "<<*wskaz<<" "<<std::endl;
     wskaz ++;
    }
}
Teraz lepiej wygląda ten kod?
To w komentarzach to próba napisania funkcji aby liczby się nie powtarzały, jednak dla wskaźników nie wygląda to tak kolorowo jak mi się wydawało, zdaje sobie sprawę ze ta funkcja nie ma za bardzo sensu bo wskaźnik raczej nie wraca na swoje miejsce - 1, czasami tak się zdarzy..czasami + funkcja wykonuje się 'ile' razy, próbowałem zmniejszać nr_indeksu ale to też nie pomaga, czasami tylko wypluwa dziwne liczby i losowań jest za dużo, co jest logiczne.
Reasumując, raczej nie mam dobrego pomysłu napisania tego.

A teraz napisze ten pomysł:
mam jakąś liczbę dla danego wskaźnika po czym sprawdzam wcześniejsze zmniejszając wskaz do 0, probowałem zrobić tak, że jeśli liczba się powtórzy to zwraca on wskaz-1 względem tego, który był przed operacją przypisania go do zera, ciężko mi to sensownie opisać, ale wydaje mi się że myślę dobrze, jednak zostaje problem wykonywania się pętli 'ile' razy.
Możecie mi przybliżyć jak to osiągnąć?

-- 1 kwi 2016, o 19:55 --

Mam jeszcze pytanie:
dlaczego zwracanie zera w mainie jest złym pomysłem, sam C++ narzuca nam aby zwracać zero, jeśli to nie jest odpowiednie, to co powinno się tam znajdować?
Afish
Moderator
Moderator
Posty: 2828
Rejestracja: 15 cze 2008, o 15:45
Płeć: Mężczyzna
Lokalizacja: Seattle, WA
Podziękował: 3 razy
Pomógł: 356 razy

[C++] dynamiczne alokowanie pamięci+ wskaźniki

Post autor: Afish »

1. Formatowanie ciągle jest zwalone, głównie wcięcia. Użyj jakiegoś sensownego edytora, używaj tabulacji lub stałej liczby spacji, wyrównaj kod i łatwiej będzie go czytać.
2. Bardzo komplikujesz sobie używanie wskaźników. Dlaczego po prostu nie użyjesz notacji z nawiasami kwadratowymi, tylko przesuwasz wskaźnik?
3. W funkcji losowe_liczby modyfikujesz przyjmowane argumenty, co zazwyczaj utrudnia debugowanie i wprowadza pewien zamęt. Raczej nie powinno się tego robić.
4. Żeby wylosować liczby bez powtórzeń możesz zastosować dwa podejścia:
a) tworzysz na boku tablicę ze wszystkimi możliwymi wartościami do wylosowania, w każdym kroku losujesz element z tej tablicy, przepisujesz go do tablicy wynikowej, usuwasz z tablicy pomocniczej, a samej tablicy pomocniczej zmniejszasz rozmiar o jeden
b) losujesz element, a następnie sprawdzasz, czy taki element już nie występuje — jeżeli tak, to losujesz ponownie
5. Zmienna a ma paskudną nazwę. Nazwa powinna tłumaczyć, do czego dana zmienna służy, pojedyncza literka prawie zawsze jest słabym pomysłem.
6. Uwagi do czystego kodu:
a) funkcja main miesza odpowiedzialności o tyle, że zarówno robi coś konkretnego (pobiera dane, wypisuje je, sprawdza ich poprawność), jak i deleguje część pracy dalej (wywołując losowe_liczby) — lepszym pomysłem w tym przypadku jest delegacja wszystkich odpowiedzialności gdzieś indziej
b) funkcja losowe_liczby przyjmuje zbyt dużo parametrów — wypadałoby z nich wydzielić jakieś sensowne byty
c) losowanie liczb i wypisywanie w jednej funkcji też tak średnio wygląda
Napisz to na przykład tak (pseudokod):

Kod: Zaznacz cały

struct Losowanie{
    int ograniczenie_dolne;
    int ograniczenie_gorne;
    int ile_liczb;
};

int main(){
    Losowanie losowanie = pobierz_dane_od_uzytkownika();
    int *wynik = przeprowadz_losowanie(losowanie);
    wypisz_wynik(wynik);
    delete []wynik;
    return EXIT_SUCCESS;
}

Losowanie pobierz_dane_od_uzytkownika(){
    int a, b;
    // wczytujemy, sprawdzamy, tworzymy wynik
    Losowanie losowanie;
    losowanie.ograniczenie_dolne = a;
    // ...
    return losowanie;
}

int *przeprowadz_losowanie(const Losowanie& losowanie){
    int *liczby = new int[losowanie.ile_liczb];
    losuj_liczby(liczby);
    return liczby;
}

void losuj_liczby(int * const liczby, const Losowanie& losowanie){
    for(int i=0;i<losowanie.ile_liczb;++i){
        int wylosowana;
        do{
            wylosowana = losuj_jedna(losowanie.ograniczenie_dolne, losowanie.ograniczenie_gorne);
        }while(!juz_jest(liczby, i, wylosowana));
        liczby[i] = wylosowana;
    }
}

void losuj_jedna(int od, int do){
     return rand()%cos tam;
}

bool juz_jest(int *tablica, int liczbaElementow, int poszukiwany){
    for(int i=0;i<liczbaElementow;++i){
        if(tablica[i] == poszukiwany){
            return false;
        }
    }
    
    return true;
}

// ...
7. Zamiast zwracania zera w mainie zwracaj EXIT_SUCCESS, bo może trafić Ci się architektura i kompilator, gdzie zwrócenie zera oznacza błąd. ... XIT_status .
Awatar użytkownika
alchem
Użytkownik
Użytkownik
Posty: 252
Rejestracja: 10 cze 2014, o 19:10
Płeć: Mężczyzna
Lokalizacja: Wrocław
Podziękował: 83 razy
Pomógł: 5 razy

[C++] dynamiczne alokowanie pamięci+ wskaźniki

Post autor: alchem »

Dzięki za rady.
Jak będę miał czas, to postaram się w tym wszystkim pogrzebać, z wskaźnikami robię tak a nie inaczej bo tak po prostu widziałem jak ktoś robił w taki sposób.

Ten kawałek kodu powołuje tablice 'ile' ideneksach

Kod: Zaznacz cały

int *tablica;
     tablica = new int [ile];
   

Kod: Zaznacz cały

int *wskaznik = tablica;
a tutaj stwarzam wskaźnik,
robię po to aby pracować na wskaźnikach co jest szybsze, chociaż w takim programie optymalizacja nie jest chyba aż tak konieczna...
No nic czekam mnie zakup książki i tyle.
Xiaos
Użytkownik
Użytkownik
Posty: 26
Rejestracja: 25 paź 2016, o 16:54
Płeć: Mężczyzna
Lokalizacja: Warszawa
Podziękował: 1 raz

[C++] dynamiczne alokowanie pamięci+ wskaźniki

Post autor: Xiaos »

Afish pisze:W C++ zwykło umieszczać się klamrę otwierającą blok w tej samej linii, co instrukcja (if/while/funkcja itp).
Sorry za odkopanie, ale dodam ciekawy punkt widzenia użytkownika 4programmers

kod z kolorowaniem:

Kod: Zaznacz cały

http://wklej.org/id/3095057/
A ja dam przykład kodu w C++ stworzonego przez Microsoft 12 lat temu. Jest to część sterownika działającego w kernel-mode.

Mamy tutaj dwuargumentową funkcję, a w środku 5 linijek faktycznego kodu. Generalnie środowisko programistów zgadza się, że Microsoft tworzy.. dobry kod

Kod: Zaznacz cały

//=============================================================================
NTSTATUS AddDevice
( 
    IN  PDRIVER_OBJECT          DriverObject,
    IN  PDEVICE_OBJECT          PhysicalDeviceObject 
)
/*++
 
Routine Description:
 
  The Plug & Play subsystem is handing us a brand new PDO, for which we
  (by means of INF registration) have been asked to provide a driver.
 
  We need to determine if we need to be in the driver stack for the device.
  Create a function device object to attach to the stack
  Initialize that device object
  Return status success.
 
  All audio adapter drivers can use this code without change.
  Set MAX_MINIPORTS depending on the number of miniports that the driver
  uses.
 
Arguments:
 
  DriverObject - pointer to a driver object
 
  PhysicalDeviceObject -  pointer to a device object created by the
                            underlying bus driver.
 
Return Value:
 
  NT status code.
 
--*/
{
    PAGED_CODE();
 
    NTSTATUS                    ntStatus;
 
    DPF(D_TERSE, ("[AddDevice]"));
 
    // disable prefast warning 28152 because 
    // DO_DEVICE_INITIALIZING is cleared in PcAddAdapterDevice
#pragma warning(disable:28152)
 
    // Tell the class driver to add the device.
    //
    ntStatus = 
        PcAddAdapterDevice
        ( 
            DriverObject,
            PhysicalDeviceObject,
            PCPFNSTARTDEVICE(StartDevice),
            MAX_MINIPORTS,
            0
        );
 
    return ntStatus;
} // AddDevice
Afish
Moderator
Moderator
Posty: 2828
Rejestracja: 15 cze 2008, o 15:45
Płeć: Mężczyzna
Lokalizacja: Seattle, WA
Podziękował: 3 razy
Pomógł: 356 razy

[C++] dynamiczne alokowanie pamięci+ wskaźniki

Post autor: Afish »

Chodzi Ci o klamrę w następnej linii? Najwyraźniej Microsoft ustalił sobie taki sposób formatowania, w firmach to wygląda często inaczej, chociażby po to, aby formatowanie było wspólne między językami. Stroustrup kodzi inaczej: ... Stroustrup
Generalnie środowisko programistów zgadza się, że Microsoft tworzy.. dobry kod
Dobry kod nie jest zasługą formatowania, co innego w drugą stronę.
kalwi
Użytkownik
Użytkownik
Posty: 1931
Rejestracja: 29 maja 2009, o 11:58
Płeć: Mężczyzna
Lokalizacja: Warszawa
Podziękował: 145 razy
Pomógł: 320 razy

[C++] dynamiczne alokowanie pamięci+ wskaźniki

Post autor: kalwi »

Afish pisze: 7. Zamiast zwracania zera w mainie zwracaj EXIT_SUCCESS, bo może trafić Ci się architektura i kompilator, gdzie zwrócenie zera oznacza błąd. .
No nie bardzo
Finally, control is returned to the host environment. If the value of status is zero or EXIT_SUCCESS, an implementation-defined form of the status successful termination is returned
Akurat 0 dla std::exit() zawsze oznacza zakończenie pracy bez błędu, niezależnie od architektury - tako rzecze standard C (i pod tym względem C++ zachowuje z nim pełną zgodność).
Oczywiście nie oznacza to, że zero jest tożsame z EXIT_SUCCESS, bo to nie jest prawda. Ale pisanie return 0; jest jak najbardziej prawidłowe dla każdej architektury.

-- 24 kwi 2017, o 20:44 --
Afish pisze:3. Nie weryfikujesz rozmiaru tablicy przed utworzeniem, co będzie, gdy podasz tam liczbę ujemną?
Równie dobrze można by się spytać, czemu korzysta ze wskaźników zwykłych, zamiast sprytnych.
Albo czemu nie korzysta od razu z vectora. I czemu nie robi tutaj bloku try-catch.
Afish pisze:5. NULL też wygląda staro, od paru lat jest nullptr albo po prostu zero.
Przecież NULL jest zdefiniowany jako wskaźnik uniwersalny na zero ((void*)0) (w języku C) albo zdefiniowany jako 0 - także "albo po prostu zero" jest zbyteczne. Od C++11 zawsze powinno się używać nullptr.

-- 24 kwi 2017, o 20:47 --
alchem pisze:Ten kawałek kodu powołuje tablice 'ile' ideneksach

Kod: Zaznacz cały

int *tablica;
     tablica = new int [ile];
   

Kod: Zaznacz cały

int *wskaznik = tablica;
a tutaj stwarzam wskaźnik,
robię po to aby pracować na wskaźnikach co jest szybsze, chociaż w takim programie optymalizacja nie jest chyba aż tak konieczna...
No nic czekam mnie zakup książki i tyle.
A zamiast tego można po prostu napisać

Kod: Zaznacz cały

 int *tablica = new int[ile];
I tablica to jest wskaźnik na pierwszy element tej tablicy
ODPOWIEDZ