Serwis Edukacyjny
w I-LO w Tarnowie
obrazek

Materiały dla uczniów liceum

  Wyjście       Spis treści       Wstecz       Dalej  

obrazek

Autor artykułu: mgr Jerzy Wałaszek
Konsultacje: Wojciech Grodowski, mgr inż. Janusz Wałaszek

©2024 mgr Jerzy Wałaszek
I LO w Tarnowie

obrazek

Warsztat

Kurs języka C

Operacje na tekstach

SPIS TREŚCI
Podrozdziały

Funkcje pamięci

Język C posiada wiele pożytecznych bibliotek, które zostały opracowane w celu ułatwienia programowania. Dostęp do funkcji tych bibliotek następuje po dołączeniu odpowiedniego pliku nagłówkowego, w którym znajdują się definicje struktur danych oraz funkcji danej biblioteki. Korzystaliśmy z tej opcji już wcześniej, używając dyrektywy preprocesora #include <stdio.h>. Dzięki niej mieliśmy dostęp do takich funkcji jak printf(), scanf(), puts(), itp. Dlaczego warto korzystać z funkcji bibliotecznych? Odpowiedź jest prosta: bo są dostępne, wiec po co się męczyć i wymyślać koło od nowa. Drugi powód to taki, że funkcje biblioteczne są napisane profesjonalnie i dobrze przebadane. Jeśli używasz ich zgodnie z ich przeznaczeniem, to raczej nie powinieneś wpaść w kłopoty. A z funkcjami własnymi jest różnie. Czasem programiście coś umknie i program staje się niestabilny. Uwierz mi, poszukiwanie błędów w dużych programach jest ciężką, żmudną i niewdzięczną pracą. Jeśli się da, to lepiej jej unikać.

W środowisku kompilatorów C dla mikrokontrolerów AVR (Atmel) oraz PIC (Microchip) dostępny jest plik nagłówkowy string.h, który definiuje funkcje wspomagające przetwarzanie obszarów pamięci. Poniżej opiszemy podstawowe funkcje zdefiniowane w tym pliku i dotyczące pamięci (w rzeczywistości jest ich więcej, lecz nie wszystkie dostępne są w obu środowiskach).

MEMSET

Funkcja memset() (ang. memory set), wypełnia zadany obszar wybraną wartością. Składnia jest następująca:

memset(adres_obszaru, wartość, liczba_bajtów);
adres_obszaru wskaźnik pierwszego bajtu obszaru, który ma zostać wypełniony
wartość obszar zostanie wypełniony bajtami o tej wartości
liczba_bajtów tyle bajtów obszaru zostanie wypełnione podaną wartością

Funkcja zwraca w wyniku adres wypełnionego obszaru.
Uruchom program:

/*
 Pamięć
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 29.10.2016
*/

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

int main()
{
  signed char t[10];
  int i;

  setlocale(LC_ALL,"");

  memset(t, -55, 10); // wypełniamy t liczbą -55
  for(i = 0; i < 10; i++) printf("t[%d] = %d\n", i, t[i]);

  return 0;
}

Program tworzy tablicę znakową t o rozmiarze 10 znaków, po czym wypełnia ją liczbami -55. Na końcu zostaje wyświetlona zawartość tablicy.

Funkcję memset() wykorzystuje się najczęściej do szybkiego zerowania tablic. Jeśli tablica przechowuje dane innego typu niż char, to należy uwzględnić ich rozmiar:

/*
 Pamięć
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 29.10.2016
*/

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

int main()
{
  float t[10];
  int i;

  setlocale(LC_ALL,"");

  memset(t, 0, 10 * sizeof(float)); // zerujemy t
  for(i = 0; i < 10; i++) printf("t[%d] = %f\n", i, t[i]);

  return 0;
}

MEMCPY

Funkcja memcpy() (ang. memory copy) kopiuje zawartość jednego obszaru pamięci do drugiego. Oba obszary nie powinny się pokrywać. W przeciwnym razie może dojść do nadpisania danych (rozważ, co się stanie, jeśli kopiujesz dane z obszaru a do obszaru b, a obszar b pokrywa częściowo obszar a). Składnia jest następująca:

memcpy(adres_przeznaczenia, adres_źródła, liczba_bajtów);
adres_przeznaczenia wskaźnik pierwszego bajtu obszaru pamięci, do którego odbędzie się kopiowanie
adres_źródła wskaźnik pierwszego bajtu obszaru, który będzie kopiowany
liczba_bajtów tyle bajtów zostanie skopiowane

Funkcja zwraca adres przeznaczenia.

Uruchom program:

/*
 Pamięć
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 29.10.2016
*/

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

// Wypisuje zawartość tablicy znakowej jako liczby
//------------------------------------------------
void pc(char c, char * x, int n)
{
  int i;

  printf("%c[] = ", c);
  for(i = 0; i < n; i++) printf("%3d", x[i]);
  printf("\n");
}

int main()
{
  char s[] = {81,82,83,84,85};
  char t[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9};

  setlocale(LC_ALL,"");

  pc('s', s, 5);
  printf("\nPrzed:\n");
  pc('t', t, 9);
  memcpy(&t[2], s, 5);
  printf("\nPo:\n");
  pc('t', t, 9);

  return 0;
}

