C語言-進階探討

不定長度參數

有時候函數不知道會有幾個參數要傳進來,例如printf跟scanf

這時候就需要用到不定長度參數,必須引入stdarg.h標頭檔

不定長度引數使用幾個識別字來建立不定長度引數

  • va_list
    一種特殊的型態,在va_start、va_arg與va_end裡當作參數使用。
  • va_start
    啟始不定長度引數
  • va_arg
    讀取不定長度引數
  • va_end
    終止不定長度引數

要宣告不定長度參數時,要在函數定義時使用’…’,表示要使用不定長度參數,而且告知要傳遞幾個參數

void func(int, ...);

然後在函數裡面使用va_arg取出參數時必須指定用什麼型態取出

va_arg(num_list, double); 

以下為範例程式

#include <stdio.h>
#include <stdarg.h>

void func(int, ...);  

int main(void) {
    double x = 1.1, y = 2.2, z = 3.3; 
    double a = 0.1, b = 0.2, c = 0.3; 

    puts("三個參數:"); 
    func(3, x, y, z); 

    puts("六個參數:"); 
    func(6, x, y, z, a, b, c); 

    return 0;
}

void func(int length, ...) { 

    double tmp; 

    va_list num_list; 

    va_start(num_list, length); 

    int i;
    for(i = 0; i < length; i++) {
        printf("%.2f\n", va_arg(num_list, double)); 
    }

    va_end(num_list); 
}

執行結果

三個參數:
1.10
2.20
3.30
六個參數:
1.10
2.20
3.30
0.10
0.20
0.30

命令列參數

程式在執行過程中,可以接受外在的參數來執行不同功能

例如在Windows底下的指令copy

copy source.txt target.txt

copy其實是一段程式,source.txt跟target.txt則是命令列參數

在之前的舉例中,main函數的括號總是空白的,而要使用命令列參數就要寫

1
2
3
int main(int argc, char *argv[]) { 
....
}

argc是一個整數,紀錄有幾個參數,例如上面的copy程式的argc就是3

每個參數之間都用空白做區隔

而argv就是參數,argv[0]是”copy”,argv[1]是”source.txt”,argv[2]是”target.txt”

而如果你的參數中包含空白,就需要用雙引號””括起來,例如

copy "Hello World.txt" target.txt

這樣argv[0]就是是”copy”,argv[1]是”Hello World.txt”,argv[2]是”target.txt”了

以下舉檔案複製為範例程式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
#include <stdio.h> 

int main(int argc, char* argv[]) {

if(argc != 3) {
puts("指令: copy <來源檔案名稱> <目的檔案名稱>");
return 1;
}

FILE *file1 = fopen(argv[1], "r");
if(!file1) {
puts("來源檔案開啟失敗");
return 1;
}

FILE *file2 = fopen(argv[2], "w");
if(!file2) {
puts("目的檔案開啟失敗");
return 1;
}

char str[50];
while(fgets(str, 50, file1) != NULL) {
fputs(str, file2);
}

fclose(file1);
fclose(file2);

return 0;

}

#inline

呼叫函數的時候,會因為需要分配記憶體空間,而產生一些效率上的負擔

為了提高效率,一些小函數可以建議編譯器像巨集一樣展開

這時候就需要用到inline保留字了

在宣告函數原型的時候加上inline,表示建議編譯器展開

如果編譯器判斷採納的話,才會展開程式碼

而判斷的標準視編譯器而定

以下是範例程式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <stdio.h> 

inline int square(int);

int main(int argc, char* argv[]) {

int num = 5;
printf("%d 的平方為 %d\n",num,square(num));

return 0;

}

inline int square(int num){
return num * num;
}

執行結果

5 的平方為 25

static

當變數宣告的時候加上static時,該變數就會一直存在記憶體中直到程式結束

不管離開區塊還是區域都是一樣,以下是範例程式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <stdio.h>

void count(void);

int main(void) {
int i;
for(i = 0; i < 10; i++) {
count();
}

return 0;
}

void count(void) {
static int c = 1;
printf("%d\n", c);
c++;
}

執行結果

1
2
3
4
5
6
7
8
9
10

static修飾之後表示只有該區域可以看到,例如修飾全域變數或者函數

這樣該變數或該函數只有那個檔案可以使用

使用時機在於此變數或函數不想被其他檔案所使用

或者不同檔案間可以使用相同名字的變數而不衝突

如果修飾在函數裡面,表示只有那個函數可以使用

很適合用來統計函數被呼叫了幾次

參考

  1. 不定長度引數(Variable-length argument)
  2. 命令列引數
  3. 行內函式(Inline function)
  4. 變數、函式可視範圍(static 與 extern)