?? 第1章 c語言.txt
字號:
1.7 怎樣才能知道循環是否提前結束了?
循環通常依賴于一個或多個變量,你可以在循環外檢查這些變量,以確保循環被正確執行。請看下例:
int x
char * cp[REQUESTED_BLOCKS]
/ * Attempt (in vain, I must add... )to
allocate 512 10KB blocks in memory. * /
for (x = 0; x<REQUESTED_ BLOCKS ; x++ )
{
cpi[x]= (char * ) malloc (10000,1)
if (cp[x]= = (char * ) NULL)
break
}
/ * If x is less than REQUESTED-BLOCKS,
the loop has ended prematurely. * /
if (x<REQUESTED_BLOCKS)
printf ("Bummer ! My loop ended prematurely ! \n" );
注意,如果上述循環執行成功,它一定會循環512次。緊接著循環的if語句用來測試循環次數,從而判斷循環是否提前結束。如果變量x的值小于512,就說明循環出錯了。
1.8 goto,longjmp()和setjmp()之間有什么區別?
goto語句實現程序執行中的近程跳轉(local jump),longjmp()和setjmp()函數實現程序執行中的遠程跳轉(nonlocaljump,也叫farjump)。通常你應該避免任何形式的執行中跳轉,因為在程序中使用goto語句或longjmp()函數不是一種好的編程習慣。
goto語句會跳過程序中的一段代碼并轉到一個預先指定的位置。為了使用goto語句,你要預先指定一個有標號的位置作為跳轉位置,這個位置必須與goto語句在同一個函數內。在不同的函數之間是無法實現goto跳轉的。下面是一個使用goto語句的例子:
void bad_programmers_function(void)
{
int x
printf("Excuse me while I count to 5000... \n") ;
x----l~
while (1)
{
printf(" %d\n", x)
if (x ==5000)
goto all_done
else
x=x+1;
}
all_done:
prinft("Whew! That wasn't so bad, was it?\n");
}
如果不使用goto語句,是例可以編寫得更好。下面就是一個改進了實現的例子:
void better_function (void)
{
int x
printf("Excuse me while I count to 5000... \n");
for (x=1; x<=5000, x++)
printf(" %d\n", x)
printf("Whew! That wasn't so bad, was it?\n") ;
}
前面已經提到,longjmp()和setjmp()函數實現程序執行中的遠程跳轉。當你在程序中調用setjmp()時,程序當前狀態將被保存到一個jmp_buf類型的結構中。此后,你可以通過調用longjmp()函數恢復到調用setjmp()時的程序狀態。與goto語句不同,longjmp()和setjmp()函數實現的跳轉不一定在同一個函數內。然而,使用這兩個函數有一個很大的缺陷,當程序恢復到它原來所保存的狀態時,它將失去對所有在longjmp()和setjmp()之間動態分配的內存的控制,也就是說這將浪費所有在longjmp()和setjmp()之間用malloc()和calloc()分配所得的內存,從而使程序的效率大大降低。因此,你應該盡量避免使用longjmp()和setjmp()函數,它們和goto語句一樣,都是不良編程習慣的表現。
下面是使用longjmp()函數和setjmp()函數的一個例子:
#include <stdio.h>
#include <setjmp.h>
jmp_buf saved_state;
void main(void);
void call_ longjmp (void);
void main(void)
{
int ret_code;
printf("The current state of the program is being saved... \n");
ret_code = setjmp (saved_state)
if (ret_code ==1)
{
printf("The longjmp function has been called. \n" )
printf("The program's previous state has been restored. \n");
exit(0)
}
printf("I am about to call longjmp and\n");
printf('return to the previous program state... \n" )
call_ longjmp ( )
}
void call_longjmp (void)
{
longjmp (saved_state, 1 )
}
1.9 什么是左值(lvaule)?
左值是指可以被賦值的表達式。左值位于賦值語句的左側,與其相對的右值(rvaule,見 1.11)則位于賦值語句的右側。每條賦值語句都必須有一個左值和一個右值。左值必須是內存中一個可存儲的變量,而不能是一個常量。下面給出了一些左值的例子:
int x;
int *p_int;
x=1;
p_int=5;
變量x是一個整數,它對應于內存中的一個可存儲位置,因此,在語句“x=1”中,x就是一個左值。注意,在第二個賦值語句“*p_int=5"中,通過“*”修飾符訪問p_int所指向的內存區域;因此,p_int是一個左值。相反,下面的幾個例子就不是左值:
#define CONST_VAL 10
int x
/* example 1 * /
l=x;
/ * example 2 * /
CONST_VAL = 5;
在上述兩條語句中,語句的左側都是一個常量,其值不能改變,因為常量不表示內存中可
存儲的位置。因此,這兩條賦值語句中沒有左值,編譯程序會指出它們是錯誤的。
請參見:
1. 10 數組(array)可以是左值嗎? .
1. 11 什么是右值(rvaule)?
1.10 數組(array)可以是左值嗎?
在1.9中,左值被定義為可被賦值的表達式。那么,數組是可被賦值的表達式嗎?不是,因為數組是由若干獨立的數組元素組成的,這些元素不能作為一個整體被賦值。下述語句是非法的:
int x[5],y[5];
x=y;
不過,你可以通過for循環來遍歷數組中的每個元素,并分別對它們賦值,例如:
int i;
int x[5];
int y[5];
......
for(i=0; i<5,i++)
x[i]=y[i];
......
此外,你可能想一次拷貝整個數組,這可以通過象memcpy()這樣的函數來實現,例如:
memcpy(x,y,sizeof(y));
與數組不同,結構(structure)可以作為左值。你可以把一個結構變量賦給另一個同類型的結構變量,例如:
typedef struct t_name
{
charlast_name[25];
char first_name[15];
char middle-init [2];
} NAME
...
NAME my_name, your_name;
...
your_name = my_name;
...
在上例中,結構變量my_name的全部內容被拷貝到結構變量your_name中,其作用和下述語句是相同的:
memcpy(your_name,my_name,sizeof(your_name);
請參見:
1.9 什么是左值(lvaule)?
1.11 什么是右值(rvaule)?
1.11 什么是右值(rvaule)?
在1.9中,左值被定義為可被賦值的表達式,你也可以認為左值是出現在賦值語句左邊的表達式。這樣,右值就可以被定義為能賦值的表達式,它出現在賦值語句的右邊。與左值不同,右值可以是常量或表達式:例如:
int X,y;
x = 1; /* 1 iS an rvalue, x is an lvalue */
y=(x+1); /* (x+1)is an rvalue;y is an lvalue */
在1.9中已經介紹過,一條賦值語句必須有一個左值和一個右值,因此,下述語句無法通過編譯,因為它缺少一個右值:
int x;
x=void_function_call(); /* the{unction void—function—call()
returns nothing */
如果上例中的函數返回一個整數,那么它可以被看作一個右值,因為它的返回值可以存儲
到左值x中。
請參見:
1.9 什么是左值(lvaule)?
1.10 數組可以是左值嗎?
1.12 運算符的優先級總能保證是“自左至右”或“自右至左”的順序嗎?
對這個問題的簡單回答是:這兩種順序都無法保證。C語言并不總是自左至右或自右至左求值,一般說來,它首先求函數值,其次求復雜表達式的值,最后求簡單表達式的值。此外,為了進一步優化代碼,目前流行的大多數C編譯程序常常會改變表達式的求值順序。因此,你應該用括號明確地指定運算符的優先級。例如,請看下述表達式:
a=b+c/d/function—call() * 5
上述表達式的求值順序非常模糊,你很可能得不到所要的結果,因此,你最好明確地指定運算符的優先級:
a=b+(((c/d)/function—call())* 5)
這樣,就能確保表達式被正確求值,而且編譯程序不會為了優化代碼而重新安排運算符的優先級了。
1.13 ++var和var++有什么區別?
“++”運算符被稱為自增運算符。如果“++”運算符出現在變量的前面(++var),那么在表達式使用變量之前,變量的值將增加1。如果“++”運算符出現在變量之后(var++),那么先對表達式求值,然后變量的值才增加1。對自減運算符(--)來說,情況完全相同。如果運算符出現在變量的前面,則相應的運算被稱為前綴運算;反之,則稱為后綴運算。
例如,請看一個使用后綴自增運算符的例子:
int x, y;
x=1;
y=(x++* 5);
上例使用了后綴自增運算符,在求得表達式的值之后,x的值才增加1,因此,y的值為1乘以5,等于5。在求得表達式的值之后,x自增為2。
現在看一個使用前綴自增運算符的例子:
int x, y;
x=1;
y=(++x*5);
這個例子和前一個相同,只不過使用了前綴自增運算符,而不是后綴自增運算符,因此,x的值先增加1,變為2,然后才求得表達式的值。這樣,y的值為2乘以5,等于10。
1.14 取模運算符(modulus operator)“%”的作用是什么?
取模運算符“%”的作用是求兩個數相除的余數。例如,請看下面這段代碼:
x=15/7;
如果x是一個整數,x的值將為2。然而,如果用取模運算符代替除法運算符"/",得到的結果就不同了:
X=15%7;
這個表達式的結果為15除以7的余數,等于1。這就是說,15除以7得2余1。
取模運算符通常用來判斷一個數是否被另一個數整除。例如,如果你要打印字母表中序號為3的倍數的字母,你可以使用下面這段代碼:
int x;
for(x=1; x<=26; x++)
if((x%3)==0)
printf("%c"; x+64);
上例將輸出字符串"cfilorux",即字母表中序號為3的倍數的所有字母。
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -