W języku C tablice są podstawową strukturą do przechowywania uporządkowanego zestawu elementów tego samego typu. Zapewniają szybki dostęp przez indeks i są ściśle związane z pracą wskaźników. Tablice mogą być deklarowane statycznie, automatycznie (na stosie), lub dynamicznie (w stercie). Typ przydziału wpływa na czas życia tablicy, dostępność z różnych części kodu i wymagania dotyczące zarządzania pamięcią.
Historia pytania
Pierwotny C pozwalał na definiowanie tylko statycznych i automatycznych tablic, ale z pojawieniem się dynamicznego przydzielania pamięci (funkcje malloc, calloc, free) pojawiły się nowe wzorce projektowe, które zwiększyły elastyczność kodu.
Problem
Programiści często popełniają błędy związane z rozmiarami, czasem życia i zwalnianiem tablic, co prowadzi do wycieków, warunków wyścigu i uszkodzeń pamięci.
Rozwiązanie
Staranny wybór typu przechowywania w zależności od zadania, staranne śledzenie inicjalizacji i terminowe zwalnianie pamięci dla dynamicznych tablic.
Przykład kodu:
#include <stdio.h> #include <stdlib.h> int main() { // Automatyczny (na stosie) int auto_arr[5] = {1,2,3,4,5}; // Statyczny (żyje dopóki działa program) static int static_arr[5]; // Dynamiczny (w stercie) int *dyn_arr = malloc(5 * sizeof(int)); for (int i = 0; i < 5; i++) dyn_arr[i] = i * 2; // Użycie for (int i = 0; i < 5; i++) printf("%d ", dyn_arr[i]); printf(" "); free(dyn_arr); return 0; }
Kluczowe cechy:
Czy można poznać rozmiar dynamicznej tablicy za pomocą sizeof?
Nie, sizeof(ptr) dla dynamicznej tablicy zwróci rozmiar wskaźnika, a nie tablicy. Należy ręcznie przechowywać rozmiar lub użyć osobnej zmiennej.
int* arr = malloc(10 * sizeof(int)); printf("%zu\n", sizeof(arr)); // Rozmiar wskaźnika, nie tablicy
Co się stanie przy wyjściu poza granice tablicy?
W języku C nie ma automatycznej kontroli granic tablicy: odwołanie się poza granice prowadzi do undefined behavior. Błędy są wykrywane tylko w czasie wykonywania lub wcale.
Czy można zwrócić lokalną (automatyczną) tablicę z funkcji?
Nie! Tablica zadeklarowana wewnątrz funkcji jest usuwana po jej zakończeniu. Zwrot takiej tablicy prowadzi do odwołania się do już zwolnionej pamięci.
int* create_wrong_array() { int arr[10]; return arr; // Błąd: zwrot wskaźnika na stos }
Programista tworzy tablicę na stosie i zwraca wskaźnik na nią z funkcji. Program czasami się wysypuje lub zwraca śmieci.
Zalety:
Wady:
Użycie dynamicznego przydziału z przekazaniem wymiaru razem z wskaźnikiem, czyszczenie pamięci przez free. Wszystkie przypadki zwalniania pamięci są testowane w testach jednostkowych.
Zalety:
Wady: