Elektroda.pl
Elektroda.pl
X

Search our partners

Find the latest content on electronic components. Datasheets.com
Elektroda.pl
Please add exception to AdBlock for elektroda.pl.
If you watch the ads, you support portal and users.

GCC problem z tablicą wskaźników

maly_elektronik 08 Aug 2010 11:15 3986 34
Computer Controls
  • #1
    maly_elektronik
    Level 23  
    Witam :)

    Spotkałem się ostatnio z dziwnym problemem dotyczącym tablicy wskaźników :)
    Mianowicie program wygląda następującą (w skróconej formie)
    Code:

    #include <stdio.h>
    int main(void)
    {
     char *tabela[3];
    funkcja(&tabela);

    printf("%s\n", tabela[0]);
    printf("%s\n", tabela[1]);
    return 0;
    }
    void funkcja(char *x[])
    {
     x[0] = "nazwa1";
     x[1] = "nazwa2";
    }


    Program polega na tym iż do tabeli (przekazywanej jako argument funkcji) zapisywane kolejno są nazwy (char*). Na PC (Linux GCC) wszystko działa bez najmniejszego problemu a gdy podobna sytuacja pojawia się w avr'ku to tablica jest pusta :(

    Czy wie ktoś może w czym leży problem :?:

    Pozdrawiam maly_elektronik
  • Computer Controls
  • #2
    tmf
    Moderator of Microcontroller designs
    x jest tablicą wskaźników o typie char. Nie możesz wykonać przypisania x[0]="costam", bo to costam jest tworzone lokalnie w funkcji i poza nią nie istnieje. To costam musiałoby byc static, żeby to miało jakiś sens. Inaczej trzeba dynamicznie alokować pamięć i kopiować przez strcpy. Na PC ci to pozornie działa bo PC ma sporo pamięci i szansa, że cośtam ci od razu zamaże napis jest mała. Niemniej to prędzej czy później skończy się błędem naruszenia ochrony pamięci.
  • #3
    nsvinc
    Level 35  
    Wskazniki...

    char *tabela[3];
    W tym przypadku "tabela" jest wskaznikiem na wskaznik na pierwszy
    element tabeli, czyli stworzyles trzyelementowa tablice wskaznikow na char
    Pamietaj, ze sama "tabela" bez dereferencji poprzez [ ] lub * jest WSKAZNIKIEM.
    Do funkcji operujacej na tej tabeli musisz przekazac TEN WSKAZNIK, a nie
    jego adres (tak jak masz oryginalnie napisane z &)

    Code:

    #include <stdio.h>

    int main(void)
    {
    char *tabela[3];
    funkcja(tabela); //bez &, bo masz przekazac 'tabela', a nie wskaznik na nia

    printf("%s\n", tabela[0]);
    printf("%s\n", tabela[1]);
    return 0;
    }

    void funkcja(char **x)
    {
     x[0] = "nazwa1";
     x[1] = "nazwa2";
    }


    ->tmf
    Zdaje mi sie, ze nie masz racji. "nazwa1" i "nazwa2" sa constami, czyli
    caly string jeden i drugi jest we flashu, ktorego nie da sie zamazac.
    Nie trzeba alokowac nic! ANSI C traktuje tak zadeklarowanego stringa
    jako wskaznik na pierwszy znak, no a pod wskaznikiem masz flasha, w ktorym
    sa pokolei sa poukladane znaki...
  • Helpful post
    #4
    tmf
    Moderator of Microcontroller designs
    Bzdura, czy przekażesz do funkcji tabela, czy &tabela wyjdzie na to samo - poczytaj o tym jak są przekazywane tabele jako argumenty funkcji w C.
    A problem leży w tym o czym już pisałem - globalny wskaźnik nie może wskazywać na dane lokalne, które nie są zaalokowane dynamicznie lub ich czas życia nie jest nieskończony jak w przypadku static. Zastanów się na co mają wskazywać te wskaźniki po opuszczeniu funkcji funkcja...

    Dodano po 3 [minuty]:

    nsvinc wrote:

    ->tmf
    Zdaje mi sie, ze nie masz racji. "nazwa1" i "nazwa2" sa constami, czyli
    caly string jeden i drugi jest we flashu, ktorego nie da sie zamazac.
    Nie trzeba alokowac nic! ANSI C traktuje tak zadeklarowanego stringa
    jako wskaznik na pierwszy znak, no a pod wskaznikiem masz flasha, w ktorym
    sa pokolei sa poukladane znaki...


    Bzdura do kwadratu. Żeby coś było const trzeba to jako const zadeklarować. Samo const nie zmienia też zakresu widoczności zmiennej, ona poza funkcją jest niewidoczna! Podobnie jak to, że tworzone są kopie tych literałów w SRAM, nie jest pobierany żaden wskaźnik do FLASH! Skąd ty bierzesz takie informacje?
    Można wykorzystać fakt, że one istnieją we FLASH deklarując je z atrybutem PROGMEM i potem pobierać wskaźnik - będzie on wskazywał na FLASH i z natury dane na które będzie wskazywał są nieulotne, tyle, że wtedy printf tego w życiu nie odczyta - ta funkcja operuje wyłącznie na wskaźnikach do SRAM.
  • Computer Controls
  • #5
    maly_elektronik
    Level 23  
    Dopiero Twój drugi post pomógł zrozumieć mi problem :)
    W takim przypadku po prostu musze stworzyć tabele w której najpierw zapisze zmienna a dopiero później będę mógł się do nich odwołać przy pomocy wskaźnika :)

    P.S. dla sprostowania w/w przykład jest pisany w C dla pc tak więc printf nie ma nic wspólnego z progmem występującym w gcc dla AVR :)
  • #6
    nsvinc
    Level 35  
    ->tmf

    Definitywnie NIE. Mozesz miec racje co do istnienia wskaznika na "nazwa"
    po wyjsciu z funkcji (z doswiadczenia wiem ze nie znika, o ile sie rzutnie na
    (const char)"nazwa", przynajmniej na ARMach kompilator Keil)...
    O dzizas, to sa AVRy...

    Na pewno "tabela" i "&tabela" to NIE TO SAMO. Wskaznik"tabela"
    gdzies JEST w pamieci, nie? Ten adres znajduje sie POD KONKRETNYM
    ADRESEM W PAMIECI. I teraz wg. ciebie, JAK zdobyc ten adres, jesli nie
    poprzez &?... Jesli jest tak jak mowisz, to dziwne ze dziala mi juz 10000
    kodow opartych na wskaznikach w ten sposob, i kompilator generuje
    w 100% poprawnego asma...

    Jest tak:

    unsigned char *tabela[3];

    void funkcja(unsigned char ***tab) //potrojny, jesli przekazujemy adres wskaznika na wskaznik
    {
    *tab[0]=(const unsigned char)"tekst"; //dodatkowa dereferencja, zeby pozbyc sie adresu
    }

    void main(void)
    {
    funkcja(&tabela); //tu podajemy ADRES wskaznika na wskaznik, czyli z &
    }
  • #7
    tmf
    Moderator of Microcontroller designs
    Lepiej napisz co chcesz osiągnąć. Jeśli to mają być stałe to lepiej je zadeklarować z atrybutem PROGMEM, żeby nie zabierały RAMu. Wtedy tablicę możesz zainicjalizować na etapie kompilacji. Jest tylko jeden problem - musisz wtedy korzystać z funkcji operujących na wskaźnikach do FLASH, klasyczne będą odczytywać dane z RAM używając wskaźnika na FLASH co do niczego dobrego nie doprowadzi.

    Dodano po 10 [minuty]:

    nsvinc wrote:
    ->tmf

    Definitywnie NIE. Mozesz miec racje co do istnienia wskaznika na "nazwa"
    po wyjsciu z funkcji (z doswiadczenia wiem ze nie znika, o ile sie rzutnie na
    (const char)"nazwa", przynajmniej na ARMach kompilator Keil)...
    O dzizas, to sa AVRy...

    Na pewno "tabela" i "&tabela" to NIE TO SAMO. Wskaznik"tabela"
    gdzies JEST w pamieci, nie? Ten adres znajduje sie POD KONKRETNYM
    ADRESEM W PAMIECI. I teraz wg. ciebie, JAK zdobyc ten adres, jesli nie
    poprzez &?... Jesli jest tak jak mowisz, to dziwne ze dziala mi juz 10000
    kodow opartych na wskaznikach w ten sposob, i kompilator generuje
    w 100% poprawnego asma...

    Jest tak:

    unsigned char *tabela[3];

    void funkcja(unsigned char ***tab) //potrojny, jesli przekazujemy adres wskaznika na wskaznik
    {
    *tab[0]=(const unsigned char)"tekst"; //dodatkowa dereferencja, zeby pozbyc sie adresu
    }

    void main(void)
    {
    funkcja(&tabela); //tu podajemy ADRES wskaznika na wskaznik, czyli z &
    }


    Nie znika ci bo jak zadeklarujesz literał jako const, ba, nawet jeśli go zadeklarujesz bez const na procu o architekturze von Neumanna to on zawsze istnieje bo jest we FLASH - wskaźnik na niego zawsze więc wskazuje poprawnie - ale to tylko jeśli mówimy o mikrokontrolerach, w których program siedzi we FLASH/SRAM ale bez ochrony pamięci. Na PC użycie takiego wskaźnika spowoduje błąd naruszenia ochrony pamięci (byćmoże nie od razu) - wskaźnik na dane nie może wskazywać na segment kodu bez zaznaczonego atrybutu, że jest to segment danych. W tym celu na PC trzebaby zrobić dodatkowy wpis do tabeli deskryptorów z segmentem RO będącym aliasem segmentu kodu. Ale to strasznie niskopoziomowe działanie i do niczego dobrego nie prowadzi. Na szczęście też niedozwolone poza trybem jądra.
    tabela i &tabela to to samo! W C takie odwołanie powoduje zawsze zwrócenie adresu tabeli, dodanie & nie zwraca przecież adresu na adres... To są podstawy C, zaglądnij do dowolnej książki. A działa ci to bo to są równoważne operacje - możesz sobie zadeklarować nawet wskaźnik na wskaźnik na wskaźnik na wskaźnik i stosować potrójną dereferencję, też to zadziała, a że jest bez sensu to inna sprawa.
  • #8
    nsvinc
    Level 35  
    ARM JEST O ARCHITEKTURZE HARVARD!!!!

    wskaznik i &wskaznik nie moze byc tym samym, sam napisales
    ze & powoduje zwrocenie ADRESU tabeli. A z tego wynika, ze
    &tabela jest adresem tabeli, czyli wskaznikiem(&) na wskaznik(tabele) na
    wskaznik na char...

    Z tego co mnie uczyli, &cokolwiek to jest adres tego cokolwiek. Jesli
    cokolwiek jest wskaznikiem, to &cokolwiek jest wskaznikiem na wskaznik.
    Musi tak dzialac, bo jesli funkcja modyfikuje (np. korzystajac z realloc) globalny
    wskaznik na wskaznik, to trzeba podac WSKAZNIK na ten pierwszy
    WSKAZNIK, ktory ma zostac zmieniony, tak aby funkcja pracowala
    na konkretnym wskazniku a nie na jego kopii!

    W k&r2 nie napisali ze &costam i costam to jest to samo, wiec
    NIE JEST. Nie polecam slepo wierzyc nowoczesnej podrecznikowej
    literaturze, bo efektem jest to, ze nawet na studiach ucza ze aby
    gonic po zmienna[a][b] przez wskaznik to musi byc on podwojny (co jest
    bzdura)
  • #9
    maly_elektronik
    Level 23  
    Potrzebuje przechwytywać do tablicy nazwy plików wydobytych z FAT32 na karcie SD :)
    Tak więc FLASH nie wchodzi w grę :(
    Wygląda to tak:
    w pętli program odczytuje dane z sektorów -> funkcja wyodrębnia istotne informacje i zapisuje do zmiennej (która z każdym powtórzeniem pętli zmienia swoja zawartosć)-> zmienna(z nazwą) ma zostać zmagazynowana w tabeli :)
  • #10
    tmf
    Moderator of Microcontroller designs
    Zrozum, że w C przekazywanie tabeli do funkcji ZAWSZE odbywa się przez referencję, nigdy przez wartość. Stąd w tym przypadku przekazanie do funkcji tabela lub &tabela jest dokładnie tym samym.
    Podobnie jak void *ptr=tabela to dokładnie to samo co void *ptr=&tabela, lub void *ptr=&tabela[0]; Jak nie wierzysz to spobi eto skompiluj i zerknij na skompilowany program.
    Co do architektury - małe ARMy mają architekturę von Neumanna. Dopiero ARM 9 ma Harvardzką, a i tak w ograniczonym zakresie - w architekturze Harvardzkiej przestrzenie adresowe danych i programu są rozdzielone, on o ile się nie mylę ma tylko rozdzielone cache dla programu i danych. W każdym razie FLASH w żadnym ARMie nie tworzy odrębnej przestrzeni adresowej tak jak w AVR, tylko jest widoczny w normalnej liniowej przestrzeni adresowej procesora. Stąd nie występują tu pewne AVR-specyficnze problemy z adresacją.
    maly_elektronik - jest kilka rozwiązań twojego problemu - tak jak ty chcesz zrobić wymagałoby wykonania x[0]=malloc(strlen(tekst)+1); strcpy(x[0],tekst); Mozesz też mocno uprościć sprawę przyjmując nazewnictwo na FAT32 w stylu 8+3 (pominąć długie nazwy, co zresztą wiele bibliotek i tak robi) i zdeklarować tablicę jako tablicę na łańcuchy 12-elementowe. Odpada wtedy dynamiczna alokacja pamięci.
  • #11
    nsvinc
    Level 35  
    Tak, rdzenie ARMv4 i starsze sa von Neumann. Wszystkie nowsze
    (ARMv5,ARMv6,ARMv7 i ich rozne modyfikacje) sa harvardzkie.
    A ja nigdy nie pracowalem na procku z rdzeniem ARMv4 lub starszym
    (tj. np. ARM7) wiec ich nie znam :D

    a na jakiej podstawie tak twierdzisz? :]
    No to patrz :D

    Code:

    //smieci
    #define TOKENLIST1 6
    u8 *tokenlist1[TOKENLIST1]={{"start"},{"stop"},{"continue"},{"filter"},{"buffer"},{"baud"}};
    u8 tokenxor1[TOKENLIST1];

    // deklaracja funkcji
    u8 znajdz_token(u8 **tlist, u8 *txor, u8 tile, u8 *buf, u16 *ofs, u16 mod);

    //uzycie
    y=znajdz_token(tokenlist1, tokenxor1, TOKENLIST1, (u8*)uart.rx, &jyb, URX_BUFSIZE); //DZIALA i kompiluje sie

    y=znajdz_token(&tokenlist1, tokenxor1, TOKENLIST1, (u8*)uart.rx, &jyb, URX_BUFSIZE); //NIE DZIALA, nie kompiluje sie i otrzymuje errora:
    argument of type u8 *(*)[6] is incompatible with parameter of type u8***


    Jeszcze jakies pytania? :D To jest dowod na to ze mam racje, i dowolny
    identyfikator z & zwraca zawsze adres. To samo dotyczy przeciez
    funkcji...

    Code:


    unsigned char (*wsk_na_funkcje)(unsigned char x);

    unsigned char func(unsigned char x)
    {
    return(x*x);
    }

    wsk_na_funkcje=&func; //dziala, dzialalo i bedzie dzialac

    //i uzyj sobie tej funkcji spod wskaznika...

    unsigned char wynik=(*wsk_na_funkcje)(4); //tak samo dziala o ile kompilator sam przesunie adres skoku o bajt (aby byl nieparzysty), dotyczy CortexM3
    //a jesli nie, to mozna przesuwac ten wskaznik o dowolny offset...
    (((unsigned char*)wsk_na_funkcje)+1);

    //albo nawet odpalic kod bedacy pod wskaznikiem na void :D:D

    void *func1=(void*)wsk_na_funkcje; //teraz kompilator juz zapomnial o offsecie skoku...wiec trzeba zrobic to recznie, a nastepnie cast...

    wynik=(*((unsigned char (*)(unsigned char x))(((unsigned char*)func1)+1)))(4);



    Widzisz jakie to proste?...

    Zrozum, ze w ANSI C kazdy parametr niezaleznie od typu jest
    przekazywany przez wartosc, bo przekazywanie czegos przez
    referencje JUZ JEST OPERACJA NA WSKAZNIKACH.
    Nie myl ANSI C z Java...
  • #12
    acid12
    Level 21  
    nsvinc, czytajac te wymiane zdan z tmf jestem pelen podziwu bo poruszacie naprawde zaawansowane kwestie, i wedlug mnie oboje macie racje :D dlaczego?
    nsvinc, masz racje ze
    Quote:

    Zrozum, ze w ANSI C kazdy parametr niezaleznie od typu jest
    przekazywany przez wartosc

    ale tmf wedlug mnie ma racje ze nie ma sensu przekazywać adresu tablicy jako parametru, ponieważ tablica sama w sobie jest adresem.
    Dlatego nie ma sensu przekazywac tablicy przez referencje, bo nawet gdy ten parametr jest przekazywany przez wartosc, kopiowany jest tylko adres wskaznika (a nie cala tablica) i wszystkie operacje są itak wykonywane na prawdziwej tablicy a nie na jej kopii.
    z powyzszych wypowiedzi tmf, czytam ze wcale nie twietdzi on ze costam oraz &castam jest sobie rownoznaczne. Twierdzi on ze przekazujac jako parametr do funkcji costam[] oraz &costam[] (costam jest tablica) w wyniku bedziemy operowac na dokladnie tej samej tabeli.
  • #13
    nsvinc
    Level 35  
    costam[] i &costam[] to juz wogole zupelnie inna bajka :]

    Niestety, nie bedziemy operowac na tej samej tabeli.
    Nie mowie, ze zawsze jest koniecznosc utrudniania sobie zycia
    wskaznikami "na wskazniki na wskazniki na wskazniki (na wskazniki)", ale
    czasami jest taka potrzeba, i opisalem taki przypadek w jednym z
    postow w tym topicu.

    Ale przekazanie wskaznika na wskaznik na wskaznik zamiast samego wskaznika na wskaznik do funkcji,
    ktora oczekuje wskaznik na wskaznik, jest bledem. I nikt nie wmowi mi
    ze nie jest...

    Tu nie chodzi o to, jaka operacja ma sens...Z tego co widze, spor
    toczy sie o nieszczesny znaczek &, bo tmf twierdzi, ze
    gdy przekazuje sie tablice (czyli wskaznik na zerowy element) do funkcji,
    to rownie dobrze mozna podac wskaznik na ten wskaznik na zerowy
    element
    i bedzie ten sam efekt. A to jest blad.

    btw, wyjasniajac &costam[n]...

    gdy:

    unsigned char ziemniak[4];

    to:

    &ziemniak[2]==(ziemniak+2)

    dowod:

    ziemniak[2] jest dereferencja ziemniaka z offsetem 2, czyli finalna
    wartosc w 'jakiejs' komorce pamieci
    . Stawiajac przed to wszystko
    &, otrzymujemy adres tej komorki pamieci, w ktorej jest ta finalna wartosc.
    Ten adres jest rowny wynikowi przesuniecia samego ziemniaka (czyli
    wskaznika na pierwszy element) o ten offset, czyli o 2...
  • #14
    tmf
    Moderator of Microcontroller designs
    Kolego, po pierwsze aż do ARM7 są w architekturze von Neumanna, ARM9 dopiero w Harwardzkiej, w dodatku nie do końca, bo tylko cache instrukcji i danych są rozdzielone.
    Twój przykład jest bzdurny, bo nie odróżniasz przekazania przez referencję od przekazania referencji do wskaźnika. Po pierwsze zacznij od dokładnego przeczytania K&R, tablice w C NIGDY NIE SĄ PRZEKAZYWANE PRZEZ WARTOŚĆ. Nie jest to możliwe, bo musiałby istnieć jakiś domyślny konstruktor tablicy, co w przypadku tablic zawierających wskaźniki wymagałoby deep copy - problemu znanego z C++ i domyślnego konstruktora obiektu robiącego tylko shallow copy. Poza tym takie przekazanie przez wartość wiązałoby się ze stratą czasu na kopiowanie i olbrzymią zajętością miejsca. Zresztą to są podstawy C. Wyłącznie typy proste (primitive types) mogą być przekazywane przez wartość. W takim wypadku ważne jest czy jako argument podajemy zmienna, czy &zmienna.
    Wracając do twojego przykładu, błąd tkwi w definicji - u8 **tlist, powinno być u8 *tlist. Po co przekazywać wskaźnik na tablice wskaźników? To miałoby sens gdyby ten wskaźnik w funkcji modyfikować, bez sensu jest kiedy za jego pomocą odnosisz się do tablicy. Wskaźnik na wskaźnik tak jak to zadeklarowałeś nie jest tablicą lecz typem prostym, stąd różnica pomiędzy użyciem znaku & lub jego nieużyciem. Dla typu prostego & zwraca referencję, czyli robi typ*, ty masz u8*, więc & zwróci u8**, ale jak pisałem to jest typ prosty i to, ze wskazuje na tablicę nie czyni z niego tablicy.
    Powyższy kod powinien wyglądać tak (uprościłem nieistotne rzeczy):
    Code:

    #define TOKENLIST1 6
    char *tokenlist1[TOKENLIST1]={"start","stop","continue","filter","buffer","baud"};

    char znajdz_token(char *tlist[], uint8_t token)
    {
       return *tlist[token];
    }

    znajdz_token(&tokenlist1[0], 1);

    lub prościej:
    Code:
    znajdz_token(tokenlist1, 1);
  • #15
    nsvinc
    Level 35  
    char *tlist[] == char **tlist... <-- wskaznik na tablice wskaznikow.
    Kompilator w jednym i drugim przypadku dokonuje dereferencji tego
    "górnego" wskaznika.

    Nigdy nie twierdzilem, ze tablice sa przekazywane przez wartosc...
    Tablica sama w sobie nie jest 'typem', bo NIE ISTNIEJE typ tablicy.
    Istnieje tylko wskaznik na pierwszy element tablicy, ktory - jak
    sam stwierdziles - jest typem prostym, i w dodatku jest const gdy
    zadeklarujesz go w "[ ]" sposob.

    Wlasnie gdyby istnial typ tablicy, wystapily by problemy z kopiowaniem
    znane z c++, o czym rowniez wspomniales.

    A typ zlozony to taki typ, ktory sklada sie z typow prostych, a ANSI C
    nie wspiera jakichkolwiek operacji na typach zlozonych. Dlaczego?
    1) ANSI C sam w sobie nie definiuje ani jednego typu zlozonego, a tylko
    daje mozliwosci stworzenia takowych
    2) A skoro typ zlozony musisz stworzyc sam, to sam napisz sobie funkcje nim
    zarzadzajace.

    W kodzie nie przerobiles wlasciwie nic, tylko zapisales i tak istniejacy
    wskaznik na wskaznik u8 **tlist, na u8 *tlist[], co w efekcie
    jest dokladnie tym samym, tyle ze w pierwszym przypadku
    wskaznik na tablice wskaznikow nie jest const, a w drugim juz tak,
    bo nie mozesz wewnatrz funkcji zmienic 'tlist'.

    Sprawa ARMow - odrozniasz architekture rdzenia od rodziny procesorow?...
    Juz to chyba kiedys tlumaczylem tutaj na elce, ze
    1) ARM7 != ARMv7
    2) ARM7 jest zbudowany w oparciu o architekture ARMv4...
    Sprawdz na wikipedii i www.arm.com :]
  • #16
    Freddie Chopin
    MCUs specialist
    nsvinc wrote:
    Tak, rdzenie ARMv4 i starsze sa von Neumann. Wszystkie nowsze
    (ARMv5,ARMv6,ARMv7 i ich rozne modyfikacje) sa harvardzkie.

    Tylko jakie znaczenie ma tutaj szczegół techniczny czy faktycznie istnieje jedna czy więcej magistral, skoro tak czy siak na każdym jest liniowa przestrzeń adresowa i nie ma możliwości stosowania innej adresacji? Tak czy siak mylisz się, bo nowsze ARMy wcale nie mają "czystej" architektury Harwardzkiej tylko zmodyfikowaną architekturę Harwardzką (zresztą podobnie jak wiele układów w architekturze "niby" Harwardzkiej) - http://en.wikipedia.org/wiki/Modified_Harvard_architecture - w skrócie - osobne szyny, ale tak czy siak dostęp w liniowej przestrzeni adresowej.

    Dla programisty nie ma to znaczenia i każdy ARM jest w zasadzie w architekturze von Neumann, bo ma liniową przestrzeń adresową.

    4\/3!!
  • #17
    nsvinc
    Level 35  
    Ja juz sie po prostu nie chcialem zaglebiac w szczegoly nie bedace
    mysla przewodnia tego topica... :]

    Oczywiste jest, ze ARM nie jest czystym harvardem (jak np. AVR)
    aczkolwiek jest harvardem w tym stopniu, ze np. istnieja oddzielne
    ICache i DCache, z ktorych rdzen moze pobrac dane jednoczesnie.
    No a von Neumannem jest pod kazdym innym wzgledem...

    Ale w ogole nie chodzi tutaj o architektury procesorow.
  • #19
    tmf
    Moderator of Microcontroller designs
    nsvinc wrote:

    Zrozum, ze w ANSI C kazdy parametr niezaleznie od typu jest
    przekazywany przez wartosc, bo przekazywanie czegos przez
    referencje JUZ JEST OPERACJA NA WSKAZNIKACH.
    Nie myl ANSI C z Java...


    nsvinc wrote:

    Nigdy nie twierdzilem, ze tablice sa przekazywane przez wartosc...


    Przepraszam, więc już nie twierdzisz, że tablice są przekazywane przez wartość? Bo to jakby zaprzecza temu co powyżej napisałeś. Raz piszesz tak, raz inaczej, zdecyduj się w końcu.
  • #20
    rpal
    Level 27  
    Może od d.. strony do autora postu. Co by się stało gdyby (tak jak się upierasz w przekazywaniu przez wartość, czyli przekazywaniu lokalnej kopi tablicy) mając do dyspozycji w procku np. 2KB RAM przekazałbyś tablicę o rozmiarze np. 1,5 KB ? Twój progam poszedłby natychmiast w krzaki z racji braku pamięci RAM więc przekazywanie przez adres jest metodą na "rozsądne" użycie zasobów pamięci dynamicznej.
  • #21
    nsvinc
    Level 35  
    Ale "tablica" nie jest typem, a wskaznikiem!... A do funkcji nie wedruje
    TEN wskaznik, tylko jego kopia - bo funkcja moze go sobie posuwac
    po calej pamieci albo go nawet zgubic, a oryginalny wskaznik przekazany
    do funkcji nie zmieni adresu na ktory wskazuje. I o to mi chodzilo...

    tablica == const wskaznik !!

    Przeciez tablice to jest tylko narzedzie upraszczajace operacje na
    wskaznikach dzieki sprawdzaniu np. max indeksu elementu dopoki
    dokonujesz dereferencji poprzez "[ ]", na etapie kompilacji. A nic juz nie sprawdza
    poprawnosci dostepu do danych gdy dokonujesz dereferencji przez
    *(wskaznik+offset).
    I do tego maja bardzo ograniczone mozliwosci, ale to nadal
    zwykle wskazniki. Dzialaja w mysl tej samej zasady i podobnie sie
    kompiluje ich uzywanie...

    ->rpal
    W ANSI C nie da sie przekazac tablicy inaczej niz przez referencje!
    Przeciez nie ma domyslnej funkcji kopiujacej dane, jak wspomnial tmf.
    JESLI FUNKCJA MA PRZYJAC 'tablice danych', TO TRZEBA JEJ PRZEKAZAC
    WSKAZNIK NA TE DANE (albo wskaznik na wskaznik na dane, itp)
  • #22
    rpal
    Level 27  
    Tablica to konkretny obszar pamięci a nie hipotetyczny wskaźnik, wskaźnik jedynie zawiera pierwszy adres komórki pamięci w której przechowywane są dane. Sam sobie przeczysz bo skoro piszesz ze przekazywany jest wskaźnik to właśnie jest to odwołanie przez referencje a nie przez wartość.
  • #23
    nsvinc
    Level 35  
    ehhh....

    ->rpal
    Sorry, ale zdaje sie ze mnie nie zrozumiales...

    Code:


    u8 *znaczki="TEKST";

    u16 lizak(u8 *ple)
    {
    u16 wyn=0;
    while(*ple) { wyn+=*ple; ple++; }; //najprostsza suma kontrolna do n/z
    }

    //w main
    u16 wyn=lizak(znaczki);



    Popatrz, ze funkcja "lizak", pracuje na wskazniku o wdziecznej nazwie
    ple. W momencie gdy wywolujesz funkcje, pod "ple" podstawia
    sie kopia wskaznika "znaczki"....funkcja moze sobie dowolnie
    zmieniac "ple", ale nigdy nie zmieni "znaczki". To nie tablica jest
    przekazywana do funkcji, a kopia wskaznika, tego ktory
    oryginalnie wskazuje na dane (czyli na zerowy element tablicy...)
    Wiec sam wskaznik jet przekazywany PRZEZ WARTOSC, obojetnie
    czy on jest const, czy nie...
  • #24
    tmf
    Moderator of Microcontroller designs
    Tutaj nsvinc ma całkowitą rację, technicznie do funkcji przekazywany jest wskaźnik wskazujący na obszar pamięci zawierający tablicę, więc jest to klasyczne odwołanie przez referencję, tyle, że implicite, bo do funkcji jest przekazywany przez wartość wskaźnik do tablicy, który funkcja może modyfikować, a że wskazuje on na obszar pamięci zajmowany przez oryginalną tablicę to wszelkie zmiany wyglądają tak jakby przekazanie parametrów nastąpiło przez referencję.
  • #25
    nsvinc
    Level 35  
    no i tyle na temat sporu o przekazanie wartosci. Juz wiesz co mialem
    na mysli piszac jeden z pow. postow na temat przekazywania przez wartosc.

    No to oprocz tego gdzie nie mam racji? :]
  • #27
    nsvinc
    Level 35  
    ale...

    TO NIE JEST MOJ KOD
    Popatrz na moj styl pisania, a jego....:P

    poza tym tam mialo byc jakies const albo jakis tam inny progmem czy
    jak mu tam...Ja po prostu o tym zapomnialem, bo myslalem ze to po
    prostu prosty temat :] poprawilem swoj blad w dokladnie piszac
    drugiego posta, po tym jak ty napisales o
    Quote:
    Żeby coś było const trzeba to jako const zadeklarować.
  • #28
    tmf
    Moderator of Microcontroller designs
    Już nie będę do tego wracał, bo zaczynamy od nowa - const nic nie zmieni. U ciebie zmienia przypadkiem, bo powoduje, że stała umieszczana jest we FLASH i wskaźnik na nią zawsze jest prawidłowy. Ani na PC, ani na AVR to już nie zadziała. W ogólności const jak pisałem nie zmienia zakresu widoczności zmiennej, więc takie przypisanie byłoby poprawne tylko w zakresie samej funkcji, nigdy poza nią. Jeśli to const zamieniłbyś na static to już byłoby lepiej. Ale ciągle kaszaniasto wygląda wskaźnik na literał.
  • #29
    nsvinc
    Level 35  
    Z tego co wywnioskowalismy nt. architektur procesorow, mozna powiedziec,
    ze kod na dowolnym rdzeniu o architekturze von Neumann bedzie dzialac, a przynajmniej w wiekszosci przypadkow. Wogole jakos nie zwrocilem na to uwagi na poczatku, nie sprawdzalem tej funkcji, zauwazylem tylko dziwny
    parametr podany do funkcji


    Ja wiem ze na PC ani AVR kod nie bedzie dzialac, ja w ogole inaczej pisze bo
    moje kody dzialaja :]
  • #30
    krdln
    Level 12  
    Z ciekawości napisałem sobie taki kod:

    Code:
    #include <stdio.h>
    

    void psuj_tablice(char *xax) {
       while (*xax!='\0') putchar(*xax++);
       putchar('\n');
    }

    int main() {
       char tablica[]="hello world";
       printf("%lx\n",(long unsigned int)tablica);
       psuj_tablice(tablica); //1
       printf("%lx\n",(long unsigned int)tablica);
       psuj_tablice(&tablica); //2
       printf("%lx\n",(long unsigned int)tablica);
       
       return 0;
    }


    Kod się kompiluje, ale dostajemy warninga w drugim wywołaniu psuj_tablice. Ten przykład pięknie pokazuje, że nadmiarowe stosowanie & w większości przypadków nie szkodzi, ale jest zwykłym niechlujstwem. Gdybyśmy użyli wersji 2., ktoś skopiował by nasz kod i skorzystał w takim programie:

    Code:
    #include <stdio.h>
    

    void psuj_tablice(char *xax) {
       while (*xax!='\0') putchar(*xax++);
       putchar('\n');
    }

    int main() {
       char *napisy[3];
       napisy[1] = "jeden";
       napisy[0] = "zero";
       napisy[2] = "dwa";
       char *tablica = napisy[1];
       
       printf("%lx\n",(long unsigned int)tablica);
       psuj_tablice(tablica);
       printf("%lx\n",(long unsigned int)tablica);
       psuj_tablice(&tablica);
       printf("%lx\n",(long unsigned int)tablica);
       
       return 0;
    }


    w drugim przypadku uzyskałby KRZAKI! Polecam sprawdzić.
    Więc, tmf, pisząc „Bzdura, czy przekażesz do funkcji tabela, czy &tabela wyjdzie na to samo” nie miałeś racji.

    Polecam i pozadrawiam,
    krdln