C Language
Oświadczenia wyboru
Szukaj…
Instrukcje if ()
Jednym z najprostszych sposobów kontrolowania przebiegu programu jest użycie instrukcji if
if. To, czy ma zostać wykonany blok kodu, czy nie, może zostać określone w tej instrukcji.
Składnia instrukcji if
select w C może wyglądać następująco:
if(cond)
{
statement(s); /*to be executed, on condition being true*/
}
Na przykład,
if (a > 1) {
puts("a is larger than 1");
}
Gdzie a > 1
jest warunkiem, który musi być true
, aby wykonać instrukcje wewnątrz bloku if
. W tym przykładzie „a jest większe niż 1” jest drukowane tylko wtedy, gdy a > 1
jest prawdziwe.
if
instrukcje wyboru mogą pominąć nawiasy klamrowe {
i }
jeśli w bloku jest tylko jedna instrukcja. Powyższy przykład można przepisać na
if (a > 1)
puts("a is larger than 1");
Jednak do wykonania wielu instrukcji w bloku muszą być użyte nawiasy klamrowe.
Warunek if
może zawierać wiele wyrażeń. if
wykona akcję tylko wtedy, gdy końcowy wynik wyrażenia jest prawdziwy.
Na przykład
if ((a > 1) && (b > 1)) {
puts("a is larger than 1");
a++;
}
będzie wykonywał tylko printf
i a++
, gdy oba i a
b
jest większa niż 1
.
if () ... instrukcje i składnia else
Podczas gdy if
wykonuje akcję tylko wtedy, gdy jej warunek ma wartość true
, if
/ else
pozwala określić różne akcje, gdy warunek jest true
a warunek jest false
.
Przykład:
if (a > 1)
puts("a is larger than 1");
else
puts("a is not larger than 1");
Podobnie jak w if
instrukcji if
, gdy blok wewnątrz instrukcji if
lub else
składa się tylko z jednej instrukcji, nawiasy klamrowe można pominąć (ale nie jest to zalecane, ponieważ może łatwo wprowadzić problemy bezwiednie). Jeśli jednak w bloku if
lub else
znajduje się więcej niż jedna instrukcja, nawiasy klamrowe muszą być użyte w tym konkretnym bloku.
if (a > 1)
{
puts("a is larger than 1");
a--;
}
else
{
puts("a is not larger than 1");
a++;
}
instrukcje switch ()
instrukcje switch
są przydatne, gdy chcesz, aby Twój program wykonywał wiele różnych czynności w zależności od wartości konkretnej zmiennej testowej.
Przykład użycia instrukcji switch
wygląda następująco:
int a = 1;
switch (a) {
case 1:
puts("a is 1");
break;
case 2:
puts("a is 2");
break;
default:
puts("a is neither 1 nor 2");
break;
}
Ten przykład jest równoważny z
int a = 1;
if (a == 1) {
puts("a is 1");
} else if (a == 2) {
puts("a is 2");
} else {
puts("a is neither 1 nor 2");
}
Jeśli wartość a
wynosi 1, gdy używana jest instrukcja switch
, a is 1
zostanie wydrukowane. Jeśli wartość a
wynosi 2, a is 2
drukowane 2. W przeciwnym razie, a is neither 1 nor 2
zostaną wydrukowane.
case n:
służy do opisania, gdzie wskoczy przepływ wykonania, gdy wartość przekazana do instrukcji switch
to n . n musi być stałą czasową kompilacji i to samo n może istnieć najwyżej raz w jednej instrukcji switch
.
default:
służy do opisania, że gdy wartość nie zgadzała się z żadnym wyborem dla case n:
Dobrą praktyką jest dołączanie default
przypadku do każdej instrukcji switch, aby wychwycić nieoczekiwane zachowanie.
break;
instrukcja jest wymagana, aby wyskoczyć z bloku switch
.
Uwaga: jeśli przypadkowo zapomnisz dodać break
po zakończeniu case
, kompilator przyjmie, że zamierzasz „przewrócić się” i wszystkie kolejne instrukcje spraw zostaną wykonane (chyba że instrukcja break zostanie znaleziona w którykolwiek z kolejnych przypadków), bez względu na to, czy kolejne oświadczenia są zgodne, czy nie. Ta konkretna właściwość służy do implementacji Urządzenia Duffa . Takie zachowanie jest często uważane za wadę w specyfikacji języka C.
Poniżej znajduje się przykład, który pokazuje skutki braku break;
:
int a = 1;
switch (a) {
case 1:
case 2:
puts("a is 1 or 2");
case 3:
puts("a is 1, 2 or 3");
break;
default:
puts("a is neither 1, 2 nor 3");
break;
}
Gdy wartość a
wynosi 1 lub 2, a is 1 or 2
a a is 1, 2 or 3
zostaną wydrukowane oba. Gdy a
wynosi 3, wydrukowane zostaną tylko a is 1, 2 or 3
. W przeciwnym razie, a is neither 1, 2 nor 3
zostaną wydrukowane.
Zauważ, że default
przypadek nie jest konieczny, szczególnie gdy zestaw wartości otrzymanych w switch
jest zakończony i znany w czasie kompilacji.
Najlepszym przykładem jest użycie switch
na enum
.
enum msg_type { ACK, PING, ERROR };
void f(enum msg_type t)
{
switch (t) {
case ACK:
// do nothing
break;
case PING:
// do something
break;
case ERROR:
// do something else
break;
}
}
Istnieje wiele zalet:
- większość kompilatorów zgłosi ostrzeżenie, jeśli nie obsłużysz wartości (nie zostanie to zgłoszone, jeśli wystąpi przypadek
default
) - z tego samego powodu, jeśli dodasz nową wartość do
enum
, zostaniesz powiadomiony o wszystkich miejscach, w których zapomniałeś obsługiwać nową wartość (w przypadkudefault
konieczne byłoby ręczne sprawdzenie kodu w poszukiwaniu takich przypadków) - Czytelnik nie musi odgadnąć „co jest
default:
ukrytedefault:
”, czy istnieją inne wartościenum
lub czy jest to ochrona „na wszelki wypadek”. A jeśli istnieją inne wartościenum
, to czy koder umyślnie zastosował dla nichdefault
wielkość liter, czy też jest błąd, który został wprowadzony, gdy dodał wartość? - obsługa każdej wartości
enum
sprawia, że kod jest oczywisty, ponieważ nie można ukryć się za dziką kartą, należy jawnie obsłużyć każdą z nich.
Niemniej jednak nie można uniemożliwić komuś napisania złego kodu, takiego jak:
enum msg_type t = (enum msg_type)666; // I'm evil
Dlatego możesz dodać dodatkową kontrolę przed przełącznikiem, aby ją wykryć, jeśli naprawdę jej potrzebujesz.
void f(enum msg_type t)
{
if (!is_msg_type_valid(t)) {
// Handle this unlikely error
}
switch(t) {
// Same code than before
}
}
if () ... else Ladder Łączenie dwóch lub więcej instrukcji if () ... else
Podczas gdy instrukcja if ()... else
pozwala zdefiniować tylko jedno (domyślne) zachowanie, które występuje, gdy warunek w if ()
nie jest spełniony, łańcuchowanie dwóch lub więcej instrukcji if () ... else
pozwala zdefiniować parę więcej zachowań przed pójściem do ostatniej else
oddziału działającego jako „default”, jeśli w ogóle.
Przykład:
int a = ... /* initialise to some value. */
if (a >= 1)
{
printf("a is greater than or equals 1.\n");
}
else if (a == 0) //we already know that a is smaller than 1
{
printf("a equals 0.\n");
}
else /* a is smaller than 1 and not equals 0, hence: */
{
printf("a is negative.\n");
}
Zagnieżdżone if () ... else VS if () .. else Drabina
Zagnieżdżone instrukcje if()...else
zajmują więcej czasu (są wolniejsze) w porównaniu do drabiny if()...else
ponieważ zagnieżdżone instrukcje if()...else
sprawdzają wszystkie wewnętrzne instrukcje warunkowe po zewnętrznej warunkowa instrukcja if()
jest spełniona, podczas if()..else
drabina if()..else
zatrzyma testowanie warunku, gdy dowolna z instrukcji warunkowych if()
lub else if()
będzie prawdziwa.
Drabina if()...else
:
#include <stdio.h>
int main(int argc, char *argv[])
{
int a, b, c;
printf("\nEnter Three numbers = ");
scanf("%d%d%d", &a, &b, &c);
if ((a < b) && (a < c))
{
printf("\na = %d is the smallest.", a);
}
else if ((b < a) && (b < c))
{
printf("\nb = %d is the smallest.", b);
}
else if ((c < a) && (c < b))
{
printf("\nc = %d is the smallest.", c);
}
else
{
printf("\nImprove your coding logic");
}
return 0;
}
W ogólnym przypadku jest uważany za lepszy niż równoważny zagnieżdżony, if()...else
:
#include <stdio.h>
int main(int argc, char *argv[])
{
int a, b, c;
printf("\nEnter Three numbers = ");
scanf("%d%d%d", &a, &b, &c);
if (a < b)
{
if (a < c)
{
printf("\na = %d is the smallest.", a);
}
else
{
printf("\nc = %d is the smallest.", c);
}
}
else
{
if(b < c)
{
printf("\nb = %d is the smallest.", b);
}
else
{
printf("\nc = %d is the smallest.", c);
}
}
return 0;
}