Mala šola C jezika - Kazalci - debata, poglavje 9

Vse o programiranju na in za PC

Moderatorji: Kroko, tilz0R

Mala šola C jezika - Kazalci - debata, poglavje 9

OdgovorNapisal/-a tilz0R » 02 Okt 2016, 15:02

Poglavje z pointerji in strukturami je za moje pojme najtežje za razumet, zato so še posebej zaželjena vprašanja in/ali pripombe.

Vabljeni tudi tisti, ki pointerje že znate, da se vključite debato. Mogoče sem kaj pozabil omeniti, kar bi bilo koristno in se s tem zadeva dopolni.

Poglavje se nahaja tukaj.
Knowledge sharing is people' caring., T. MAJERLE
Uporabniški avatar
tilz0R
 
Prispevkov: 1855
Pridružen: 18 Jan 2015, 00:12
Kraj: Črnomelj
Zahvalil se je: 231 krat
Prejel zahvalo: 528 krat
Uporabnika povabil: s56rga
Število neizkoriščenih povabil: 255

Re: Mala šola C jezika - Kazalci - debata, poglavje 9

OdgovorNapisal/-a s54mtb » 02 Okt 2016, 15:56

Občasno je kakšna domača naloga kar fina, takole za obnovo znanja. Za tisti trikotnik iz prejšnje vaje sicer nisem naredil računanja stranic, sem pa slutil, kaj bo v poglavju 9, zato sem pripravil en primer. Komentarji so le nejnujnejši. Komur se ljubi... predlagam pa, da program dopolnete tako, da bo zračunal in izpisal še dolžine stranic trikotnika. Uporabljeno je po mojem večina do sedaj opisanega, tako da kdor razume spodnjo kodo, je dojel bistvo.

Koda: Izberi vse
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

typedef struct
{
    double x ;
    double y;
    double z;
} koordinate_tip;

void dobi_koordinate(char *k1, koordinate_tip *k2)
{
    k2->x = atof(k1++);
    k2->y = atof(k1++);
    k2->z = atof(k1);

}

// Izracun kota med vektorji z arctan skalarnega produkta vektorjev
// predpostavljamo, da sta v izhodiscu
double kot_med_vektorjema_norm(koordinate_tip t1, koordinate_tip t2)
{
    double kot = 0.0f;
    double skal_prod = t1.x*t2.x + t1.y*t2.y + t1.z*t2.z;
    double dolzina1kvadrat = t1.x*t1.x + t1.y*t1.y + t1.z*t1.z;
    double dolzina2kvadrat = t2.x*t2.x + t2.y*t2.y + t2.z*t2.z;
    if ((dolzina1kvadrat != 0) & (dolzina2kvadrat != 0))
    {
        kot = acos(skal_prod/sqrt(dolzina1kvadrat * dolzina2kvadrat));
    }
    return kot;
}

void premakni_tocko(koordinate_tip *tocka, koordinate_tip delta)
{
    tocka->x -= delta.x;
    tocka->y -= delta.y;
    tocka->z -= delta.z;
}

// Tri tocke >>> [0] je skupna obema vektorjema, [1] - konec prvega vcektorja, [2] - konec drugega vektorja
double kot_med_vektorjema(koordinate_tip *t)
{
    koordinate_tip t1, t2;
    memcpy(&t1,&t[1],sizeof(koordinate_tip));
    memcpy(&t2,&t[2],sizeof(koordinate_tip));
    premakni_tocko(&t1, t[0]);
    premakni_tocko(&t2, t[0]);
    return kot_med_vektorjema_norm(t1, t2);
}

// n = 0...2 za kot, ki ga iscemo v trikotniku
double KotiTrikotnika(koordinate_tip *trik, int n)
{
    koordinate_tip n_trik[3];
    double kot = 0;

    switch (n)
    {
    case 0 :  // smo ze prav postavljeni
        kot = kot_med_vektorjema(trik);
        break;

    case 1: // zavrti v levo
        memcpy(&n_trik[0],&trik[1],sizeof(koordinate_tip));
        memcpy(&n_trik[1],&trik[2],sizeof(koordinate_tip));
        memcpy(&n_trik[2],&trik[0],sizeof(koordinate_tip));
        kot = kot_med_vektorjema(n_trik);
        break;

    case 2: // zavrti v desno
        memcpy(&n_trik[0],&trik[2],sizeof(koordinate_tip));
        memcpy(&n_trik[1],&trik[0],sizeof(koordinate_tip));
        memcpy(&n_trik[2],&trik[1],sizeof(koordinate_tip));
        kot = kot_med_vektorjema(n_trik);
        break;
    }

    return kot;
}