Program tworzy dwie tablice znakowe: s [ ] – 5-cio elementowa i t [ ] – 9-cio elementową. Tablice są wstępnie wypełnione liczbami. Następnie program kopiuje zawartość tablicy s do tablicy t od pozycji elementu t [2].

Jeśli obszary zawierają elementy o rozmiarze innym niż 1 bajt, to należy to odpowiednio uwzględnić w ilości kopiowanych bajtów. Ten sam przykład dla liczb całkowitych:

/*
 Pamięć
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 29.10.2016
*/

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

// Wypisuje zawartość tablicy
//---------------------------
void pi(char c, int * x, int n)
{
  int i;

  printf("%c[] = ",c);
  for(i = 0; i < n; i++) printf("%3d",x[i]);
  printf("\n");
}

int main()
{
  int s[] = {81,82,83,84,85};
  int t[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9};

  setlocale(LC_ALL,"");

  pi('s', s, 5);
  printf("\nPrzed:\n");
  pi('t', t, 9);
  memcpy(&t[2], s, 5 * sizeof(int));
  printf("\nPo:\n");
  pi('t', t, 9);

  return 0;
}

Ostrożność należy zachować, jeśli kopiowane obszary pamięci pokrywają się częściowo. W takim przypadku wynik jest nieokreślony, ponieważ zależy od konkretnej implementacji funkcji memcpy().

MEMMOVE

Funkcja memmove() (ang. memory move) również kopiuje obszary pamięci, lecz w tym przypadku obszary te mogą na siebie częściowo zachodzić.

memmove(adres_przeznaczenia, adres_źródła, liczba_bajtów);
adres_przeznaczenia wskaźnik pierwszego bajtu obszaru pamięci, do którego odbędzie się kopiowanie
adres_źródła wskaźnik pierwszego bajtu obszaru, który będzie kopiowany
liczba_bajtów tyle bajtów zostanie skopiowane

Uruchom program:

/*
 Pamięć
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 29.10.2016
*/

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

// Wypisuje zawartość tablicy
//---------------------------
void pi(char c, int * x, int n)
{
  int i;

  printf("%c[] = ",c);
  for(i = 0; i < n; i++) printf("%3d",x[i]);
  printf("\n");
}

int main()
{
  int t[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9};

  setlocale(LC_ALL,"");

  printf("\nPrzed:\n");
  pi('t', t, 9);
  memmove(t, &t[2], 7 * sizeof(int));
  printf("\nPo:\n");
  pi('t', t, 9);

 return 0;
}

Program tworzy tablicę t z ustaloną zawartością. Następnie przenosi na początek fragment tej tablicy od komórki t [2] w górę.

Zmodyfikuj ten program, tak aby przenoszony fragment rozpoczynał się na początku tablicy i był przenoszony w obszar rozpoczynający się od komórki t [2]. Zwróć uwagę na rozmiar przenoszonego obszaru, który należy dobrać, tak aby program nie nadpisał danych umieszczonych w pamięci za ostatnią komórką tablicy.

MEMCHR

Funkcja memchr() (ang. memory character) przeszukuje zadany obszar pamięci, szukając wystąpienia w nim bajtu o podanej wartości. Jeśli taki bajt znajdzie, to jest zwracany jego adres. Jeśli nie, to funkcja zwraca wskaźnik pusty NULL – w języku C adres NULL (czyli 0) nie wskazuje żadnego obiektu w pamięci komputera.

memchr(adres_obszaru, wartość, liczba_bajtów);
adres_obszaru wskaźnik pierwszego bajtu obszaru pamięci, w którym będzie poszukiwana zadana wartość
wartość wartość do wyszukania
liczba_bajtów tyle bajtów zostanie przeszukane

Uruchom program:

/*
 Pamięć
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 29.10.2016
*/

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

int main()
{
  char t[] = {12,76,33,11,55,97,48,62,81};
  char v = 48; // szukana wartość;
  char * p;
  int i;

  setlocale(LC_ALL,"");

  p = memchr(t,v,9);
  printf("Szukane %d: ", v);
  if(p)
  {
    printf("na pozycji %d w t[]\n\n"
           "t[] = ", p - t);
    for(i = 0; i < 9; i++)
      if(p == &t[i]) printf("{%d} ", t[i]);
      else           printf(" %d ", t[i]);
  }
  else printf("BRAK W t[]");
  printf("\n\n");

  return 0;
}

MEMCMP

Funkcja memcmp() (ang. memory compare) porównuje ze sobą dwa obszary pamięci. Funkcja posiada następujące parametry:

memcmp(adres_obszaru_1, adres_obszaru_2, liczba_bajtów);
adres_obszaru wskaźnik pierwszego bajtu porównywanego obszaru
liczba_bajtów tyle bajtów zostanie porównane

Funkcja zwraca w wyniku:

Na przykład:

obszar 1: 2,3,5,5,7,2,8,1,9 wynik 0, obszar_1 = obszar_2
obszar 2: 2,3,5,5,7,2,8,1,9
obszar 1: 2,3,5,5,7,2,8,1,9 wynik 1, obszar_1 > obszar_2
obszar 2: 2,3,5,5,7,0,8,1,9
obszar 1: 2,3,5,5,7,2,8,1,9 wynik -1, obszar_1 < obszar_2
obszar 2: 2,3,5,5,7,2,9,1,9

Uruchom program:

/*
 Pamięć
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 30.10.2016
*/

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

int main()
{
  char a[] = {2,3,5,5,7,2,8,1,9};
  char b[] = {2,3,5,5,7,2,8,1,9};
  char c[] = {2,3,5,5,7,0,8,1,9};
  char d[] = {2,3,5,5,7,2,9,1,9};

  setlocale(LC_ALL,"");

  printf("a i b -> %2d\n", memcmp(a,b,9));
  printf("a i c -> %2d\n", memcmp(a,c,9));
  printf("a i d -> %2d\n", memcmp(a,d,9));

  return 0;
}

Na początek:  podrozdziału   strony 

Funkcje łańcuchowe

Tekst/łańcuch (ang. string) jest w języku C obszarem pamięci, w którym przechowuje się znaki. Na końcu tekstu wstawiany jest znak NUL o kodzie 0, który pełni funkcję znacznika końca tekstu. Teksty przechowujemy w tablicach znakowych. Tablica powinna posiadać wystarczająco duży rozmiar, aby pomieścić znaki łańcucha oraz kończący go znak NUL.

Stała łańcuchowa to tekst umieszczony w cudzysłowach:

"ABC", "domek", "Bartek i Agatka", itp.

Wartością tej stałej jest wskaźnik do obszaru, w którym umieszczone są znaki:

/*
 Teksty
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 31.10.2016
*/

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

int main()
{
  char *p = "Hulaj dusza ile wola!";

  setlocale(LC_ALL,"");

  while(*p) printf("znak tekstu: %c\n",*p++);

  return 0;
}

Ponieważ stała łańcuchowa daje w wyniku wskaźnik, to nie możemy przypisać ją do tablicy znakowej, np. tak:
char t[12];
t = "Abracadabra"; // Źle!!

Zamiarem programisty było tu wprowadzenie do tablicy tekstu podanego w cudzysłowach. Jednak to nie zadziała, ponieważ jest to próba przypisania nowego adresu wskaźnikowi t, a jak pamiętamy z rozdziału o wskaźnikach i tablicach, zmienna tablicy jest stałą, czyli elementem, który nie może zmieniać w programie swojej wartości. Poza tym, nawet gdyby to zadziałało, to tekst nie znalazłby się w tablicy. Jedynie jego adres zostałby przypisany zmiennej t. A nie o to chodziło. Zatem do umieszczenia tekstu w tablicy musimy skorzystać z odpowiedniej funkcji łańcuchowej, która zadanie to wykona jak należy.

Wyjątkiem jest inicjalizacja tablicy w momencie jej tworzenia, wtedy można zastosować konstrukcję:
char tablica[] = "dowolny tekst";

Tekst zostanie umieszczony w tablicy, która otrzyma taki rozmiar, aby ten tekst pomieścić wraz z kończącym go znakiem NUL.

STRCPY

Funkcja strcpy() (ang. string copy) kopiuje łańcuch znakowy do tablicy znakowej. Kopiowane są wszystkie znaki łańcucha wraz z kończącym go znakiem NUL. Tablica powinna być odpowiednio duża, aby pomieścić cały łańcuch tekstowy wraz ze znakiem końca tekstu.
strcpy(adres_docelowy, adres_łańcucha);

adres_docelowy wskaźnik pierwszego bajtu obszaru, w którym zostanie umieszczony kopiowany łańcuch tekstowy
adres_łańcucha wskaźnik pierwszego znaku kopiowanego łańcucha tekstowego.

Funkcja zwraca w wyniku adres docelowy.
/*
 Teksty
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 31.10.2016
*/

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

int main()
{
  char t[12];

  setlocale(LC_ALL,"");

  strcpy(t,"Abracadabra");
  printf("%s\n",t);

  return 0;
}

STRNCPY

Funkcja strncpy() działa podobnie do poprzednio opisanej funkcji strcpy(). Różnica jest taka, iż kopiuje co najwyżej zadaną liczbę znaków. Jednak musisz zwrócić uwagę na jedną ważną rzecz: Jeśli w n znakach kopiowanego łańcucha nie znajdzie się znak o kodzie 0, to nie będzie on umieszczony w tablicy docelowej. Musisz sam o to zadbać, przez wpisanie kodu 0 na pozycję za n pierwszymi znakami w tablicy.
strncpy(adres_docelowy, adres_łańcucha, liczba_znaków);

adres_docelowy wskaźnik pierwszego bajtu obszaru, w którym zostanie umieszczony kopiowany łańcuch tekstowy
adres_łańcucha wskaźnik pierwszego znaku kopiowanego łańcucha tekstowego.
liczba_znaków tyle maksymalnie znaków zostanie przesłanych z łańcucha do obszaru docelowego. Jeśli łańcuch jest krótszy, to na końcu łańcucha będzie umieszczony znak NUL. Jeśli łańcuch jest dłuższy, to znak NUL nie będzie umieszczony w obszarze docelowym.

