Užrašai

Užrašai, rodyklės C programavimo kalboje


Nedideli užrašai apie rodykles C kalboje,
susidedu taip, kaip suprantamiau man. :)
Naudojama 64bit mašina, todėl visi adresų dydžiai 64bit.

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

int main()
{
    int a = 10;  // int kintamasis pavadinimu a, kuriam priskiriama reikšmė 10
    int *p;      // rodyklė į int
    p = &a; 	// rodyklei p priskiriamas kintamojo a adresas

    printf("%d\n", a); // spausdina kintamąjį a
    printf("%p\n", (void*) &a); // spausdina kintamojo a adresą
    printf("%p\n", (void*) p); // spausdina adresą, kuriuo rodo p
    printf("%d\n\n", *p); // spausdina int vertę adresu kuris nurodytas p

    char c = 'E'; // char kintamasis pavadinimu c, kuriam priskiriama reikšmė E
    char *p_c;  // rodyklė į char
    p_c = &c; 	// rodyklei p priskiriamas kintamojo c adresas

    printf("%c\n", c); // spausdina kintamąjį c
    printf("%p\n", (void*) &c); // spausdina kintamojo c adresą
    printf("%p\n", (void*) p_c); // spausdina adresą, kuriuo rodo p_c
    printf("%c\n\n", *p_c); // spausdina char vertę adresu kuris nurodytas p_c

    //dydžiai:
    printf("%zu\n", sizeof(int));   // int dydis baitais
    printf("%zu\n", sizeof a);      // kintamojo a dydis baitais
    printf("%zu\n", sizeof *p);     // kintamojo į kurį rodo p dydis baitais
    printf("%zu\n", sizeof(int*));  // rodyklės į int dydis
    printf("%zu\n", sizeof &a);     // kintamojo a adreso dydis
    printf("%zu\n", sizeof p);      // rodyklės p dydis

    return 0;
}

Failas:
rodykles-1.c
gcc rodykles-1.c -o testineprograma

Rezultatas paleidus:

10
0x7ffcc69563e4
0x7ffcc69563e4
10

E
0x7ffcc69563e3
0x7ffcc69563e3
E

4
4
4
8
8
8


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

int main()
{
    int a;
    int *p;
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};

    a = arr[0];  // į a įkeliama pirmo elemento arr masyve reikšmė
    printf("%d\n", a);  // spausdina kintamąjį a

    a = arr[3];  // į a įkeliama ketvirto elemento arr masyve reikšmė
    printf("%d\n", a);  // spausdina kintamąjį a

    p = arr;  // rodyklė p rodys adresu į arr masyvo pradžia, tas pats kas į pirmą elementą
    printf("%d\n", *p);

    p = &arr[0];  // rodyklė p rodys adresu į pirmą elementą arr masyve
    printf("%d\n", *p);

    p = &arr[3];  // rodyklė p rodys adresu į ketvirtą elementą arr masyve
    printf("%d\n\n", *p);

    p = arr;
    printf("%d ", *p);
    p += 1; // (!) adresas čia pastūmiamas per vieną int dydį
    printf("%d ", *p);
    p += 1;
    printf("%d ", *p);
    p++; // inkrementas, pakelimas per 1
    printf("%d\n\n", *p);

    char *p_c;
    char arr_c[4] = {'K','A','V','A'};

    printf("%c", arr_c[0]);
    printf("%c", arr_c[1]);
    printf("%c", arr_c[2]);
    printf("%c\n\n", arr_c[3]);

    p_c = arr_c;

    printf("%c", *p_c);
    printf("%c", *(p_c+1)); // papildomi skliausteliai reikalingi tam, kad pridėtų prie adreso, o ne prie vertės, kuri randasi adresu p_c
    printf("%c", *(p_c+2));
    printf("%c\n\n", *(p_c+3));

    printf("%c", *p_c);
    p_c += 1; // (!) adresas čia pastūmiamas per vieną char dydį
    printf("%c", *p_c);
    p_c += 1;
    printf("%c", *p_c);
    p_c += 1;
    printf("%c\n\n", *p_c);

    return 0;
}

Failas:
rodykles-2.c
gcc rodykles-2.c -o testineprograma

Rezultatas paleidus:

1
4
1
1
4

1 2 3 4

KAVA

KAVA

KAVA

Funkcija, kuri gražina rodyklę


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

int *TestFnc( int *p, int n )
{
    return p + n;
}

int main()
{
    int arr[5] = {1,2,3,4,5};

    int *p = arr;
    
    printf("%d\n", *p);
    
    p = TestFnc( p, 2 );
    
    printf("%d\n", *p);

    return 0;
}

Failas:
rodykles-3.c
gcc rodykles-3.c -o testineprograma

Rezultatas paleidus:

1
3

Rodyklės funkcijos parametruose

Pastaba:
Kas yra funkcijos parametrai ir kas yra funkcijos argumentai ?
Parametras - tai kintamasis kuris nurodomas funkcijos apibrėžime, nurodo kokius duomenis funkcija tikisi gauti.
Argumentas - tai faktinė vertė perduota funkcijai.
Pvz.:

int Fnc( int a, int b )
{
    return a + b;
}
...
int n = Fnc(2,5);
...

a ir b yra funkcijos parametrai, 2 ir 5 yra funkcijos argumentai.

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

void Fnc1( char *p )
{
    p++;    
    printf("%c\n", *p);
}

void Fnc2( char *p )
{
    p++;
    *p = 'e';   
    p++;
    *p = 'p'; 
    p++;
    *p = 'e'; 
    p++;
    *p = 'l'; 
    p++;
    *p = 'i';
    p++;
    *p = 'n';
    p++;
    *p = 'a';
    p++;
    *p = 'i';
}

void Fnc3( char **p )
{
    *p = *p + 1;
}

int main()
{
    char arr[10] = {'C','E','P','E','L','I','N','A','I',0};
    
    char *p_a;
    
    p_a = arr;
    
    printf("%c\n", *p_a);
    
    Fnc1(p_a);
    
    printf("%c\n\n", *p_a); // pastebėk, nors funkcijoje buvo pakeistas adresas, čia jis liko toks pat kaip prieš iššaukiant funkciją

    for( unsigned int i = 0; i < 9; ++i )
    {
        printf("%c", arr[i]);
    }
    
    printf("\n");
    
    Fnc2(p_a); // naudojntis rodykle, funkcijoje galima pakeisti vertes į ką rodyklė rodo
    
    for( unsigned int i = 0; i < 9; ++i )
    {
        printf("%c", arr[i]);
    }

    printf("\n\n");
     
    printf("%c\n", *p_a);

    // pakeičiam adresą į kurį rodo rodyklė funkcijoje

    Fnc3( &p_a );
    
    printf("%c\n\n", *p_a);

    return 0;
}

Failas:
rodykles-4.c
gcc rodykles-4.c -o testineprograma

Rezultatas paleidus:

C
E
C

CEPELINAI
Cepelinai

C
e

Vertės gražinimas per funkcijos parametrą


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

void Fnc1( char *p )
{
    *p = 'a'; 
}

bool Fnc2( char **p, unsigned int dydis )
{

    *p = malloc( dydis );
    
    if(!*p)
    {
        return false;    
    }

    return true;
}

int main()
{
    char a;
    
    Fnc1( &a );
    
    printf("%c\n\n", a);
    
    char *ptr;

    if(!Fnc2( &ptr , 100 ))
    {
        printf("Klaida: negaliu priskirti atminties bloko.\n");        
        return 1;    
    }    
    
    free(ptr);

    printf("Gerai.\n");

    return 0;
}

Failas:
rodykles-5.c
gcc rodykles-5.c -o testineprograma

Rezultatas paleidus:

a

Gerai.

Testuojam su valgrind:
in use at exit: 0 bytes in 0 blocks
total heap usage: 2 allocs, 2 frees, 1,124 bytes allocated
All heap blocks were freed -- no leaks are possible

Rodyklė į funkciją


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

void Fnc( int a ) 
{ 
    printf("%d\n", a ); 
} 

int main()
{
    //void (*f_p)(int); 
    //f_p = &Fnc;    
    
    void (*f_p)(int) = &Fnc; 
  
    (*f_p)(123); 

    return 0;
}

Failas:
rodykles-6.c
gcc rodykles-6.c -o testineprograma

Rezultatas paleidus:

123

Dvimatis masyvas ir rodyklės

Bandymas dinamiškai priskirti atmintį dvimačiam masyvui, jį inicijalizuoti.
Variantas 1:

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

