Panel użytkownika
Nazwa użytkownika:
Hasło:
Nie masz jeszcze konta?

Wskaźniki

Ostatnio zmodyfikowano 2009-09-29 17:44
Autor Wiadomość
malan
Temat założony przez niniejszego użytkownika
Wskaźniki
» 2009-09-28 22:16:37
Witam.
Zacząłem się bawić wskaźnikami i mam 2 pytania dotyczące poniższego kodu:

1. Czy taki sposób wyświetlania 'wartości' wskaźnika jest poprawny ?
C/C++
for( int i = 0; i < Ilosc - 1; i++, twsk++ )
{
    std::cout << "Pytanie " << i + 1 << std::endl << std::endl;
   
    std::cout <<(( * twsk ) ).Pyt << std::endl;
    std::cout <<(( * twsk ) ).O1 << std::endl;
    std::cout <<(( * twsk ) ).O2 << std::endl;
    std::cout <<(( * twsk ) ).O3 << std::endl;
    std::cout <<(( * twsk ) ).O4 << std::endl;
    std::cout <<(( * twsk ) ).POdp << std::endl;
    std::cout << std::endl;
   
}

2. Czy da się zrobić tak żeby nie wczytywać danych z pliku do tablicy, tylko od razu do wskaźnika (czy w ogóle to możliwe) ? Szukałem na google, przejrzałem wiki, ale chyba nikt tak nie robił.

Cały kod:
C/C++
#include <iostream>
#include <string>
#include <fstream>

struct Pytania
{
    std::string Pyt;
    std::string O1;
    std::string O2;
    std::string O3;
    std::string O4;
    std::string POdp;
};

void Read_from_file( std::string file_name );
void Console_write( void );

Pytania tPytania[ 69 ];
Pytania * twsk = tPytania;
unsigned int Ilosc = 0;

int main()
{
    Read_from_file( "pytania.txt" );
    Console_write();
   
    getchar();
    return 0;
}

void Read_from_file( std::string file_name )
{
    std::fstream file;
    file.open( file_name.c_str(), std::ios::in );
   
    if( file.good() )
    {
        while( !file.eof() )
        {
            getline( file, tPytania[ Ilosc ].Pyt );
            getline( file, tPytania[ Ilosc ].O1 );
            getline( file, tPytania[ Ilosc ].O2 );
            getline( file, tPytania[ Ilosc ].O3 );
            getline( file, tPytania[ Ilosc ].O4 );
            getline( file, tPytania[ Ilosc ].POdp );
           
            Ilosc++;
           
        }; //while
    } //if
};

void Console_write( void )
{
    for( int i = 0; i < Ilosc - 1; i++, twsk++ )
    {
        std::cout << "Pytanie " << i + 1 << std::endl << std::endl;
       
        std::cout <<(( * twsk ) ).Pyt << std::endl;
        std::cout <<(( * twsk ) ).O1 << std::endl;
        std::cout <<(( * twsk ) ).O2 << std::endl;
        std::cout <<(( * twsk ) ).O3 << std::endl;
        std::cout <<(( * twsk ) ).O4 << std::endl;
        std::cout <<(( * twsk ) ).POdp << std::endl;
        std::cout << std::endl;
       
    }
   
    return;
};
P-10533
DejaVu
» 2009-09-28 22:28:17
1. Tak.
2. Rozwiązanie, które masz wygląda na pierwszy rzut oka na poprawne. Jeśli chcesz nie mieć ograniczonej ilości rekordów, polecam zapoznać się z tematyką listy. Innym i znacznie wygodniejszym rozwiązaniem będzie zastosowanie STL'a, a konkretniej vector'a. Nawiązując do wskaźnika... możesz zarezerwować sobie dynamicznie pamięć, np.
dane * ble = new dane[ 123 ];
Nie można jednak pisać do wskaźnika niezainicjowanego. Będzie on wskazywał na nieprawidłowy adres w pamięci i w momencie gdy nastąpi pisanie aplikacja się posypie z powodu naruszenia ochrony pamięci.
P-10534
malan
Temat założony przez niniejszego użytkownika
» 2009-09-30 23:35:04
Dzięki wielkie za wyjaśnienie ;).
@Ten zuy: przetestuję Twoje rozwiązanie.
P-10555
manfred
» 2009-09-29 17:44:02
Nie można jednak pisać do wskaźnika niezainicjowanego. Będzie on wskazywał na nieprawidłowy adres w pamięci i w momencie gdy nastąpi pisanie aplikacja się posypie z powodu naruszenia ochrony pamięci.
Bujda na resorach - taka sytuacja to UB (undefined behavior), może się stać cokolwiek, przykładowo może wykoleić się tramwaj. Zgoda, w większości przypadków program pójdzie w buraki - jednakże, należy sprawdzić w słowniku znaczenie słowa większość - nie oznacza to zawsze. Czasem może się zdarzyć, że jednak taki wskaźnik będzie miał poprawną wartość:
C/C++
#include <cstdio>
#include <cstring>
#pragma function(memcpy)

int a = 69;
int * zuo = & a;

void __declspec( noinline ) foo()
{
    int * foo;
    std::printf( "%d\n", * foo );
    * foo = 666;
}

int main()
{
    int b;
    for( volatile int a = 0; a < 10; )
    {
        std::memcpy( & b - 1, & zuo, sizeof( int ) );
    }
    foo();
    std::printf( "%d\n", a );
}
Jak widać, mamy sobie funkcję foo(), która odczytuje z niezainicjalizowanego wskaźnika, po czym do niego pisze. Super, powinno się wysypać. Ale musimy pamiętać, że mamy pętlę piszącą po pamięci. Pod clem stało się takie coś, że ta pętla zrobiła jeden obrót (czyli z tego wynika, że za zmienną "b" kompilator wstawił licznik pętli, który z racji swojego volatile, w sumie musiał być wstawiony na stos), po czym program wszedł do funkcji foo(). Teraz się dzieje najlepsza jazda - jako rejestr, do którego będzie odczytywał licznik pętli kompilator wybrał ECX. Jeśli funkcja alokuje na stosie tylko cztery bajty, jak w tym przypadku foo(), to zaczyna się od "push ecx" (bo jest mniejsze i szybsze niż "sub esp, 4"). Khem, czyli dostajemy wartość nadpisanego licznika pętli (który ma wartość taką, jak globalna zmienna zuo) na stos, po czym dereferujemy ten adres. Co to powoduje? Adres jest stuprocentowo poprawnym wskaźnikiem na zmienną "a", czyli nastąpi odczyt z niej (powinno wypisać więc za pierwszym razem 69), a następnie zapis do niej. Po powrocie do main() wypisujemy wartość zmiennej "a" - hurra, udało się zmienić, pokazało 666! Czyli z tym wykrzaczeniem się nie jest tak do końca...

Oczywiście to jest sztuczny przykład, ale... No wiecie, zawsze jest jakieś ale ;) - pokazuje, co się może stać, jak zakładamy, że niezainicjalizowany wskaźnik przy dereferencji wykrzaczy nam program - wystarczy popełnić jeden z najczęstszych bugów pt. "nadpisanie pamięci", a wskaźnik taki może nam działać, przez co bug będzie siedział ukryty (bo programiści lubią olewać warningi kompilatora, który w takiej sytuacji ostrzega) i uwjawni się w najmniej odpowiednim momencie...

Inicjacja to może być członka zakonu, zmienne się inicjalizuje.
P-19393
« 1 »
  Strona 1 z 1