Wynikiem funkcji jest adres docelowy.
/*
 Teksty
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 31.10.2016
*/

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

int main()
{
  char t[6];

  setlocale(LC_ALL,"");

  strncpy(t,"Abracadabra",6);
  printf("%s\n",t);

  return 0;
}

W tym programie kopiujemy do tablicy t dłuższy łańcuch i nie umieszczamy na końcu znaku NUL. Za ostatnim znakiem na wydruku pojawią się śmieci z pamięci za tablicą.
/*
 Teksty
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 31.10.2016
*/

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

int main()
{
  char t[7];

  setlocale(LC_ALL,"");

  strncpy(t,"Abracadabra",6);
  t[6] = 0; // Znak NUL na koniec
  printf("%s\n",t);

  return 0;
}

Ten z kolei program robi to właściwie. Tekst w tablicy to pierwsze 6 znaków łańcucha plus znak NUL.

STRLEN

Funkcja strlen() (ang. string length) zwraca liczbę znaków, które zawiera podany łańcuch. Znak NUL nie jest wliczany.
strlen(adres_łańcucha);

adres_łańcucha wskaźnik pierwszego znaku łańcucha tekstowego, którego liczbę znaków funkcja wylicza.

Uruchom program:
/*
 Teksty
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 31.10.2016
*/

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

int main()
{
  char t[20];

  setlocale(LC_ALL,"");

  strcpy(t,"Kajko i Kokosz");
  printf("Tekst >%s< ma znakow %d\n",t,strlen(t));

  return 0;
}

STRCAT

Funkcja strcat() (ang. string concatenate) dołącza do końca jednego łańcucha tekstowego drugi. W efekcie powstaje łańcuch tekstowy zawierający tekst obu łańcuchów.
strcat(adres_docelowy, adres_łańcucha);

adres_docelowy wskaźnik pierwszego znaku obszaru zawierającego łańcuch, do którego końca zostanie dołączony drugi łańcuch.
adres_łańcucha wskaźnik pierwszego znaku dołączanego łańcucha tekstowego.

Funkcja zwraca adres docelowy. Obszar docelowy powinien być wystarczająco duży, aby pomieścić tekst obu łańcuchów oraz zamykający znak NUL.
/*
 Teksty
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 31.10.2016
*/

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

int main()
{
  char t[100];

  setlocale(LC_ALL,"");

  strcpy(t,"Kajko i Kokosz"); printf("%s\n",t);
  strcat(t," to dwaj"); printf("%s\n",t);
  strcat(t," dobrzy"); printf("%s\n",t);
  strcat(t," przyjaciele."); printf("%s\n",t);

  return 0;
}

STRNCAT

Funkcja strncat() działa podobnie do strcat(). Jednakże posiada dodatkowy parametr, który określa maksymalną liczbę znaków do dołączenia z drugiego łańcucha. Wynikowy łańcuch będzie zakończony znakiem NUL.
strncat(adres_docelowy, adres_łańcucha, liczba_znaków);

adres_docelowy wskaźnik pierwszego znaku obszaru zawierającego łańcuch, do którego końca zostanie dołączony drugi łańcuch.
adres_łańcucha wskaźnik pierwszego znaku dołączanego łańcucha tekstowego.
liczba_znaków tyle maksymalnie znaków będzie dołączone z łańcucha.

Funkcja zwraca adres docelowy.
/*
 Teksty
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 31.10.2016
*/

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

#define MAXC 78

int main()
{
  char t[MAXC + 1];

  setlocale(LC_ALL,"");

  t[0] = 0; // pusty łańcuch tekstowy
  while(strlen(t) < MAXC)
    printf("%s\n",strncat(t,"Ala ma rudego kocura gbura ",MAXC - strlen(t)));

  return 0;
}

STRCHR

Funkcja strchr() (ang. string character) szuka w podanym łańcuchu tekstowym pierwszego wystąpienia określonego znaku. Jeśli znak zostanie znaleziony, funkcja zwraca jego adres. Jeśli znaku nie ma w łańcuchu zwracany jest adres pusty (NULL).
strchr(adres_łańcucha, znak_do_wyszukania);

adres_łańcucha wskaźnik pierwszego znaku przeszukiwanego łańcucha tekstowego.
znak_do_wyszukania kod ASCII znaku, który będzie poszukiwany w łańcuchu.

Poniższy program demonstruje sposób używania funkcji strchr() do wyszukania wszystkich wystąpień litery 'e', która zostaje zastąpiona literą 'E':
/*
 Teksty
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 1.11.2016
*/

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

int main()
{
  char t[] = "Dzisiaj misiaczek wypije kubeczek pysznego miodku.";
  char *p;

  setlocale(LC_ALL,"");

  printf("Przed: %s\n",t);
  for(p = t;(p = strchr(p,'e')); p++) *p = 'E';
  printf("Po : %s\n",t);

  return 0;
}

W programie wykorzystujemy wskaźnik p, który przed pętlą ustawiamy na adres tablicy znakowej t, która przechowuje przeszukiwany łańcuch. Następnie w pętli wykonujemy poszukiwanie pierwszego wystąpienia litery 'e'. Gdy zostanie znaleziona, do wskaźnika p trafi jej adres. Zmieniamy wtedy znak 'e' na 'E', po czym jako nowy adres łańcucha przyjmujemy adres następnego znaku. Jeśli litery 'e' funkcja strchr() nie znajdzie w łańcuchu, to zwróci wskaźnik NULL. Instrukcja przypisania w warunku kontynuacji pętli będzie miała wartość 0, zatem pętla przestanie być wykonywana. Zwróć uwagę, że instrukcja ta jest ujęta w nawiasy. Jest to znak dla kompilatora, iż nie pomyliliśmy się i nie jest to test równości typu p == ..., lecz właśnie instrukcja przypisania. Spróbuj te nawiasy usunąć, a zobaczysz reakcję kompilatora. Dzięki takim konstrukcjom program w języku C staje się bardziej zwięzły i krótszy.

STRRCHR

Funkcja strrchr() (ang. string right character) działa podobnie do strchr(). Różnica polega na tym, iż znak jest wyszukiwany od końca łańcucha. Jeśli zostanie znaleziony, to funkcja zwraca jego adres. W przeciwnym razie zwraca wskaźnik pusty (NULL).
strrchr(adres_łańcucha, znak_do_wyszukania);

adres_łańcucha wskaźnik pierwszego znaku przeszukiwanego łańcucha tekstowego.
znak_do_wyszukania kod ASCII znaku, który będzie poszukiwany w łańcuchu.

Poniższy program zamienia w ostatnim wyrazie litery małe na duże – postaraj się odkryć, jak to jest robione:
/*
 Teksty
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 2.11.2016
*/

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

int main()
{
  char t[] = "Dzisiaj misiaczek wypije kubeczek pysznego miodku.";
  char *p;

  setlocale(LC_ALL,"");

  printf("Przed: %s\n",t);
  if((p = strrchr(t,' ')))
  while(*++p) if(*p >= 'a' && *p <= 'z') *p -= 32;
  printf("Po : %s\n",t);

  return 0;
}

STRCMP

Funkcja strcmp() (ang. string compare) porównuje ze sobą dwa łańcuchy znakowe s1 i s2. Zwraca następujące wartości:
0 s1 jest równy s2, tzn. zawiera dokładnie ten sam tekst.
-1 Łańcuch s1 jest leksykograficznie mniejszy od s2. Oznacza to jedną z dwóch możliwości:
  1. Łańcuch s1 zawiera te same znaki co początek łańcucha s2, lecz jest krótszy od s2. Np. "Jan" < "Janek".
  2. Pierwszy różny znak w obu łańcuchach ma w łańcuchu s1 mniejszy kod ASCII od kodu odpowiadającego mu znaku w łańcuchu s2. Np. "Janek" < "Jasiek". Pozostałe znaki nie mają już znaczenia.
1 Łańcuch s1 jest leksykograficznie większy od łańcucha s2. Oznacza to jedną z dwóch możliwości:
  1. Początek łańcucha s1 zawiera te same znaki, co łańcuch s2, lecz łańcuch s1 jest dłuższy od s2. Np. "Kazik" > "Kaz".
  2. Pierwszy różny znak w obu łańcuchach ma w łańcuchu s1 wiekszy kod ASCII od kodu odpowiadającego mu znaku w łańcuchu s2. Np. "Kazimierz" > "Kazik". Pozostałe znaki nie mają już znaczenia.

Funkcja posiada następującą składnię:
strcmp(adres_łańcucha_s1, adres_łańcucha_s2);

adres_łańcucha wskaźnik pierwszego znaku łańcucha uczestniczącego w porównaniu.

Uruchom program:
/*
 Teksty
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 2.11.2016
*/

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

int main()
{
  char s1[100], s2[100], c;

  setlocale(LC_ALL,"");

  printf("Wpisz dwa wyrazy:\n\n");
  scanf("%s %s", s1, s2);
  switch(strcmp(s1, s2))
  {
    case -1: c = '<'; break;
    case 0: c = '='; break;
    case 1: c = '>'; break;
  }
  printf("\n\n%s %c %s\n\n", s1, c, s2);

  return 0;
}

STRNCMP

Funkcja strncmp() działa identycznie jak strcmp(). Różnicą jest to, że porównanie dotyczy zadanej liczby początkowych znaków obu łańcuchów.
strncmp(adres_łańcucha_s1, adres_łańcucha_s2, liczba_znaków);

adres_łańcucha wskaźnik pierwszego znaku łańcucha uczestniczącego w porównaniu.
liczba_znaków tyle maksymalnie znaków będzie porównywanych

Uruchom program:
/*
 Teksty
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 2.11.2016
*/

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

int main()
{
  char s1[100],s2[100],c;

  setlocale(LC_ALL,"");

  printf("Wpisz dwa wyrazy:\n\n");
  scanf("%s %s",s1,s2);
  switch(strncmp(s1,s2,3))
  {
    case -1: c = '<'; break;
    case 0: c = '='; break;
    case 1: c = '>'; break;
  }
  printf("\n\nWg trzech pierwszych znakow %s %c %s\n\n",s1,c,s2);

  return 0;
}