int main()
{
    int **a = {0};

    // Tarkim bus 3 antro lygio blokai, kaip kad int a[3][x]

    a = malloc( 3 * sizeof *a );

    if (!a)
    {
        printf("Klaida: nagaliu priskirti atminties bloko. (1)\n");
        return 1;
    }

    // priskiriam atminti pirmam int blokui, kurį tarkim sudarys 10 elementų

    a[0] = malloc( 10 * sizeof **a );

    if (!a[0])
    {
        printf("Klaida: nagaliu priskirti atminties bloko. (2)\n");
        free(a);
        return 1;
    }

    // priskiriam atminti antram int blokui, kurį tarkim sudarys 8 elementai

    a[1] = malloc( 8 * sizeof **a );

    if (!a[1])
    {
        printf("Klaida: nagaliu priskirti atminties bloko. (3)\n");
        free(a[0]);
        free(a);
        return 1;
    }

    // priskiriam atminti trečiam int blokui, kurį tarkim sudarys 4 elementai

    a[2] = malloc( 4 * sizeof **a );

    if (!a[2])
    {
        printf("Klaida: nagaliu priskirti atminties bloko. (4)\n");
        free(a[0]);
        free(a[1]);
        free(a);
        return 1;
    }

    // inicijalizuojam pirmą bloką

    for ( unsigned int i = 0; i < 10; ++i )
    {
       a[0][i] = i;
    }

    // inicijalizuojam antrą bloką

    for ( unsigned int i = 0; i < 8; ++i )
    {
       a[1][i] = i + 10;
    }

    // inicijalizuojam trečią bloką

    for ( unsigned int i = 0; i < 4; ++i )
    {
       a[2][i] = i + 100;
    }

    // bandom perskaityti keletą reikšmių

    printf("%d\n", a[0][0]);
    printf("%d\n", a[0][3]);
    printf("%d\n", a[1][0]);
    printf("%d\n", a[1][7]);
    printf("%d\n", a[2][0]);
    printf("%d\n", a[2][3]);

    // atlaisvinam atminties blokus, 3 nes nutarėm kad tiek bus antro lygio blokų

    for ( unsigned int i = 0; i < 3; ++i )
    {
       free(a[i]);
    }

    free(a);

    return 0;
}

Failas:
dm-rodykles-1.c
gcc dm-rodykles-1.c -o testineprograma

Rezultatas paleidus:

0
3
10
17
100
103

testuojam su valgrind:

in use at exit: 0 bytes in 0 blocks
total heap usage: 5 allocs, 5 frees, 1,136 bytes allocated
All heap blocks were freed -- no leaks are possible

Variantas 2:

Tarkim tiksliai žinom kiek bus antro lygio blokų.
Pabandom priskirti atminitį ir inicijalizuoti funkcijoje,
taip pat naudojam atskirą funkciją atminties atlaisvinimui.


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

#define ALB_SKAICIUS 3

int *a[ALB_SKAICIUS];

void Cleanup()
{
    for ( unsigned int i = 0; i < ALB_SKAICIUS; ++i )
    {
       free(a[i]);
    }
}

/*
bool TestFnc( int **p, unsigned int dydis, int sk )
{
    int *p_tmp = malloc( dydis * sizeof **p );

    if (!p_tmp)
    {
        printf("Klaida: nagaliu priskirti atminties bloko.\n");
        return false;
    }

    for ( unsigned int i = 0; i < dydis; ++i )
    {
       p_tmp[i] = i + sk;
    }

    *p = p_tmp;

    return true;
}
*/

bool TestFnc( int **p, unsigned int dydis, int sk )
{    
    *p = malloc( dydis * sizeof **p );

    if (!*p)
    {
        printf("Klaida: nagaliu priskirti atminties bloko.\n");
        return false;
    }

    for ( unsigned int i = 0; i < dydis; ++i )
    {
       (*p)[i] = i + sk;
    }

    return true;
}

int main()
{
    // priskiriam atmintį pirmam blokui, kurį, tarkim, sudarys 10 elementų

    if(!TestFnc( &a[0], 10, 0 ))
    {
        printf("Klaida: (1)\n");
        Cleanup();
        return 1;
    }

    // priskiriam atmintį antram blokui, kurį, tarkim, sudarys 8 elementai

    if(!TestFnc( &a[1], 8, 10 ))
    {
        printf("Klaida: (2)\n");
        Cleanup();
        return 1;
    }

    // priskiriam atmintį trečiam blokui, kurį, tarkim, sudarys 4 elementai

    if(!TestFnc( &a[2], 4, 100 ))
    {
        printf("Klaida: (3)\n");
        Cleanup();
        return 1;
    }

    // bandom perskaityti keletą reikšmių

    printf("%d\n", a[0][0]);
    printf("%d\n", a[0][3]);
    printf("%d\n", a[1][0]);
    printf("%d\n", a[1][7]);
    printf("%d\n", a[2][0]);
    printf("%d\n", a[2][3]);

    Cleanup();


    return 0;
}

Failas:
dm-rodykles-2.c
gcc dm-rodykles-2.c -o testineprograma

Rezultatas paleidus:

0
3
10
17
100
103

testuojam su valgrind:

in use at exit: 0 bytes in 0 blocks
total heap usage: 4 allocs, 4 frees, 1,112 bytes allocated
All heap blocks were freed -- no leaks are possible