Jazyk C 2013 – 13. cvičenie [Argc/Argv, QSort, Matice]

Čítanie z parametrov

Načítajte z parametrov programu n čísiel a nájdite najväčšie spomedzi nich.

  • Využime funkciu main() s dvoma parametrami.

  • deklarácia char ** znamená po čítaní sprava do ľava
    • pointer na pointer na char, ale keďže char * je veľmi často reťazec, tak:
    • pointer na reťazec, ale keďže pole a pointer sú ekvivalentné, tak
    • pole reťazcov
  • na prevod reťazca na číslo použime strtol().
  • druhý parameter v strtol je tiež char **. V tomto prípade to však nie je pole charov, ale parameter odovzdávaný odkazom (teda vstupno-výstupná premenná), v ktorom sa po skončení parsovania objaví podreťazec vstupu, ktorý sa už nedal viac naparsovať na číslo.
  • strtol vracia long int! (analógia long z Javy)
  • nezabudnime inicializovať premennú pre doposiaľ nájdené maximum!
    • využijeme “najmenší možný long” z limits.h
  • pri výpise long intov cez printf() využime konštantu %ld.

    #include<stdlib.h>
    #include<stdio.h>
    #include<limits.h>
    
    int main(int argc, char** argv) {
        int i;
        long int max = LONG_MIN;
        long int cislo;
    
        if(argc > 1) {
            max = strtol(argv[1], NULL, 10);
        }
        for (i = 2; i < argc; i++) {
            cislo = strtol(argv[i], NULL, 10);
            if(cislo > max) {
                max = cislo;
            }
        }
        printf("%ld\n", max);
    
        return 0;
    }
    

Triedenie polí

Zotrieďte 10prvkové pole

  • Využime qsort() zo stdlib.h, čo je quicksort.

Quicksort qsort

  • Poobdivujme deklaráciu:

    void qsort(void *base,
        size_t nmemb, 
        size_t size,
        int(*compar)(const void *, const void *));
    
    • prvý parameter base obsahuje pole, ktoré sa ide triediť
      • je typu void *, pointer na void.
      • V tomto prípade to chápeme ako pointer na ľubovoľný typ.
      • Ide o generický pointer, teda premennú, ktorá môže ukazovať na ľubovoľný typ.
      • Akousi veľmi veľmi vzdialenou analógiou je premenná typu Object v Jave, ktorá môže tiež obsahovať ľubovoľný objekt.
      • v tomto prípade je to však pole ľubovoľného typu
    • druhý parameter obsahuje počet prvkov poľa
      • je typu size_t, čo je alias pre celé číslo reprezentujúce veľkost
      • size_t je napríklad typ, ktorý vracia makro sizeof
    • tretí parameter obsahuje veľkost jedného prvku
      • ak máme pole intov, posielame sizeof(int)
    • štvrtý parameter predstavuje odkaz na funkciu, ktorá porovná dva prvky poľa (komparátor)

      • v Jave máme Collections.sort(), ktorá očakáva komparátor, teda triedu implementujúci interfejs java.util.Comparator
      • komparátorom v C je ľubovoľná funkcia, ktorá spĺňa predpis

        int(*compar)(const void *, const void *));
        
        • musí vracať int
        • berie dva parametre, oba typu const void *
          • znamená to, že berie dva generické pointery

Pointre na funkcie

  • qsort() dostane odkaz na funkciu
  • štvrtý parameter je potom pointer na funkciu

    int(*compar)(const void *, const void *));
    

    Predpis čítame zvnútra von ako compar je premenná typu pointer na funkciu, ktorá vracia int a berie dva generické pointre.

  • ukážka funkcie, ktorá porovná dva inty:

    int compare(const void * cislo1, const void * cislo2) {
        int * c1 = (int *) cislo1;
        int * c2 = (int *) cislo2;
    
        if(*c1 < *c2){
            return -1;
        } else if (*c1 > *c2) {
            return 1;
        } else {
            return 0;
        }
    }
    
    • oba generické pointre musíme pretypovať na pointre príslušných typov
    • podobne ako v Jave musíme Object pretypovať na príslušnú triedu, ak ho chceme zmysluplne používať
    • s pointrami pracujeme potom cez operátor referencie klasickým spôsobom
    • pozor na zádrheľ, ak by sme referencie vynechali, porovnávali by sme v c1 < c2 dve adresy, čo je síce možné (adresy sú čísla), ale nedáva to zmysel
  • funkciu potom použijeme veľmi jednoducho: cez názov

    qsort(cisla, 8, sizeof(int), compare);
    

Celý kód

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

#define DLZKA 8

int compare(const void * cislo1, const void * cislo2) {
    int * c1 = (int *) cislo1;
    int * c2 = (int *) cislo2;

    if(*c1 < *c2){
        return -1;
    } else if (*c1 > *c2) {
        return 1;
    } else {
        return 0;
    }
}

int main(void) {
    int i = 0;

    int cisla[] = { 1, 5, -52, 68, 12, 45, -48, 78 };

    qsort(cisla, DLZKA, sizeof(int), compare);

    for(i = 0; i < DLZKA; i++) {
        printf("%d, ", cisla[i]);
    }

    return 0;
}

Bonus

