NOVINKA! E-learningové kurzy umělé inteligence. Nyní AI za nejlepší ceny. Zjisti více:
NOVINKA – Víkendový online kurz Software tester, který tě posune dál. Zjisti, jak na to!

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

V předchozím kvízu, Online test znalostí C++, jsme si ověřili nabyté zkušenosti z kurzu.

Aktivity
Avatar
Dominika Šulcová(dominiQa):22.12.2015 9:25

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
Lukáš Hruda
Tvůrce
Avatar
Odpovídá na Dominika Šulcová(dominiQa)
Lukáš Hruda:22.12.2015 10:21

Zadny problem tam nevidim.

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

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

Nahoru Odpovědět
22.12.2015 10:26
https://www.facebook.com/peasantsandcastles/
Avatar
Odpovídá na Luboš Běhounek Satik
Dominika Šulcová(dominiQa):22.12.2015 10:40

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:22.12.2015 11:01

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
https://www.facebook.com/peasantsandcastles/
Avatar
Lukáš Hruda
Tvůrce
Avatar
Odpovídá na Luboš Běhounek Satik
Lukáš Hruda:22.12.2015 11:04

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
Tvůrce
Avatar
Odpovídá na Dominika Šulcová(dominiQa)
Martin Dráb:22.12.2015 11:05

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):22.12.2015 11:16

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
Luboš Běhounek Satik:22.12.2015 12:11

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
https://www.facebook.com/peasantsandcastles/
Avatar
Lukáš Hruda
Tvůrce
Avatar
Odpovídá na Luboš Běhounek Satik
Lukáš Hruda:22.12.2015 13:04

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
22.12.2015 13:04
Avatar
David Novák
Tvůrce
Avatar
Odpovídá na Lukáš Hruda
David Novák:22.12.2015 14:05

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
22.12.2015 14:05
Chyba je mezi klávesnicí a židlí.
Avatar
Martin Dráb
Tvůrce
Avatar
Odpovídá na Dominika Šulcová(dominiQa)
Martin Dráb:22.12.2015 14:32

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.