STRSTR

Funkcja strstr() (ang. string in string) zwraca adres pierwszego wystąpienia łańcucha s2 w łańcuchu s1. Jeśli łańcuch s2 nie występuje w s1, funkcja zwraca adres pusty (NULL).
strstr(adres_łańcucha_s1, adres_łańcucha_s2);

adres_łańcucha wskaźnik pierwszego znaku łańcucha.

Uruchom program:
/*
 Teksty
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 3.11.2016
*/

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

int main()
{
  char s1[] = "Kuba nie lubi kawy, mleka i kakao";
  char s2[] = "ka"
  char *p;
  int i;

  setlocale(LC_ALL,"");

  strcpy(s1,);
  strcpy(s2,"ka");
  p = s1;
  printf("Przed: %s\n\n",s1);
  while((p = strstr(p,s2)))
    for(i = 0; s2[i]; i++) *p++ -= 32;
  printf("Po : %s\n\n",s1);

  return 0;
}

Program wyszukuje w łańcuchu s1 wszystkie wystąpienia łańcucha s2 i zmienia w nich literki małe na duże. Postaraj się odkryć zasadę jego działania.

STRPBRK

Funkcja strpbrk() (ang. string pointer break) zwraca adres pierwszego wystąpienia w łańcuchu s1 dowolnego ze znaków umieszczonych w łańcuchu s2. Jeśli żaden ze znaków w s2 nie pojawia się w s1, funkcja zwraca adres pusty.
strpbrk(adres_łańcucha_s1, adres_łańcucha_s2);

adres_łańcucha wskaźnik pierwszego znaku łańcucha.

Uruchom poniższy program, który w łańcuchu s zamienia wszystkie samogłoski małe na duże:
/*
 Teksty
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 3.11.2016
*/

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

int main()
{
  char s[80],*p;

  setlocale(LC_ALL,"");

  printf("Wpisz wiersz tekstu (do 79 znaków):\n\n");
  gets(s);
  p = s;
  while((p = strpbrk(p,"aeiou"))) *p++ -= 32;
  printf("%s\n\n",s);

  return 0;
}

STRTOK

Funkcja strtok() (ang. string to keywords) rozbija łańcuch na poszczególne słowa, które mogą być oddzielone od siebie znakami separatorów. Składnia funkcji jest następująca:
strtok(adres_łańcucha_s, adres_łańcucha_separatorów);

adres_łańcucha_s wskaźnik pierwszego znaku łańcucha, który będzie rozbijany na słowa.
adres_łańcucha_separatorów wskaźnik pierwszego znaku łańcucha zawierającego znaki separujące.

Funkcja zwraca adres pierwszego znaku słowa lub NULL, jeśli brak dalszych słów w łańcuchu.

Aby poprawnie użyć tej funkcji, musisz zrozumieć sposób jej działania. W pierwszym wywołaniu podajemy w pierwszym argumencie adres łańcucha s, który chcemy rozbić na słowa. W drugim argumencie podajemy adres łańcucha ze znakami separującymi słowa. Funkcja znajduje pierwsze słowo, a znak separatora za tym słowem zastępuje znakiem NUL i zwraca jako wynik adres pierwszego znaku tego słowa. W kolejnych wywołaniach w pierwszym parametrze podajesz adres pusty NULL. Drugi parametr pozostaje bez zmian. Adres NULL jest informacją dla funkcji, aby w pierwotnym łańcuchu wyszukała następne słowo.  Funkcja wyszukuje je i zwraca jego adres, a na końcu słowa jest umieszczany znak NUL, który zastępuje separator. Powtarzamy tę sekwencję aż do momentu, gdy funkcja zwróci adres NULL, co oznacza brak dalszych słów.

Uruchom program:
/*
 Teksty
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 5.11.2016
*/

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

int main()
{
  char s[80],*p,*t = ",?!.;: ";
  int i;

  setlocale(LC_ALL,"");

  printf("Wpisz wiersz tekstu (do 79 znaków):\n\n");
  gets(s);
  printf("\n");
  p = strtok(s, t); // pierwsze słowo
  i = 1;
  while(p) // dopóki są słowa
  {
    printf("Wyraz nr %2d : %s\n",i++,p);
    p = strtok(NULL, t); // następne słowa
  }

  return 0;
}

Usunięcie znaku

Nie ma funkcji standardowej, która usuwa z łańcucha znak. Jednak funkcję taką można łatwo napisać. Uruchom poniższy program:
/*
 Teksty
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 6.11.2016
*/

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

// Usuwa znak wskazywany przez p
void delchr(char *p)
{
  if(p) // jeśli p wskazuje łańcuch
    do
    {
      p++; // następny znak w łańcuchu
      *(p - 1) = *p; // kopiujemy znaki
    } while(*p); // aż do znaku NUL włącznie
}

int main()
{
  char t[80], *p;

  setlocale(LC_ALL,"");

  printf("Wpisz wiersz tekstu (do 79 znaków):\n\n");
  gets(t);
  p = t;
  while(*p)
    if(*p == 'a') delchr(p);
    else          p++;
  printf("%s\n\n",t);

  return 0;
}