Keďže pole cisla je staticky alokované (teda nie cez malloc()), môžeme zistiť jeho dĺžku v programe:

int dlzka = sizeof(cisla) / sizeof(int);

Čítajme to ako “počet bajtov, ktoré zaberá celé pole delené počtom bajtov pre jeden prvok”.

Toto je jediný prípad, keď v C vieme zistiť dĺžku poľa za behu! Pole však musí byť alokované staticky, lebo celý výraz sa vyhodnotí v čase kompilácie!

Dvojrozmerné polia

Naplňte maticu číslami a vypíšte ju

Prvý pokus:

int matica[][] = {
    { 1, 2, 3},
    { 4, 5, 6},
    { 123456, 8, 9}
};

Kompilátor však vypíše:

error: array type has incomplete element type

Dvojrozmerné pole treba chápať ako pole polí. V tomto prípade musíme povedať, koľko polí bude podpole obsahovať (= koľko riadkov má matica):

int matica[][3] = {
    { 1, 2, 3},
    { 4, 5, 6},
    { 123456, 8, 9}
};

Ak by sme chceli funkciu pre výpis matice, musíme pamätať na to, že pole potrebuje dodať aj jeho dĺžku. Dvojrozmerné pole potrebuje teda dva rozmery:

/* TOTO NEFUNGUJE */
void matica_print(int m[][], int r, int s);

Opäť však dostaneme hlášku:

error: type of formal parameter 1 is incomplete

Funkcia musí dostať vonkajší rozmer!

void matica_print(int m[][3], int r, int s);

Táto funkcia je však prudko nepoužiteľná. Musíme to spraviť dynamicky. (Vysvetlenie napríklad C-Faq.com 6.19).

Funkcia bude mať parameter typu int ** (chápaný ako pole polí intov):

void matica_to_string(int ** m, int pocet_riadkov, int pocet_stlpcov) {

V kóde funkcie radostne pristupujeme k dvojrozmernému poľu cez indexy.

void matica_to_string(int ** m, int pocet_riadkov, int pocet_stlpcov) {
    int i = 0;
    int j = 0;
    for(i = 0; i < pocet_riadkov; i++) {
        for(j = 0; j < pocet_stlpcov; j++){
            printf("%d ", m[i][j]);
        }
        printf("\n");
    }

}

Dynamická alokácia poľa

  • najprv alokujeme pamäť pre vonkajšie pole (bude obsahovať pointre na “int`, teda odkazy na riadkové polia.):

    int ** m = malloc(POCET_RIADKOV * sizeof( int * ));

  • potom pre každé riadkové pole alokujeme pamäť.

    int ** m = malloc(POCET_RIADKOV * sizeof( int * ));
    if( m == NULL ) {
        perror("Zlyhal malloc()");
        return -1;
    }
    for(i = 0; i < POCET_RIADKOV; i++) {
        m[i] = malloc( POCET_STLPCOV * sizeof( int ));
    }
    
  • následne pristupujeme k pamäti cez indexy.

Dynamická dealokácia

  • použime opačný proces: najprv v cykle dealokujeme riadkové polia
  • potom dealokujeme hlavné pole.

Celý kód

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

#define POCET_RIADKOV
#define POCET_STLPCOV

void matica_print(int ** m, int r, int r) {
    int i = 0;
    int j = 0;
    for(i = 0; i < r; i++) {
        for(j = 0; j < s; j++){
            printf("%d ", m[i][j]);
        }
        printf("\n");
    }

}

int ** matica_new(int riadkov, int stlpcov) {
    int ** m = malloc(POCET_RIADKOV * sizeof( int * ));
    if( m == NULL ) {
        perror("Zlyhal malloc()");
        return NULL;
    }
    for(i = 0; i < POCET_RIADKOV; i++) {
        m[i] = malloc( POCET_STLPCOV * sizeof( int ));
        if(m[i] == NULL) {
            perror("Zlyhal malloc()");
            return NULL;
        }
    }       

    return m;    
}

void matica_free(int ** matica, int riadkov) {
    for(i = 0; i < riadkov; i++) {
        free(matica[i]);
    }
    free(m);                
}

int main(void) {
    int i;

    int ** m = matica_new(POCET_RIADKOV, POCET_STLPCOV);

    m[0][0] = 1; m[0][1] = 2; m[0][0] = 1;
    m[0][0] = 4; m[0][1] = 5; m[0][0] = 6;
    m[0][0] = 1234567890; m[0][1] = 8; m[0][0] = 9;

    matica_print(m, POCET_RIADKOV, POCET_STLPCOV);

    matica_free(m, POCET_RIADKOV);

    return 0;
}

Zdroje

2 thoughts on “Jazyk C 2013 – 13. cvičenie [Argc/Argv, QSort, Matice]

  1. Dobry den

    Chcel by som sa spytat, ako mam postupovat ak mam vytvorit program, ktory pomocou funkcie nacita prvky do stvrocovej matice rozmeru 4×4, nasledne vytvori transponovanu maticu,(tj. vymeni stplce za riadky) a pomocou dalsej funkcie tuto maticu vypise.

    Dakujem :)

Pridaj komentár

Vaša e-mailová adresa nebude zverejnená. Vyžadované polia sú označené *