// Radiani v stopinje
double rad2stop(double rad)
{
    return 180 * rad / 3.141592653589793;
}


int main(int argc, char *argv[])
{
    char lbl1[] = "abc";
    char lbl2[] = "bca";
    koordinate_tip trikotnik[3];
    double koti[3];
    int i, pravokotni = -1;

    if (argc != 10) // 9 stevil. polje kazalcev na stringe ima pri [0] ime exe datoteke, zato je indeksov za 1 vec
    {
        printf("Napaka! Podati moras tri tocke v prostoru: %s x1 y1 z1 x2 y2 z2 x3 y3 z3\n", argv[0]);
    }
    else
    {
        for (i=0; i<3; i++)
        {
            dobi_koordinate(argv[1+3*i],&trikotnik[i]);
        }

        for (i=0; i<3; i++)
        {
            printf("Kot med stranicama %c in %c je %f\n", lbl1[i], lbl2[i], koti[i]=rad2stop(KotiTrikotnika(trikotnik, i)));
            if (koti[i] == 90.0)
            {
                pravokotni = i;
            }
        }
        printf("Vsota kotov trikotnika je: %f\n",koti[0]+koti[1]+koti[2]);
        if (pravokotni >= 0)
        {
            printf("Trikotnik je pravokotni pri kotu med stranicama %c in %c\n", lbl1[pravokotni], lbl2[pravokotni]);
        }

    }

    return 0;
}



Koda: Izberi vse
D:\!work\razno\geometrija\bin\Debug>geometrija 1 1 1 2 2 2 5 6 7
Kot med stranicama a in b je 1.034787
Kot med stranicama b in c je 178.706065
Kot med stranicama c in a je 0.259149
Vsota kotov trikotnika je: 180.000000
D:\!work\razno\geometrija\bin\Debug>geometrija 1 0 0 1 1 0 0 1 0
Kot med stranicama a in b je 5.194429
Kot med stranicama b in c je 90.000000
Kot med stranicama c in a je 84.805571
Vsota kotov trikotnika je: 180.000000
Trikotnik je pravokotni pri kotu med stranicama b in c
Uporabniški avatar
s54mtb
 
Prispevkov: 11488
Pridružen: 15 Jan 2015, 01:10
Zahvalil se je: 1588 krat
Prejel zahvalo: 4192 krat
Uporabnika povabil: Vrtni palček
Število neizkoriščenih povabil: 120

Re: Mala šola C jezika - Kazalci - debata, poglavje 9

OdgovorNapisal/-a Kroko » 03 Okt 2016, 08:59

V s54mtb kodi je "napaka". Koda bo sicer delala, performančno pa ne bo najbolje.

Koda: Izberi vse
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>

typedef struct
{
   double x;
   double y;
} point_struct;

double distance1(point_struct pt)
{
   pt.x = pt.x * pt.x;
   pt.y = pt.y * pt.y;
   return sqrt(pt.x + pt.y);
}

double distance2(point_struct* pt)
{
   pt->x = pt->x * pt->x;
   pt->y = pt->y * pt->y;
   return sqrt(pt->x + pt->y);
}

double distance3(const point_struct* pt)
{
   double xx = pt->x * pt->x;
   double yy = pt->y * pt->y;
   return sqrt(xx + yy);
}

int main()
{
   point_struct pt;
   pt.x = 2;
   pt.y = 3;
   distance1(pt);
   printf("x:%f y:%f\n", pt.x, pt.y);

   pt.x = 2;
   pt.y = 3;
   distance2(&pt);
   printf("x:%f y:%f\n", pt.x, pt.y);

   pt.x = 2;
   pt.y = 3;
   distance3(&pt);
   printf("x:%f y:%f\n", pt.x, pt.y);
}


