Blok to lista instrukcji do wykonania. Przykłady bloków pojawiających się w C znajdują się po instrukcji while i w instrukcjach if
while( boolean expression)
statement OR block
if (boolean expression)
statement OR block
C pozwala również na zagnieżdżanie bloku w bloku. Mogę użyć tego do ponownego użycia nazw zmiennych, przypuśćmy, że naprawdę lubię „x”
int x = 0;
while (x < 10)
{
{
int x = 5;
printf("%d",x)
}
x = x+1;
}
wypisze cyfrę 5 dziesięć razy. Chyba widziałem sytuacje, w których pożądane jest utrzymywanie niskiej liczby nazw zmiennych. Być może w makropoleceniach. Nie widzę jednak żadnego trudnego powodu, aby potrzebować tej funkcji. Czy ktoś może mi pomóc w zrozumieniu zastosowań tej funkcji, podając niektóre idiomy tam, gdzie jest ona używana.
Odpowiedzi:
Pomysł nie polega na utrzymywaniu niskiej liczby nazw zmiennych lub zachęcaniu do ich ponownego użycia, ale raczej na ograniczaniu zakresu zmiennych. Jeśli masz:
wówczas zakres
y
jest ograniczony do bloku, co oznacza, że możesz o nim zapomnieć przed lub po bloku. Widzisz to najczęściej używane w połączeniu z pętlami i warunkami warunkowymi. Widuje się go również częściej w językach podobnych do C, takich jak C ++, gdzie poza zakresem zmienna powoduje jej zniszczenie.źródło
Ponieważ w dawnych czasach C nowe zmienne mogły być deklarowane tylko w nowym bloku.
W ten sposób programiści mogą wprowadzać nowe zmienne w środku funkcji bez wycieku i minimalizacji zużycia stosu.
W dzisiejszych optymalizatorach jest to bezużyteczne i znak, że należy rozważyć wyodrębnienie bloku we własnej funkcji.
W instrukcji switch użyteczne jest zamknięcie skrzynek we własnych blokach, aby uniknąć podwójnej deklaracji.
W C ++ jest to bardzo przydatne na przykład dla blokad blokujących RAII i zapewniania, że destruktory uruchamiają blokadę zwolnienia, gdy wykonanie wykracza poza zakres i nadal wykonuje inne czynności poza sekcją krytyczną.
źródło
Nie patrzyłbym na to jak na „arbitralne” bloki. Nie jest to funkcja przeznaczona specjalnie dla programistów, ale sposób, w jaki C używa bloków, pozwala na stosowanie tej samej konstrukcji bloku w wielu miejscach o tej samej semantyce. Blok (w C) to nowy zakres, a zmienne, które go opuszczają, są eliminowane. Jest to jednolite bez względu na sposób użycia bloku.
W innych językach tak nie jest. Ma to tę zaletę, że pozwala ograniczyć liczbę nadużyć, jak pokazano, ale wadą jest to, że bloki zachowują się inaczej w zależności od kontekstu, w jakim się znajdują.
Rzadko widziałem samodzielne bloki używane w C lub C ++ - często, gdy istnieje duża struktura lub obiekt reprezentujący połączenie lub coś, co chcesz wymusić zniszczenie. Zwykle jest to wskazówka, że twoja funkcja robi zbyt wiele rzeczy i / lub jest za długa.
źródło
Musisz zdać sobie sprawę, że zasady programowania, które wydają się teraz oczywiste, nie zawsze były takie. C najlepsze praktyki w dużym stopniu zależą od wieku twoich przykładów. Kiedy po raz pierwszy wprowadzono C, rozbicie kodu na małe funkcje było uważane za zbyt nieefektywne. Dennis Ritchie po prostu skłamał i powiedział, że wywołania funkcji były naprawdę wydajne w C (nie były w tym czasie), co sprawiło, że ludzie zaczęli ich częściej używać, chociaż programiści C jakoś nigdy tak naprawdę nie przeszli kultury przedwczesnej optymalizacji.
Jest to dobra praktyka programowania nawet dzisiaj, aby ograniczyć zakres zmiennych jak najmniejsza. W dzisiejszych czasach zwykle robimy to, tworząc nową funkcję, ale jeśli funkcje są uważane za drogie, wprowadzenie nowego bloku jest logicznym sposobem ograniczenia zakresu bez narzutu wywołania funkcji.
Jednak zacząłem programować w C ponad 20 lat temu, kiedy musiałeś zadeklarować wszystkie swoje zmienne na początku zakresu i nie przypominam sobie, żeby cieniowanie zmiennych było takie, jak zawsze uważane za dobry styl. Ponowne zgłaszanie w dwóch blokach jeden po drugim, tak jak w instrukcji switch, tak, ale nie w cieniu. Może jeśli zmienna została już używany i nazwa specyficzna była bardzo idiomatyczne dla API ty dzwonisz, jak
dest
isrc
wstrcpy
, na przykład.źródło
Arbitralne bloki są przydatne do wprowadzania zmiennych pośrednich, które są używane tylko w szczególnych przypadkach obliczeń.
Jest to powszechny wzorzec w obliczeniach naukowych, w których procedury numeryczne zwykle:
Ze względu na drugi punkt przydatne jest wprowadzenie zmiennych tymczasowych o ograniczonym zasięgu, które osiąga się dodatkowo, stosując dowolny blok lub wprowadzając funkcję pomocniczą.
Chociaż wprowadzenie funkcji pomocniczej może wydawać się bezmyślnością lub najlepszą praktyką, którą należy ślepo stosować, w rzeczywistości w tej konkretnej sytuacji jest niewiele korzyści .
Ponieważ istnieje wiele parametrów i wielkości pośrednich, chcemy wprowadzić strukturę, która przekaże je do funkcji pomocniczej.
Ponieważ jednak chcemy konsekwentnie stosować się do naszych praktyk, nie wprowadzimy tylko jednej funkcji pomocniczej, ale kilka. Tak więc, najpierw wprowadzamy struktury ad-hoc przenoszące parametry dla każdej funkcji, które wprowadzają wiele narzutu kodu do przenoszenia parametrów tam iz powrotem, lub wprowadzamy jeden, który będzie rządził wszystkimi strukturami arkusza, który zawiera wszystkie nasze zmienne, ale wygląda jak pakiet bitów bez konsekwencji, w którym tylko połowa parametrów ma interesujące znaczenie.
Dlatego te struktury pomocnicze są zwykle uciążliwe, a ich użycie oznacza wybranie między rozdęciem kodu lub wprowadzeniem abstrakcji, której zakres jest zbyt szeroki i osłabia znaczenie programu, zamiast go rozszerzać .
Wprowadzenie funkcji pomocniczych mogłoby ułatwić testowanie jednostkowe programu poprzez wprowadzenie drobniejszego testu szczegółowości, ale łączenie testów jednostkowych nie dla procedur niskopoziomowych i testów regresyjnych w formie porównań (z cyfrą liczbową) śladów numerycznych procedur wykonuje równie dobrą robotę .
źródło
Ogólnie, ponowne użycie nazw zmiennych w ten sposób powoduje zbyt duże zamieszanie wśród przyszłych czytelników twojego kodu. Lepiej po prostu nazwać zmienną wewnętrzną czymś innym. W rzeczywistości język C # nie zezwala na używanie zmiennych w ten sposób.
Widziałem, w jaki sposób użycie zagnieżdżonych bloków w rozwinięciach makr byłoby przydatne w zapobieganiu kolizjom nazw zmiennych.
źródło
Służy do określania zakresu rzeczy, które zwykle nie mają zasięgu na tym poziomie. Jest to niezwykle rzadkie i ogólnie refaktoryzacja jest lepszym wyborem, ale natknąłem się na to raz lub dwa razy w przeszłości w instrukcjach switch:
Jeśli rozważasz konserwację, refaktoryzację należy zrównoważyć, mając wszystkie implementacje w rzędzie, o ile każda ma tylko kilka linii. Łatwiej jest mieć je wszystkie razem niż listę nazw funkcji.
W moim przypadku, jeśli dobrze pamiętam, większość przypadków była niewielkimi zmianami tego samego kodu - z wyjątkiem tego, że jeden z nich był identyczny z drugim, tylko z dodatkowym przetwarzaniem wstępnym. Przełącznik okazał się najprostszą formą dla kodu, a dodatkowy zakres pozwolił na użycie dodatkowych zmiennych lokalnych, o które on i inne przypadki nie musieli się martwić (takie jak nazwy zmiennych przypadkowo pokrywające się z nazwami zdefiniowanymi przed przełącznikiem, ale używane później).
Zauważ, że instrukcja switch to po prostu użycie jej, z którą się spotkałem. Jestem pewien, że może mieć zastosowanie również w innych miejscach, ale nie przypominam sobie, aby były skutecznie wykorzystywane w ten sposób.
źródło