Diskuze: c - ujištění se ohledně pointerů

C++ C a C++ c - ujištění se ohledně pointerů

Avatar
Dominika Šulcová(dominiQa):

ahoj, chtěla bych se ujistit, zda jsem správně pochopila pole struktur vs. pole pointerů na strukturu. Popis problému je ve zdrojáku. Čerpala jsem v knížce od p. Herouta. Děkuji.

#include <stdio.h>
#include <stdlib.h>

typedef struct {
        char * jmeno; int vek;
}Osoba;

int main() {
        Osoba a; a.jmeno = "Karel"; a.vek = 32;
        Osoba b; b.jmeno = "Robot"; b.vek = 5;
        Osoba * p_a = &a;
        Osoba * osoby;

        osoby = (Osoba*)malloc(2 * sizeof(Osoba)); // tohle je jenom pole stuktur Osoba
        osoby[0] = a; osoby[1] = b;
        printf("%s, %d let\n", osoby[0].jmeno, osoby[0].vek);
        printf("%s, %d let \n", osoby[1].jmeno, osoby[1].vek);

        printf("--------------\n");

        Osoba ** osoby2;
        osoby2 = (Osoba**)malloc(2 * sizeof(Osoba*)); //a tohle je pole pointerů na strukturu Osoba
        osoby2[0] = p_a;
        osoby2[1] = &b;
        printf("%s, %d let\n", osoby2[0]->jmeno, osoby2[0]->vek);
        printf("%s, %d let \n", osoby2[1]->jmeno, osoby2[1]->vek);

        free((void*)osoby); osoby = NULL;
        free((void**)osoby2); osoby2 = NULL;

        return 0;
}
Odpovědět 22.12.2015 9:25
I ♥ nutella
Avatar
Odpovídá na Dominika Šulcová(dominiQa)
Luboš Běhounek (Satik):

nespadne to na prvnim radku metody main na access violation? :)

Nahoru Odpovědět 22.12.2015 10:26
:)
Avatar
Odpovídá na Luboš Běhounek (Satik)
Dominika Šulcová(dominiQa):

Ne, ale padá mi to na acces violation, když si chci to pole vrátit přes funkci. A tady jsem už bezradná. Proč to spadne když tam to místo pro pointer na stukturu alokované mám?

#include <stdio.h>
#include <stdlib.h>

typedef struct {
        char * jmeno; int vek;
}Osoba;


Osoba ** osobyAlok() {
        Osoba ** osoby = (Osoba**)malloc(2 * sizeof(Osoba*));
        Osoba a; a.jmeno = "Karel"; a.vek = 32;
        Osoba b; b.jmeno = "Robot"; b.vek = 5;

        osoby[0] = (Osoba*)malloc(sizeof(Osoba));
        osoby[1] = (Osoba*)malloc(sizeof(Osoba));

        osoby[0] = &a;
        osoby[1] = &b;
        return osoby;
}

int main() {

        Osoba ** osoby = osobyAlok();
        if (osoby != NULL) {
                printf("%s, %d let\n", osoby[0]->jmeno, osoby[0]->vek);
                printf("%s, %d let \n", osoby[1]->jmeno, osoby[1]->vek);
        }
        free((void**)osoby); osoby = NULL;

        return 0;
}
Nahoru Odpovědět 22.12.2015 10:40
I ♥ nutella
Avatar
Odpovídá na Polymath
Luboš Běhounek (Satik):

Zapisuje do promenne jmeno , ktera neni naalokovana - pokud to nehodi access violation hned, tak tim prepise nekde neco jinyho, ta sance na accessviolation je tam podle me celkem velka :)

Nahoru Odpovědět 22.12.2015 11:01
:)
Avatar
Odpovídá na Luboš Běhounek (Satik)
Lukáš Hruda (Luckin):

Na tom prvnim radku v main to nespadne urcite, ta struktura je slokovana na stacku. Promenna jmeno je jenom pointer, stejne tak retezcovy literal.

Editováno 22.12.2015 11:06
 
Nahoru Odpovědět 22.12.2015 11:04
Avatar
Martin Dráb
Redaktor
Avatar
Odpovídá na Dominika Šulcová(dominiQa)
Martin Dráb:

Ve funkci OsobyAlok vytváříš dvouprvkové pole ukazatelů na strukturu Osoba. Tyto odkazy nasměruješ na struktury a a b, které jsou ale lokálními proměnnými. To znamená, že jakmile vykonávání funkce skončí, de facto přestanou existovat.

Realita je obvykle taková, že místo, kde tyto proměnné byly (a kam tedy odkazují ty dva ukazatele) stále existuje, jen je zaplněno jinými daty (pokud vůbec nějakými). Ty se je pak pokusíš interpretovat jako strukturu Osoba, ale protože tam už pravděpodobně jsou uložena jiná data, dostaneš se do problémů (zejména proto, že jméno osoby je ve struktuře uloženo jako ukazatel na první znak (Cčkový řetězec)... a hodnota toho ukazatele je (potom, co ty lokální proměnné přestaly existovat), řekněme, náhodná, a tedy obvykle vede na neexistující oblast paměti).

Nahoru Odpovědět 22.12.2015 11:05
2 + 2 = 5 for extremely large values of 2
Avatar
Odpovídá na Martin Dráb
Dominika Šulcová(dominiQa):

Děkuji za objasnění :). Jinak teď jsem trochu experimentovala a zkusila napsat toto v té funkci osobyAlok:

//osoby[1] = &b;
*osoby[1] = b;

nevím ale teda co přesně to znamená, ale funguje to :D, v mainu to ty osoby vypíše. A ještě bych se chtěla zeptat jak naplňovat pole ve funkci, jestli to jde tím řádkem jak jsem teď napsala nebo se to řeší jinak. Děkuji.

Nahoru Odpovědět 22.12.2015 11:16
I ♥ nutella
Avatar
Odpovídá na Lukáš Hruda (Luckin)
Luboš Běhounek (Satik):

Jo vlastne, "Karel" bude nejspis nekde staticky sedet na stacku, takze se do a.jmeno jen hodi ukazatel na tohle misto. :)

Nahoru Odpovědět 22.12.2015 12:11
:)
Avatar
Odpovídá na Luboš Běhounek (Satik)
Lukáš Hruda (Luckin):

Já bych spíš řekl, že "Karel" bude staticky alokovaný pro celou aplikaci, tím si už úplně jistý nejsem, ale pamatuji si, že když jsem kdysi otevřel .exe zkompilovaný z C kódu v textovém editoru, tak tam ty řetězcové literály byly vidět, z toho soudím, že to bude spíš kompletně statické.

 
Nahoru Odpovědět  +2 22.12.2015 13:04
Avatar
David Novák
Tým ITnetwork
Avatar
Odpovídá na Lukáš Hruda (Luckin)
David Novák:

Všechny řetězcové literály definované v kódu jsou staticky alokovány v datovém segmentu binárky. Standardní také je, že pokud máte v kódu více stejných literálů (třeba několikrát přiřazeno "Ahoj", tak se alokuje pouze jednou.

Proto je chyba, když člověk udělá něco takovéhoto:

char *mujstring = "Ahoj";
char *jinystring = "Ahoj";
mujstring[0] = 'a';

Pokud by vám to systém dovolil, tak si přepíšete oba stringy. Typicky to ale bude segfault, protože oblast statických dat je chráněná proti zápisu ;)

Nahoru Odpovědět  +2 22.12.2015 14:05
Chyba je mezi klávesnicí a židlí.
Avatar
Martin Dráb
Redaktor
Avatar
Odpovídá na Dominika Šulcová(dominiQa)
Martin Dráb:

Tento řádek

*osoby[1] = b;

překopíruje strukturu Osoba uloženou v proměnné b (člen po členu) na adresu zapsanou v Osoby[1]. Jelikož jsi ten paměťový blok předtím alokovala, tak to bude fungovat. Já bych tam osobně ještě přidal nějakou tu kulatou závorku, protože se v prioritách operátorů moc neorientuju:

*(osoby[1]) = b;

Ono se to dá i trochu odvodit. Víš, že proměnná Osoby má typ Osoba **, proměnná b má typ Osoba. Operátory * a [] oba lze chápat jako něco, co ti z datového typu argumentu odebere jedno přesměrování (jednu *), takže výsledek je takový, že na obou stranách přiřazovacího příkazu máš něco s datovým typem Osoba. A přiřazení znamená kopírování obsahu (pokud jsme v C).

Nahoru Odpovědět 22.12.2015 14:32
2 + 2 = 5 for extremely large values of 2
Děláme co je v našich silách, aby byly zdejší diskuze co nejkvalitnější. Proto do nich také mohou přispívat pouze registrovaní členové. Pro zapojení do diskuze se přihlas. Pokud ještě nemáš účet, zaregistruj se, je to zdarma.

Zobrazeno 13 zpráv z 13.