Wstawienie znaku

Nie ma również funkcji, która wstawia do łańcucha nowy znak. Napiszemy sobie ją sami. Tablica przechowująca łańcuch musi być dostatecznie duża, aby pomieścić go po wstawieniu nowego znaku.

/*
 Teksty
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 6.11.2016
*/

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

// dodaje znak na pozycję p
void inschr(char *p, char a)
{
  if(p) // jeśli p wskazuje łańcuch
  {
    memmove(p+1,p,strlen(p)+1); // robimy miejsce
    *p = a; // wstawiamy znak
  }
}

int main()
{
  char t[80], *p;

  setlocale(LC_ALL,"");

  printf("Wpisz wiersz tekstu (do 79 znaków):\n\n");
  gets(t);
  p = t;
  while(*p)
  {
    if(*p == 'a') inschr(p++,'x');
    p++;
  }
  printf("%s\n\n",t);

  return 0;
}

W ramach ćwiczeń napisz podobne funkcje:


Na początek:  podrozdziału   strony 

Funkcje konwersji

Funkcje konwersji umożliwiają przetwarzanie wartości liczbowej na łańcuch tekstowy i na odwrót. Definicje funkcji znajdują się w pliku nagłówkowym stdlib.h.

ATOI

Funkcja atoi() (ang. ASCII string to integer) oblicza wartość dziesiętną liczby całkowitej zapisanej w postaci znaków ASCII. Początkowe spacje są pomijane. Przed cyframi liczby może się znajdować znak + lub -. Wynik konwersji zwracany jest jako wartość typu int.

atoi(adres_łańcucha);
adres_łańcucha wskaźnik pierwszego znaku łańcucha.

Jeśli tekstu nie da się zinterpretować jako liczbę całkowitą, funkcja zwraca wartość 0.

/*
 Konwersje znakowo-liczbowe
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 3.11.2016
*/

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

int main()
{
  char s[] = "-1236";

  setlocale(LC_ALL,"");

  printf("%d\n\n",atoi(s));

  return 0;
}

ATOL

Funkcja atol() (ang. ASCII string to long integer) oblicza wartość dziesiętną liczby całkowitej zapisanej w postaci znaków ASCII. Początkowe spacje są pomijane. Przed cyframi liczby może się znajdować znak + lub -. Wynik konwersji zwracany jest jako wartość typu long int.

atol(adres_łańcucha);
adres_łańcucha wskaźnik pierwszego znaku łańcucha.

Jeśli tekstu nie da się zinterpretować jako liczbę całkowitą, funkcja zwraca wartość 0.

/*
 Konwersje znakowo-liczbowe
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 3.11.2016
*/

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

int main()
{
  char s[] = "386799588";

  setlocale(LC_ALL,"");

  printf("%ld\n\n",atol(s));

  return 0;
}

W dużym komputerze PC nie ma różnicy pomiędzy typem int a long int, ponieważ oba oznaczają liczbę 32-bitową. Jednak w mikrokontrolerach taka różnica wystąpi:

W dużym komputerze PC dostępna jest zwykle funkcja atoll(), która łańcuch w liczbę typu long long int (64-bity, zakres -9223372036854775808...9223372036854775807).

ATOF

Funkcja atof() (ang. ASCII string to floating point number) oblicza wartość dziesiętną liczby rzeczywistej zapisanej w postaci znaków ASCII. Początkowe spacje są pomijane. Sama liczba może być zapisana jako:

Wynik konwersji zwracany jest jako wartość typu double.

atof(adres_łańcucha);
adres_łańcucha wskaźnik pierwszego znaku łańcucha.

Jeśli tekstu nie da się zinterpretować jako liczbę rzeczywistą, funkcja zwraca wartość 0.

/*
 Konwersje znakowo-liczbowe
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 3.11.2016
*/

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

int main()
{
  char s[] = "3.141575";

  setlocale(LC_ALL,"");

  printf("%9.6f\n\n",atof(s));

  return 0;
}

ITOA

Funkcja itoa() (ang. integer to ASCII string) dokonuje konwersji liczby całkowitej na jej reprezentację w wybranym systemie pozycyjnym w postaci ciągu liczb.

itoa(liczba, adres_bufora, podstawa);
liczba liczba, która zostanie poddana konwersji
adres_bufora adres tablicy znakowej, w której zostanie umieszczony ciąg cyfr. Tablica musi posiadać wystarczającą pojemność na wszystkie cyfry oraz znak NUL.
podstawa liczba, która określa podstawę systemu, w którym zostanie zapisana liczba. Zakres podstawy wynosi od 2 do 36.

Funkcja zwraca adres bufora.

/*
 Konwersje znakowo-liczbowe
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 4.11.2016
*/

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

int main()
{
  char buf[50];
  int n, i;

  setlocale(LC_ALL,"");

  printf("liczba = "); scanf("%d", &n);
  printf("\n");
  for(i = 2; i <= 16; i++)
    printf("%d(10) = %s(%d)\n",n,itoa(n,buf,i),i);

  return 0;
}

Uwaga: w środowisku mikrokontrolerów PIC argumenty funkcji itoa() są w innej kolejności:

