Programowanie strukturalne
- Wykład 6

Tablice jednowymiarowe

Definicja

Tablica - ciąg elementów jednego typu.

Deklaracja:

    int tab[4];
    char tab2[3];
    float tabf[5];

Inicjalizacja

int tab[4] = {1,-3,4,5};

Stałe tablice

const int tab[4] = {1,-3,4,5};

Tworzenie przez stałą DEFINE

#include <stdio.h>
#include <stdlib.h>
#define ROZMIAR 3

int main()
{
    int tab[ROZMIAR] = {3,-4,3};
    return 0;
}

Brak inicjalizacji?

#include <stdio.h>
#include <stdlib.h>
#define ROZMIAR 3

int main()
{
    int tab[ROZMIAR];
    for(int i=0;i<ROZMIAR;i++)
    {
        printf("%d\n",tab[i]);
    }
    return 0;
}

Za mało wartości?

#include <stdio.h>
#include <stdlib.h>
#define ROZMIAR 3

int main()
{
    int tab[ROZMIAR] ={2,3};
    for(int i=0;i<ROZMIAR;i++)
    {
        printf("%d\n",tab[i]);
    }
    return 0;
}

Za dużo wartości?

#include <stdio.h>
#include <stdlib.h>
#define ROZMIAR 2

int main()
{
    int tab[ROZMIAR] ={2,3,-2,0};
    for(int i=0;i<ROZMIAR;i++)
    {
        printf("%d\n",tab[i]);
    }
    return 0;
}
#include <stdio.h>
#include <stdlib.h>

int main()
{
    int tab[] ={2,3,-2,0};
    for(int i=0;i<sizeof tab/sizeof(int);i++)
    {
        printf("%d\n",tab[i]);
    }
    return 0;
}

Oznaczona inicjalizacja - od C99

#include <stdio.h>
#include <stdlib.h>
#define ROZMIAR 5

int main()
{
    int tab[ROZMIAR] = {[2] = 34};
    for(int i=0;i<ROZMIAR;i++)
    {
        printf("%d\n",tab[i]);
    }
    return 0;
}

Przypisanie wartości

#include <stdio.h>
#include <stdlib.h>
#define ROZMIAR 5

int main()
{
    int tab[ROZMIAR] = {[2] = 34};
    tab[0]=2;
    for(int i=0;i<ROZMIAR;i++)
    {
        printf("%d\n",tab[i]);
    }
    return 0;
}

Zakres tablic

#include <stdio.h>
#include <stdlib.h>
#define ROZMIAR 5

int main()
{
    int tab[ROZMIAR] = {1,-2,3};
    printf("%d",tab[-1]);
    return 0;
}

Jak określić rozmiar inaczej niż przez stałą DEFINE?

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

int main()
{
    int n = 5;
    int m = 8;
    int tabb1[5]; // ok
    int tabb2[3*2+1]; // ok
    int tabb3[sizeof(int)+1]; // ok
    //int tabb4[-2]; // nie,  bo <0
    int tabb5[0]; // ???
    //int tabb6[6.5]; // nie, bo nie calk.
    //int tabb7[(int)2.5]; // ???
    int tabb8[n]; // brak dla C90
    int tabb9[m]; // Brak dla C90
    return 0;
}

Wskaźniki do tablic

Nazwa tablicy jest równocześnie adresem jej pierwszego elementu.

#include <stdio.h>
#include <stdlib.h>
#define ROZMIAR 3

int main()
{
    int tab[ROZMIAR] = {4,0,-2};
    printf("%i",tab==&tab[0]);
    return 0;
}

Równoważne:

tab + 3 == &tab[3] // ten sam adres
*(tab + 3) == tab[3] // ta sama wartosc

Uwaga na nawiasy:

*(tab +2) // wartosc elementu tablicy o indeksie 2
*tab +2 // 2 dodane do wartosci elementu o indeksie 0

Kod do analizy

#include <stdio.h>
#include <stdlib.h>
#define ROZMIAR 3

int main()
{
    int tab[ROZMIAR] = {4,7,11};
    printf("%i\n",tab==&tab[0]);
    printf("%p\n",tab);
    printf("%p\n",&tab);
    printf("%p\n",&tab[0]);
    printf("%d\n",*tab);
    printf("%d\n",tab[0]);
    printf("%i\n",*tab==tab[0]);
    printf("%p\n",tab+1);
    printf("%p\n",&tab[1]);
    printf("%d\n",*(tab+1));
    printf("%d\n",tab[1]);
    printf("%d\n",*tab+1);
    return 0;
}

Przekazywanie tablic do funkcji

Równoważnie

int suma(int *tab, int n)
{
// tutaj kod
}

lub

int suma(int tab[], int n)
{
// tutaj kod
}

Uwaga: nie możemy wprost odczytać rozmiaru(!).

Kod do analizy:

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

int suma(int tab[])
{
    int temp=0;
    for(int i=0;i<sizeof tab/sizeof(int);i++)
    {
        temp+=tab[i];
    }
    return temp;
}

int main()
{
    int tablica[5]={1,-4,2,0,3};
    printf("%d\n",suma(tablica));
    return 0;
}

Działania na wskaźnikach

  • Przypisanie - przypisywana wartość może być tablicą albo zmienną poprzedzoną operatorem adresu (&), ewentualnie innym wskaźnikiem.
#include <stdio.h>
int main(void)
{
    int tab[4]={1,-3,4,2};
    int *wsk1, *wsk2;
    wsk1=tab;
    wsk2=&tab[2];
}
  • Pobranie wartości (dereferencja, wyłuskiwanie) - *
  • Pobranie adresu wskaźnika - &
  • Dodawanie liczb całkowitych do wskaźnika — za pomocą operatora + można dodać liczbę całkowitą do wskaźnika albo wskaźnik do liczby całkowitej. Wtedy liczba całkowita jest mnożona przez liczbę bajtów zajmowaną przez wartość wskazywanego typu, a wynik jest dodawany do pierwotnej wartości adresu.
#include <stdio.h>
int main(void)
{
    int tab[4]={1,-3,4,7};
    int *wsk1, *wsk2;
    wsk1=tab;
    wsk2=wsk1+3;
    printf("%p %p\n",wsk1,wsk2);
    printf("%d %d\n",*wsk1,*wsk2);
    return 0;
}
#include <stdio.h>

int main(void)
{
    printf("%Iu\n",sizeof(short));
    short tab[4]={1,-3,4,7};
    short *wsk1, *wsk2;
    wsk1=tab;
    wsk2=wsk1+3;
    printf("%p %p\n",wsk1,wsk2);
    printf("%d %d\n",*wsk1,*wsk2);
    return 0;
}
#include <stdio.h>

int main(void)
{
    printf("%Iu\n",sizeof(double));
    double tab[4]={1.2,-3.3,4.3,7.7};
    double *wsk1, *wsk2;
    wsk1=tab;
    wsk2=wsk1+3;
    printf("%p %p\n",wsk1,wsk2);
    printf("%lf %lf\n",*wsk1,*wsk2);
    return 0;
}
  • Zwiększenie wskaźnika (inkrementacja). Można je uzyskać przez zwykłe dodawanie lub za pomocą operatora inkrementacji. Zwiększenie wskaźnika do elementu tablicy sprawia, iż wskazuje on kolejny element.
#include <stdio.h>
int main(void)
{
    int tab[4]={1,-3,4,7};
    int *wsk1, *wsk2;
    wsk1=tab;
    printf("%p %d\n",wsk1,*wsk1);
    wsk1++;
    printf("%p %d\n",wsk1,*wsk1);
    return 0;
}
  • Odejmowanie liczby całkowitej od wskaźnika — w tym celu można użyć operatora - ; wskaźnik musi być pierwszym z operandów. Liczba całkowita jest mnożona przez liczbę bajtów zajmowaną przez wartość wskazywanego typu, a wynik jest odejmowany od pierwotnego adresu.
  • Zmniejszenie (dekrementacja) wskaźników - analogicznie.
  • Odejmowanie. Możliwe jest znalezienie różnicy między dwoma wskaźnikami. Działanie to jest wykonywane zwykle na wskaźnikach do elementów tej samej tablicy — w celu określenia, jak daleko od siebie się znajdują. Wynik jest wyrażony w jednostce o rozmiarze typu.
#include <stdio.h>
int main(void)
{
    int tab[4]={1,-3,4,7};
    int *wsk1, *wsk2;
    wsk1=tab;
    wsk2=&tab[3];
    printf("%d\n",wsk2-wsk1);
    return 0;
}
  • Porównanie — możemy używać operatorów relacyjnych, aby porównać wartości dwóch wskaźników tego samego typu.
#include <stdio.h>
int main(void)
{
    int tab[4]={1,-3,4,7};
    int *wsk1, *wsk2;
    wsk1=tab;
    wsk2=&tab[3];
    printf("%d\n",wsk2>wsk1);
    return 0;
}

A czy jest dodawanie wskaźników?

#include <stdio.h>
int main(void)
{
    int tab[4]={1,-3,4,7};
    int *wsk1, *wsk2;
    wsk1=tab;
    wsk2=&tab[3];
    //printf("%d\n",wsk2+wsk1);
    return 0;
}

Bibliografia