Koliko bytov spomina se porabi za distance1 in koliko za distance2?
Zakaj drugi printf izpiše "X:4, Y:9" ?
Zakaj v distance3 ne moremo uporabiti "pt->x ="?
...

V prvem primeru se parametri prenašajo "by value". To pomeni, da se v spominu naredi kopija strukture in v funkcijo se pošlje 16 bytov (za dva doubla).

V drugem primeru se kot parameter pošlje samo pointer. Kot parameter se pošlje samo sizeof(point_struct) bytov. Na nekaterih arhitekturah se sizeof(point_struct) podatkov pošlje celo direktno preko registra in niti uporaba stacka ni potrebna. Ampak tu postanejo stvari že prezapletene za ta nivo. Ta drugi primer je močno hitrejši od prvega (še posebaj, še je struktura velika) je pa lahko nevaren. Videli ste, da se izpis spremeni. No, velikoma je to to, kar tudi želimo.

Tretji primer je enak kot drugi le da spreminjanje "zaklenemo". Poleg tega s "const" povemo prevajalniku, da se parameter ne bo spreminjal in bo še veliko bolje lahko optimiziral kodo.

Nočem biti nesramen ampak zgornji primer je tipičen primer slabe kode, ki jo potem nekdo najde na netu in po copy/paste principu širi slabo kodo še naprej.
http://www.planet-cnc.com poskakuješ na eni nogi in žvižgaš alpske podoknice Kroko was here!
Uporabniški avatar
Kroko
 
Prispevkov: 4877
Pridružen: 14 Jan 2015, 12:12
Kraj: Ljubljana
Zahvalil se je: 690 krat
Prejel zahvalo: 1704 krat
Uporabnika povabil: Vrtni palček
Število neizkoriščenih povabil: 255

Re: Mala šola C jezika - Kazalci - debata, poglavje 9

OdgovorNapisal/-a booxco » 03 Okt 2016, 09:46

Kroko je napisal/-a:Tretji primer je enak kot drugi le da spreminjanje "zaklenemo". Poleg tega s "const" povemo prevajalniku, da se parameter ne bo spreminjal in bo še veliko bolje lahko optimiziral kodo.


Tole s const je zanimivo. Ne samo, da se kazalec pt ne bo preminjal (npr. s pt++), prevajalnik ne pusti spreminjanja ničesar, na kar kaže pt. Če v distance3() poskusim pisati v strukturo, dobimo napako:

Koda: Izberi vse
main.c:30:10: error: assignment of member 'x' in read-only object                                                                                               
    pt->x = 5;                                                                                                                                                   
          ^           


Bom od sedaj naprej tale const bolj striktno uporabljal. Zna preprečiti kako neprijetno situacijo.
booxco
 
Prispevkov: 115
Pridružen: 25 Sep 2016, 22:17
Zahvalil se je: 19 krat
Prejel zahvalo: 47 krat
Uporabnika povabil: radix
Število neizkoriščenih povabil: 9

Re: Mala šola C jezika - Kazalci - debata, poglavje 9

OdgovorNapisal/-a Kroko » 03 Okt 2016, 12:18

Ne samo pri pointerjih. Za vse parametre, ki jih ne bomo spreminjali je pametno uporabiti const in s tem prevajalniku (in optimizerju) močno olajšati delo.
Če se to navadimo že v začetku nam bo hudo prišlo prav, ko zagrizemo v C++.
http://www.planet-cnc.com poskakuješ na eni nogi in žvižgaš alpske podoknice Kroko was here!
Uporabniški avatar
Kroko
 
Prispevkov: 4877
Pridružen: 14 Jan 2015, 12:12
Kraj: Ljubljana
Zahvalil se je: 690 krat
Prejel zahvalo: 1704 krat
Uporabnika povabil: Vrtni palček
Število neizkoriščenih povabil: 255


Vrni se na Programski jeziki

Kdo je na strani

Po forumu brska: 0 registriranih uporabnikov in 1 gost