itoa(adres_bufora, liczba, podstawa);

W środowiskach programowania mikrokontrolerów dostępne są podobne funkcje, których niestety nie ma w CodeBlocks (nie są one standardowe):

char * ltoa (long val, char *s, int radix)
char * utoa (unsigned int val, char *s, int radix)
char * ultoa (unsigned long val, char *s, int radix)
val liczba, która zostanie poddana konwersji
s tablica znakowa, w której zostanie umieszczony ciąg cyfr. Tablica musi posiadać wystarczającą pojemność na wszystkie cyfry oraz znak NUL.
radix liczba, która określa podstawę systemu, w którym zostanie zapisana liczba. Zakres podstawy wynosi od 2 do 36.

Sprawdź to w instrukcji swojego kompilatora.


Na początek:  podrozdziału   strony 

Funkcje znakowe

Dołączając do swojego programu plik nagłówkowy ctype.h uzyskujesz dostęp do wielu przydatnych funkcji znakowych. Poniżej podajemy ich spis wraz z krótkim opisem działania.

Funkcje znakowe są zrealizowane w postaci tzw. makr (tego tematu nie poruszałem w tym krótkim kursie) preprocesora. Makra są rozwijane w odpowiednie rozkazy języka C. Parametrem każdego z makr jest wartość całkowita c, traktowana jako kod znaku ASCII. Wynikiem jest 0, jeśli test wykonywany przez makro daje rezultat negatywny, lub 1, jeśli test daje wynik pozytywny.

isalnum(c) czy c jest literą lub cyfrą: 0...9, a...z lub A...Z ?
isalpha(c) czy c jest literą a...z lub A...Z ?
isascii(c) czy c jest 7-bitowym znakiem ASCII ?
iscntrl(c) czy c jest znakiem sterującym ?
isdigit(c) czy c jest cyfrą dziesiętną ?
islower(c) czy c jest małą literą a...z ?
isprint(c) czy c jest znakiem drukowalnym ?
isgraph(c) czy c jest znakiem drukowalnym oprócz spacji ?
ispunct(c) czy c nie jest literą lub cyfrą, lecz jest znakiem drukowalnym ?
isspace(c) czy c jest znakiem spacji, tabulacji lub nowego wiersza ?
isupper(c) czy c jest dużą literą A...Z ?
isxdigit(c) czy c jest cyfrą szesnastkową: 0...9, a...f lub A...F ?

Uruchom program:

/*
 Znaki
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 4.11.2016
*/

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

int main()
{
 char t[80], *p;
 int a,d,s,o;

  setlocale(LC_ALL,"");

  printf("Wpisz wiersz (do 79 znaków)\n\n");
  gets(t);
  a = d = s = o = 0; // zerujemy liczniki
  p = t;
  while(*p)
  {
    if(isalpha(*p)) a++; // litery
    if(isdigit(*p)) d++; // cyfry
    if(isspace(*p)) s++; // spacje
    if(ispunct(*p)) o++; // pozostałe znaki
    p++;
  }
  printf("\n\nStatystyka:\n\n"
         "Litery: %2d\n"
         "Cyfry : %2d\n"
         "Spacje: %2d\n"
         "Inne : %2d\n\n", a, d, s, o);

  return 0;
}

Kolejne trzy makra zwracają w wyniku przetworzoną wartość argumentu jako znak ASCII:

toascii(c) zwraca kod podstawowy ASCII (0...127) przez wyzerowanie starszych bitów argumentu c
tolower(c) jeśli c jest dużą literą A...Z, to zwraca kod małej litery a...z. Jeśli c nie jest dużą literą, zwraca wartość c bez zmiany.
toupper(c) jeśli c jest małą literą a...z, to zwraca kod dużej litery A...Z. Jeśli c nie jest małą literą, zwraca wartość c bez zmiany.

Funkcje tolower() i touper() nie reagują na polskie litery, których kody nie należą do podstawowego zestawu ASCII.

Uruchom program:

/*
 Konwersje znakowo-liczbowe
 (C)2016 mgr Jerzy Wałaszek
 Data utworzenia: 4.11.2016
*/

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

int main()
{
  char t[80], *p;

  setlocale(LC_ALL,"");

  printf("Wpisz wiersz (do 79 znaków)\n\n");
  gets(t);
  for(p = t; *p; p++) *p = toupper(*p);
    printf("%s\n\n", t);

  return 0;
}

Zapraszam do ostatniego rozdziału.


Na początek:  podrozdziału   strony 

Zespół Przedmiotowy
Chemii-Fizyki-Informatyki

w I Liceum Ogólnokształcącym
im. Kazimierza Brodzińskiego
w Tarnowie
ul. Piłsudskiego 4
©2024 mgr Jerzy Wałaszek

Materiały tylko do użytku dydaktycznego. Ich kopiowanie i powielanie jest dozwolone
pod warunkiem podania źródła oraz niepobierania za to pieniędzy.

Pytania proszę przesyłać na adres email: i-lo@eduinf.waw.pl

Serwis wykorzystuje pliki cookies. Jeśli nie chcesz ich otrzymywać, zablokuj je w swojej przeglądarce.

Informacje dodatkowe.