logo elektroda
logo elektroda
X
logo elektroda
Adblock/uBlockOrigin/AdGuard mogą powodować znikanie niektórych postów z powodu nowej reguły.

[C]Struktury a zmiana wartości pól

maly_elektronik 24 Sie 2011 23:23 2012 15
  • #1 9859849
    maly_elektronik
    Poziom 23  
    Witam,
    napotkałem dzisiaj na problem strike natury programowej (w sumie go rozwiązałem ale jestem dociekliwy dlatego pytam) :)

    Tworze sobie strukturę:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    I teraz gdy chce przypisać do obiektu (na który wskazuje *wsk1)
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    Kompilator nie "krzyczy" a mimo to program nie działa
    ale jeśli zrobię tak:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod

    i tak:
    Kod: C / C++
    Zaloguj się, aby zobaczyć kod


    To wszystko jest ok :) Dlaczego tak się dzieje :?:
  • #2 9859874
    kiziu13
    Poziom 17  
    Zadeklarowanie wskaźnika nie powoduje przydzielenia pamięci dla niego, toteż w pierwszym wypadku nie masz po czym pisać, gdyż jedynie masz wskaźniki nie wskazujące na nic. W drugim wypadku stworzyłeś sobie zmienne, które to już mają przydzieloną pamięć.
  • #3 9860084
    maly_elektronik
    Poziom 23  
    To dlaczego taki kod nie jest uznawany przez kompilator jako błąd :?:
    Przecież wskazuje na coś co nie istnieje :)
  • #4 9860125
    mirekk36
    Poziom 42  
    maly_elektronik napisał:
    To dlaczego taki kod nie jest uznawany przez kompilator jako błąd :?:
    Przecież wskazuje na coś co nie istnieje :)


    Bo to nie jest błąd, dla kompilatora to jest rozkaz, że ma zapisać coś pod adres w pamięci RAM, na który wskazuje wskaźnik. A wskaźnik, jako że został domyślnie zainicjalizowany wartością ZERO, to wskazuje na początek pamięci RAM. Kompilatorowi to wsio rybka, programista każe - sługa robi. Tylko że programista musi wiedzieć jak to działa - bo błędy popełniane podczas działań ze wskaźnikami, wynikające z braku wiedzy jak one działają prowadzą właśnie do najdziwaczniejszych baboli w programach.

    To zresztą podobny przypadek jak np z tablicą:

    char tab[2] = {'a', 'b', 'c'};
    char *w=tab;

    dlaczego kompilator nie będzie krzyczał gdy dokonasz takiej np operacji ???:

    w+=7;
    tab[*w] = 'z';

    Bo w takich przypadkach ty sam musisz się pilnować i wiedzieć dokładnie jak to zadziała, a sens ma to również jeśli wskaźnika byś nawet nie zainicjalizował. Z tym, że w pierwszym przypadku nadpiszesz jakiś obszar pamięci w przód za tablicą (7-my element tablicy której nie ma przecież już w tym miejscu), albo jeśli wskaźnik będzie = 0 bo zdefiniowany jest w obszarze zmiennych globalnych, to nadpiszesz dokładnie 8-mą komórkę pamięci RAM.
  • #5 9860353
    maly_elektronik
    Poziom 23  
    Rozumie swój błąd:)
    Chodziło mi tylko o to dlaczego kompilator nie zwraca na to uwagi :)
    Teraz już wiem (chodź dobrze by było gdyby dawał przynajmniej jakiegoś warninga albo jakąś informacje że kod może nie działać poprawnie) :)
  • #6 9860429
    gaskoin
    Poziom 38  
    Wtedy przy każdym przypisaniu by musiał dawać warninga co było by dość irytujące. Możesz dopisać do gcc sprawdzanie czy wskaźnik był wcześniej zainicjalizowany poprawnym adresem - źródła są otwarte.
  • #7 9860582
    tmf
    VIP Zasłużony dla elektroda
    Tylko skąd kompilator ma wiedzieć jaki adres jest poprawny a jaki nie? Tym bardziej, że z punktu widzenia języka każdy adres jest poprawny, nawet NULL (co zresztą jest wykorzystywane w makrze offsetof).
    Do tego, co koledzy napisali dodałbym tylko, że pamięć przydzielona wskaźnikowi (malloc i spółka), musi zostać zwolniona (free, malloc). Ten problem da się częściowo rozwiązać automatycznie w C++ stosując tzw. smart pointers.
    mirekk36 - one domyślnie są inicjowane na 0 tylko w pewnych warunkach, ogólnie to założenie jest ryzykowne.
  • #8 9860587
    mirekk36
    Poziom 42  
    maly_elektronik napisał:
    Rozumie swój błąd:)
    Chodziło mi tylko o to dlaczego kompilator nie zwraca na to uwagi :)
    Teraz już wiem (chodź dobrze by było gdyby dawał przynajmniej jakiegoś warninga albo jakąś informacje że kod może nie działać poprawnie) :)


    Spróbuj sobie zdeklarować taki wskaźnik ale nie w obszarze zmiennych globalnych tylko np w funkcji main() czy innej.

    Wtedy dostaniesz warninga - może nie o tym, że przekraczasz zakres ale na pewno o tym, że wskaźnik nie został zainicjalizowany.

    Natomiast jeśli wiemy, że w obszarze zmiennych globalnych wszystko jest inicjalizowane zerem - mamy tego pełną świadomość, to dla kompilatora jest to też jasne - użytkownik może chce zrobić właśnie taką operację, nie będę mu przeszkadzał warningiem ;)

    Dodano po 12 [minuty]:

    tmf napisał:

    mirekk36 - one domyślnie są inicjowane na 0 tylko w pewnych warunkach, ogólnie to założenie jest ryzykowne.


    Hmm nie będę się sprzeczał oczywiście z fachowcem, ale możesz wyjaśnić dlaczego? przecież deklarowane wskaźniki w obszarze zmiennych globalnych powinny być tak jak i one zerowane. No ale może mi się to tylko wydaje bo czegoś jeszcze nie doczytałem.

    ale zaraz, tu na stronce

    http://www.nongnu.org/avr-libc/user-manual/FAQ.html#faq_varinit

    , jest jednak napisane:

    Cytat:
    Global and static variables are guaranteed to be initialized to 0 by the C standard. avr-gcc does this by placing the appropriate code into section .init4 (see The .initN Sections). With respect to the standard, this sentence is somewhat simplified (because the standard allows for machines where the actual bit pattern used differs from all bits being 0), but for the AVR target, in general, all integer-type variables are set to 0, all pointers to a NULL pointer, and all floating-point variables to 0.0.


    więc chyba jednak zawsze wskaźniki będą miały wartość NULL (zero)
  • #9 9860697
    gaskoin
    Poziom 38  
    tmf napisał:
    Tylko skąd kompilator ma wiedzieć jaki adres jest poprawny a jaki nie? Tym bardziej, że z punktu widzenia języka każdy adres jest poprawny, nawet NULL (co zresztą jest wykorzystywane w makrze offsetof).
    Do tego, co koledzy napisali dodałbym tylko, że pamięć przydzielona wskaźnikowi (malloc i spółka), musi zostać zwolniona (free, malloc). Ten problem da się częściowo rozwiązać automatycznie w C++ stosując tzw. smart pointers.
    mirekk36 - one domyślnie są inicjowane na 0 tylko w pewnych warunkach, ogólnie to założenie jest ryzykowne.


    No właśnie o to chodziło, że to wcale nie jest takie proste, bo gdyby było to programiści gcc już dawno by coś takiego zaimplementowali.

    @mirekk36 To jest trochę bardziej skomplikowane niż przypisanie adresu 0 takim wskaźnikom. W rzeczywistości NULL pointer może wskazywać miejsce, które na pewno nie będzie nigdy zainicjalizowane i nie koniecznie musi to być adres 0x00
  • #10 9860783
    mirekk36
    Poziom 42  
    gaskoin --> ale zamiast pisać tłumaczenia typu: "że to nie takie proste", że to trochę bardziej skomplikowane", "że jakby coś było proste to ktoś już dawno być coś zrobił" - przeczytaj jeszcze raz ten tekst dokładnie:

    Cytat:
    With respect to the standard, this sentence is somewhat simplified (....), but for the AVR target, in general, all integer-type variables are set to 0, all pointers to a NULL pointer, and all floating-point variables to 0.0.


    Więc ja co najwyżej mogłem popełnić jak zwykle (u mnie) złe założenie, że pytanie autora dotyczy AVR GCC. Bo jeśli to nie prawda - to zgadzam się, że wskaźniki nie zawsze być może będą zainicjalizowane zerami. Ale tu jest to jasno napisane. Więc jeśli coś wiesz dokładniej to napisz konkretniej. Zamiast opowiadać, że coś nie jest takie proste. Wszystko jest proste tylko trzeba wiedzieć jak działa. A nie opierać się na domysłach.
  • #11 9860926
    tmf
    VIP Zasłużony dla elektroda
    No właśnie o to mi chodziło:
    - po pierwsze tak jest dla avr-gcc, ale C to nie tylko ten kompilator,
    - po drugie NULL to ma być unikalna wartość o której wiadomo, że nie zostanie użyta i w efekcie w ten sposób można wyróżnić wskaźnik nie przypisany niczemu, ale nie musi to być wartość 0,
    - ale najważniejsze - na co sam sobie odpowiedziałeś - domyślnie 0 będą miały wskaźniki globalne, ale wskaźniki utworzone lokalnie już nie. A że wskaźniki to niebezpieczne bestie to ja jednak wolę nadmiarowo je inicjować w każdej sytuacji.
    Tym bardziej, że sens stosowania wskaźników globalnych (podobnie jak zmiennych globalnych) jest wątpliwy (z wyjątkiem pewnych okoliczności, np. wskaźniki na zasoby systemowe).
  • #12 9861013
    gaskoin
    Poziom 38  
    mirekk36 napisał:
    gaskoin --> ale zamiast pisać tłumaczenia typu: "że to nie takie proste", że to trochę bardziej skomplikowane", "że jakby coś było proste to ktoś już dawno być coś zrobił" - przeczytaj jeszcze raz ten tekst dokładnie:


    Przecież zacytowałem @tmf, że o to co napisał mi chodziło. Nie podważyłem tego, że inaczej działa to co napisałeś.

    Przy okazji - w tekście który mi cytujesz jest jeden bardzo istotny w tym co piszę fragment:

    Cytat:
    With respect to the standard, this sentence is somewhat simplified (....), but for the AVR target, in general, all integer-type variables are set to 0, all pointers to a NULL pointer, and all floating-point variables to 0.0.


    Bo tak naprawdę wg standardu języka C, null pointer to wskaźnik który różni się wartością od wszystkich innych wskaźników i jest nierówny wskaźnikom do jakiegokolwiek obiektu lub funkcji (nie jest to adres to żadnego z nich). I wcale niekoniecznie musi to być ostatecznie wartość równa zero. A niezainicjalizowany wskaźnik to już coś innego -> bo on może wskazywać na cokolwiek i nie ma nic wspolnego z null pointerem.

    I pisałem ogólnie, a nie o AVR-GCC bo C nie ogranicza się jedynie do tych układów

    Wystarczą Ci takie wyjaśnienia czy chcesz więcej szczegółów (przy okazji komentując, że to wszystko wyssane z palca i to tylko moje domysły :) ) ?
  • #13 9861066
    mirekk36
    Poziom 42  
    Posłuchajcie panie gaskoin z wami też nie mam zamiaru się kłócić czy sprzeczać. Za to jak zwykle wybucha dyskusja o tym co się dzieje w innych przypadkach i kompilatorach języka C.

    Tymczasem pytanie zostało zadane w konkretnym dziale tego forum:

    Mikrokontrolery AVR

    warto na to też zwrócić uwagę - bo ja udzieliłem konkretnej odpowiedzi na konkretne pytanie. I tylko z tego powodu wydały mi się dziwne twoje tłumaczenia, że "to trochę bardziej skomplikowane, że coś tam coś tam". W AVR GCC wskaźniki będą miały wartość ZERO i już.
  • #14 9861080
    michalko12
    Specjalista - Mikrokontrolery
    maly_elektronik napisał:
    Rozumie swój błąd:)
    Chodziło mi tylko o to dlaczego kompilator nie zwraca na to uwagi :)
    Teraz już wiem (chodź dobrze by było gdyby dawał przynajmniej jakiegoś warninga albo jakąś informacje że kod może nie działać poprawnie) :)


    Takie rzeczy to tylko w JAVIE ;)
  • #15 9862320
    Fredy
    Poziom 27  
    Dla ciekawości CodeVision wyświetla warninga.
  • #16 9862537
    tmf
    VIP Zasłużony dla elektroda
    Wyświetla warninga, że używasz niezainicjowanej zmiennej, a nie, że wskaźnik wskazuje na nie wiadomo co. W takiej sytuacji gcc też warninga zasygnalizuje.
REKLAMA