Historia pytania
Operator return pojawił się w C w celu jawnego zakończenia pracy funkcji i przekazania wyniku do kodu wywołującego. We wczesnych językach programowania nie zawsze istniała możliwość zwracania wartości, a mechanizm return pozwolił na wyraźne wskazanie wyniku obliczeń. To zwiększyło wyrazistość i bezpieczeństwo programów.
Problem
Główne zadanie: poprawne zakończenie funkcji i, jeśli to konieczne, zwrócenie wartości odpowiadającej określonemu typowi. Błędy często pojawiają się z powodu zwracania wartości niewłaściwego typu, wskaźników na nieistniejące lub lokalne zmienne, lub ignorowania zwracanej wartości przez wywołującą stronę.
Rozwiązanie
Przykład kodu:
#include <stdio.h> struct Point { int x, y; }; struct Point make_point(int x, int y) { // zwracamy strukturę (kopię) struct Point p = {x, y}; return p; } int* dangerous() { int num = 42; return # // niebezpieczne: zwracamy adres zmiennej lokalnej! } void do_nothing() { return; // poprawnie dla funkcji typu void } int main() { struct Point p = make_point(3, 4); printf("%d %d\n", p.x, p.y); int* ptr = dangerous(); // UB: ptr wskazuje na zniszczoną przestrzeń }
Kluczowe cechy:
Czy można używać return w funkcjach bez wartości (void)?
Odpowiedź: Tak, można pisać "return;" dla funkcji void, ale nie można podawać wyrażenia (return x;) dla funkcji void.
Co się dzieje przy zwracaniu tablicy z funkcji?
Odpowiedź: W C nie można bezpośrednio zwrócić tablicy. Można zwrócić tylko wskaźnik (na przykład, na tablicę statyczną), ale częściej należy zwracać wskaźnik i rozmiar lub użyć dynamicznie przydzielonej tablicy.
int* make_arr() { static int arr[5] = {1,2,3,4,5}; return arr; // tablica statyczna żyje po wyjściu z funkcji }
Dlaczego niebezpiecznie jest zwracać wskaźnik na zmienną lokalną?
Odpowiedź: Po wyjściu z funkcji pamięć pod lokalną zmienną jest zwalniana (obszar stosu). Użycie zwróconego wskaźnika prowadzi do niezdefiniowanego zachowania.
Negatywny przypadek
Funkcja zwraca wskaźnik na zmienną lokalną, wywołujący otrzymuje "śmieci", nieprzewidywalne zachowanie i rzadkie błędy wyścigu.
Zalety:
Wady:
Pozytywny przypadek
Użycie zwracanej struktury (kopiowanej przez wartość) lub zwrócenie wskaźnika na pamięć statyczną/dynamiczną:
Zalety:
Wady: