Wstęp do programowania w języku C/Tablice

From Studia Informatyczne

Tablice jednowymiarowe

W C tablice można inicjować podczas deklaracji.

int a[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

Jeśli liczba elementów początkowych jest mniejsza od długości tablicy, wówczas pozostałe elemnty tablicy inicjowane są na 0. Oto przykład inicjacji tablicy zerami.

int a[10] = {0};

Liczba elementów początkowych nie może być większa od rozmiaru tablicy.

Przykład

Należy wczytać liczbę całkowitą dodatnią typu long int i sprawdzić, czy w jej zapisie wszystkie cyfry są różne.

#include <stdio.h>
 
#define TRUE 1
#define FALSE 0
 
typedef int Bool;
 
main()
{
  Bool widziane_cyfry[10] = { 0 };
  int cyfra;
  long int n;
 
  printf("Podaj liczbe: ");
  scanf("%ld", &n);
 
  while (n > 0) {
    cyfra = n % 10;
    if ( widziane_cyfry[cyfra] )
      break;
    widziane_cyfry[cyfra] = TRUE;
    n /= 10;
  }
 
  if ( n > 0 )
    printf("Cyfry sie powatrzaja.\n\n");
  else
    printf("Cyfry nie powtarzaja sie\n\n");
  return 0;
}

Rozważmy teraz następujące zadanie. Niech R będzie dwuargumentową relacją na zbiorze X=\{0,1,\ldots,n-1\} (n jest parametrem zadania). Domknięciem przechodnim relacji R nazywamy relację R^* taką, że para (a,b)\in R^* wtedy i tylko wtedy, gdy a = b lub dla pewnego l\geq 1 istnieje ciąg a_0, a_1,\ldots,a_l elementów z X taki, że a_0=a, a_l=b, (a_0,a_1)\in R,\ldots, (a_{l-1},a_l)\in R. Napisz program, który dla danej relacji R oblicza jej domknięcie przechodnie.

#include <stdio.h>
#define Max_n 10
#define TRUE 1
#define FALSE 0
typedef int Bool;
main()
{
  Bool R[Max_n][Max_n] = { FALSE };
  int i, j, k, n;
 
  printf("Podaj rozmiar zbioru X (1 <= . <= %d) : ",
                                          Max_n);
  scanf("%d%", &n);
 
  /* Wczytanie relacji R */
  printf("Podawaj kolejne pary relacji R.\n");
  printf("Para -1 -1 konczy ciag wejsciowy\n");
  printf("Pamietaj, ze relacja jest zdefiniowana\n");
  printf("na zbiorze {0,...,%d\n", n-1);
 
  scanf("%d %d", &i , &j);
  while ( i != -1 ){
    R[i][j] = TRUE;
    scanf("%d %d", &i , &j);
  }
 
  /* Wypisanie poczatkowej relacji. */
  printf("\n\nRelacja R\n\n");
  for ( i = 0; i < n; i++){
    for ( j = 0; j < n; j++)
      printf("%d", R[i][j]);
    printf("\n");
  }
 
  /*Obliczanie domkniecia relacji R*/
  for ( i = 0; i < n; i++ )
    R[i][i] = TRUE;
 
  for ( k = 0; k < n; k++ )
  /*Nzm.: R[a][b] = TRUE wiw, gdy a=b lub 
          istnieje ciag
          elementow a_0, a_1, ...,a_l taki, ze 
          a_0 = a, a_l = b,
          a_1,...,a_{l-1} naleza do {0,...,k-1} oraz
          R[a_0][a_1] = TRUE,
          R[a_1,a_2] = TRUE,..., 
          R[a_{l-1}][a_l] = TRUE.
  */
    for ( i = 0; i < n; i++ )
      for ( j = 0; j < n; j++ )
        R[i][j] = R[i][j] || ( R[i][k] && R[k][j] );
 
  /* Wypisanie domkniecia relacji. */
  printf("\n\nDomkniecie relacji R\n\n");
  for ( i = 0; i < n; i++){
    for ( j = 0; j < n; j++)
      printf("%d", R[i][j]);
    printf("\n");
  }
 
  return 0;
}

Funkcje

Rozważmy program, który wczytuje dwie liczby rzeczywiste i liczy ich średnią.

#include <stdio.h>
 
main()
{
  double x, y;
 
  printf("podaj dwie liczby rzeczywiste: ");
  scanf("%lf %lf", &x, &y);
 
  printf("(%5.2f+%5.2f)/2 = %5.2f\n\n",x,y,(x+y)/2);
 
  return 0;
}

Oto ten sam program zapisany z użyciem funcji obliczającej średnią.

#include <stdio.h>
double srednia(double x, double y)
{
  return (x+y)/2;
}
 
main()
{
  double x, y;
 
  printf("podaj dwie liczby rzeczywiste: ");
  scanf("%lf %lf", &x, &y);
 
  printf("(%5.2f+%5.2f)/2=%5.2f\n",x,y,srednia(x,y));
 
  return 0;
}

C nie wymaga definiowania funkcji przed jej wywołaniem. Dobrym zwyczajem jest jednak deklarować funkcję przed miejscem jej wywołania. Tym razem deklaracja funkcji została oddzielona od jej definicji.

#include <stdio.h>
double srednia(double x, double y); 
/* Deklaracja funkcji */
main()
{
  double x, y;
 
  printf("podaj dwie liczby rzeczywiste: ");
  scanf("%lf %lf", &x, &y);
 
  printf("(%5.2f+%5.2f)/2=%5.2f\n",x,y,srednia(x,y));
 
  return 0;
}
 
double srednia(double x, double y) 
/* Definicja funkcji */
{
  return (x+y)/2;
}

Funkcja w C nie musi zwracać żadnej wartości. Oto funkcja tab, która dostaje na wejściu liczbę całkowitą n\in [1..10] i wypisuje tabliczkę możenia przez n. Zwróć uwagę na słówko kluczowe void które oznacza, że funkcja nie zwraca wartości. Taka funkcja nie może być wywoływana jako część wyrażenia. Wywołanie funkcji tab musi mieć postać:

tab(parametr);

void tab(int n)
{
  int i;
 
  for ( i = 1; i <= 10; i++ )
    printf("%d * %d = %d\n", n, i, n*i);
}

Funkcja też może nie mieć żadnych parametrów, np.

void gwiazdki(void)
{
  printf("*********************");
}

Taką funkcję wywułujemy:

gwiazdki();

Ogólnie definicja funkcji ma postać:

typ wartości nazwa--funkcji ( parametry )
{
 deklaracje
 instrukcje
}

Zwróćmy uwagę na różnicę między parametrami i argumentami funkcji. Prametry występują na poziomie definicji funkcji. Są one "zmiennymi", którym muszą być przypisane rzeczywiste argumenty (wyrażenia) podczas wywołania funkcji. W C argumenty są przekazywane przez wartość: podczas wywołania funkcji obliczana jest wartość każdego argumentu (wyrażenia) i ta wartość jest przypisywane do odpowiedniego parametru. Zmiany wartości parametru podczas obliczeń nie mają wpływu na wartość argumentu. Parametrami mogą być także tablice. W deklaracji funkcji możemy nie specyfikować rozmiaru tablicy. Oto przykład parametru-tablicy:

int sum_tab(int a[], int n)
{
  int i, sum = 0;
 
  for (i = 0; i < n; i++)
    sum += a[i];
 
  return sum;
}

Gdy chcemy parametrem uczynić tablicę dwuwymiarową musimy podać liczbę kolumn tablicy.

#define L_kol 10
 
int sum_tab(int a[][L_kol], int n)
{
  int i, j, sum = 0;
 
  for (i = 0; i < n; i++)
    for (j = 0; j < n; j++)
      sum += a[i][j];
 
  return sum;
}

Pamiętajmy, że każda funkcja nie-void musi zwracać wartość. Instrukcja return ma postać:

return wyrażenie;