在 C 中,goto 語句是不能跨越函數的,而執(zhí)行這類跳轉功能的是 setjmp 和 longjmp 宏
。這兩個宏對于處理發(fā)生在深層嵌套函數調用中的出錯情況是非常有用的。
此即為:非局部跳轉。非局部指的是,這不是由普通 C 語言 goto 語句在一個函數內實施的跳轉,而是在棧上跳過若干調用幀,返回到當前函數調用路徑的某個函數中。
#include <setjmp.h>
int setjmp (jmp_buf env) ; /*設置調轉點*/
void longjmp (jmp_buf env, int val) ; /*跳轉*/
setjmp 參數 env 的類型是一個特殊類型 jmp_buf。這一數據類型是某種形式的數組,其中存放 在調用 longjmp 時能用來恢復棧狀態(tài)的所有信息。因為需在另一個函數中引用 env 變量,所以應該將 env 變量定義為全局變量。
longjmp 參數 val,它將成為從 setjmp 處返回的值。
#include <stdio.h>
#include <setjmp.h>
static jmp_buf buf;
void second(void)
{
printf("second\n");
longjmp(buf,1);
// 跳回setjmp的調用處使得setjmp返回值為1
}
void first(void)
{
second();
printf("first\n");
// 不可能執(zhí)行到此行
}
int main()
{
if (!setjmp(buf))
{
// 進入此行前,setjmp返回0
first();
}
else
{
// 當longjmp跳轉回,setjmp返回1,因此進入此行
printf("main\n");
}
return 0;
}
直接調用 setjmp 時,返回值為 0,這一般用于初始化(設置跳轉點時)。以后再調用 longjmp 宏時用 env 變量進行跳轉。程序會自動跳轉到 setjmp 宏的返回語句處,此時 setjmp 的返回值為非 0,由 longjmp 的第二個參數指定。
一般地,宏 setjmp 和 longjmp 是成對使用的,這樣程序流程可以從一個深層嵌套的函數中返回。
\ 頭文件定義了一些宏,當函數參數未知時去獲取函數的參數變量:typedef va_list
宏:
va_start()
va_arg()
va_end()
va_list 類型通過 stdarg 宏定義來訪問一個函數的參數表,參數列表的末尾會用省略號省略
( va_list 用來保存 va_start , va_end 所需信息的一種類型。為了訪問變長參數列表中的參數,必須聲明 va_list 類型的一個對象 )
我們通過初始化( va_start )類型為 va_list 的參數表指針,并通過 va_arg 來獲取下一個參數
。
//求任意個整數的最大值
#include <stdio.h>
#include <stdarg.h>
int maxint(int n, ...) /* 參數數量由非變長參數n直接指定 */
{
va_list ap;
int i, arg, max;
va_start(ap, n); /* ap為參數指針,首先將其初始化為最后一個具名參數, 以便va_arg獲取下一個省略號內參數 */
for (i = 0; i < n; i++) {
arg = va_arg(ap, int); /* 類型固定為int, 按照給定類型返回下一個參數 */
if (i == 0)
max = arg;
else {
if (arg > max)
max = arg;
}
}
va_end(ap);
return max;
}
void main()
{
printf("max = %d\n", maxint(5, 2, 6, 8, 11, 7));
}
歷史上,C語言只支持在編譯時就能確定大小的數組。程序員需要變長數組時,不得不用malloc或calloc這樣的函數為這些數組分配存儲空間,且涉及到多維數組時,不得不顯示地編碼,用行優(yōu)先索引將多維數組映射到一維的數組。
ISO C99引入了一種能力,允許數組的維度是表達式,在數組被分配的時候才計算出來。
#include <stdio.h>
int main(void)
{
int n, i ;
scanf("%d", &n) ;
int array[n] ;
for (; i<n; i++)
{
array[i] = i ;
}
for (i=0; i<n; i++)
{
printf("%d,", array[i]) ;
}
return 0;
}
注意:
如果你需要有著變長大小的臨時存儲,并且其生命周期在變量內部時,可考慮VLA(Variable Length Array,變長數組)。但這有個限制:每個函數的空間不能超過數百字節(jié)。因為 C99 指出邊長數組能自動存儲,它們像其他自動變量一樣受限于同一作用域。即便標準未明確規(guī)定,VLA 的實現(xiàn)都是把內存數據放到棧中。VLA 的最大長度為 SIZE_MAX 字節(jié)。考慮到目標平臺的棧大小,我們必須更加謹慎小心,以保證程序不會面臨棧溢出、下個內存段的數據損壞的尷尬局面。
#include <stdio.h>
int main(void)
{
int i=0;
scanf("%d", &i) ;
switch(i)
{
case 1 ... 9: putchar("0123456789"[i]);
case 'A' ... 'Z': //do something
}
return 0;
}
switch (a)
{
case 1: ;
// ...
if (b==2)
{
case 2:;
// ...
}
else case 3:
{
// ...
for (b=0;b<10;b++)
{
case 5: ;
// ...
}
}
break;
case 4:
}
在C99之前,你只能按順序初始化一個結構體。在C99中你可以這樣做:
struct Foo {
int x;
int y;
int z;
};
Foo foo = {.z = 3, .x = 5};
這段代碼首先初始化了foo.z,然后初始化了foo.x. foo.y 沒有被初始化,所以被置為0。
這一語法同樣可以被用在數組中。以下三行代碼是等價的:
int a[5] = {[1] = 2, [4] = 5};
int a[] = {[1] = 2, [4] = 5};
int a[5] = {0, 2, 0, 0, 5};
關鍵字 restrict 僅對指針有用,修飾指針,表明要修改這個指針所指向的數據區(qū)的內容,僅能通過該指針來實現(xiàn),此關鍵字的作用是使編譯器優(yōu)化代碼,生成更高效的匯編代碼。
int foo (int* x, int* y)
{
*x = 0;
*y = 1;
return *x;
}
很顯然函數foo()的返回值是0,除非參數x和y的值相同??梢韵胂?,99%的情況下該函數都會返回0而不是1。然而編譯起必須保證生成100%正確的代碼,因此,編譯器不能將原有代碼替換成下面的更優(yōu)版本:
int f (int* x, int* y)
{
*x = 0;
*y = 1;
return 0;
}
現(xiàn)在我們有了 restrict 這個關鍵字,就可以利用它來幫助編譯器安全的進行代碼優(yōu)化了,由于指針 x 是修改 x的唯一途徑,編譯起可以確認 “ y=1; ”這行代碼不會修改 * x的內容,因此可以安全的優(yōu)化。
int f (int *restrict x, int *restrict y)
{
*x = 0;
*y = 1;
return 0;
}
很多C的庫函數中用restrict關鍵字:
void memcpy( void restrict dest ,const void restrict src,sizi_t n)
這是一個很有用的內存復制函數,由于兩個參數都加了 restrict 限定,所以兩塊區(qū)域不能重疊,即 dest 指針所指的區(qū)域,不能讓別的指針來修改,即 src 的指針不能修改. 相對應的別一個函數 memmove(void dest,const void * src,size_t)則可以重疊。
void f(int a[static 10]) {
/* ... */
}
你向編譯器保證,你傳遞給 f 的指針指向一個具有至少10個 int 類型元素的數組的首個元素。我猜這也是為了優(yōu)化;例如,編譯器將會假定 a 非空。編譯器還會在你嘗試要將一個可以被靜態(tài)確定為 null 的指針傳入或是一個數組太小的時候發(fā)出警告。
void f(int a[const]) {
/* ... */
}
你不能修改指針 a.,這和說明符 int * const a.作用是一樣的。然而,當你結合上一段中提到的 static 使用,比如在int a[static const 10] 中,你可以獲得一些使用指針風格無法得到的東西。
int x = 'ABCD' ;
這會把 x 的值設置為 0×41424344(或者0×44434241,取決于大小端)我們一般的小端機上,低位存在低字節(jié)處,DCBA 依次從低字節(jié)到高字節(jié)排列。
這只是一種看起來比較炫酷的寫法,一般沒什么用。
更多建議: