函數(shù)之間的通信方式
程序無外乎由變量和函數(shù)組成,前者負責保存和組織數(shù)據(jù),后者負責業(yè)務邏輯,即操作這些數(shù)據(jù)以獲得期望的結果。函數(shù)彼此獨立,但又不孤立,而是需要同其他函數(shù)進行交流,以便協(xié)同工作。因此,理解函數(shù)之間的通信方式顯得尤為重要。
通信方式的種類
所謂函數(shù)的通信方式,就是一個函數(shù)將值傳遞給另一個函數(shù)的渠道。有三種:
-
函數(shù)的返回值
:調用函數(shù)可以直接獲得被調用函數(shù)的返回值。 -
函數(shù)的參數(shù)
:以引用傳遞的方式將一個指針傳遞給被調用函數(shù),后者將需要返回給調用函數(shù)的值放入其中。 -
全局變量
:任意函數(shù)都可以訪問,多用在兩個函數(shù)之間需要進行通信,但又不直接調用對方的情況下。
本文重點講解前兩項。
getint函數(shù)
K&R中有一個很好的例子,說明了返回值和參數(shù)這兩個通信方式(以下簡稱通道)之間的的區(qū)別和聯(lián)系。(K&R Section5.2 P95)
需要設計一個名為getint()的函數(shù),作用是從標準輸入流中讀取一個數(shù)字字符串(整數(shù)),然后將其轉化為相應整數(shù)數(shù)值。
從程序設計的角度來講,很明顯這個函數(shù)在工作期間會遇到若干種情況,而且需要向它的調用方說明這些情況,這些情況有:
1. 成功轉換字符串,得到一個需要被返回的數(shù)值。
2. 轉換失敗,因為讀取到的字符不是數(shù)字。
3. 轉換失敗,因為讀取到了EOF。
問題是,以上情況并不互相排斥,而是有可能同時出現(xiàn)。
例如,對于"12345qwer"這樣一個字符串,"12345"的部分可以被成功讀取;而"qwer"部分會因為不是數(shù)字而轉換失敗。這是函數(shù)應該返回"12345"表示的數(shù)值,并且告訴調用方猶豫后續(xù)字符不是數(shù)字,無法繼續(xù)。
又例如,對于"-1"這樣一個字符串來說,它后面如果緊跟著EOF,那么函數(shù)就需要在返回-1的同時,還需要告訴調用方它遇到了EOF而終止。EOF是-1,恰好同字符串所代表的數(shù)值相同,如果利用返回值進行傳遞,肯定會造成誤解。
很明顯,如果所有的可能情況都通過返回值進行傳遞,是不可能的。這是就要同時利用返回值和參數(shù)進行傳遞:
返回值:負責說明轉換是否成功,以及錯誤的原因(錯誤原因互相排斥)。
參數(shù):在轉換成功的情況下,負責將轉換后的數(shù)值傳遞出去。
上面的分析可以概括為:
- 轉換成功(返回一個正數(shù)):
- 轉換結果(利用參數(shù)傳遞出去)
- 由于讀取到EOF而失敗(返回-1):
- 由于讀取到非數(shù)字字符失敗(返回0):
詳細代碼如下
#include <stdio.h>
#include <ctype.h>
/**
* 從標準輸入流中讀取一個整數(shù)數(shù)字字符串,并將其轉換為對應的整數(shù)數(shù)值
*
* @param pn int型指針,用于將轉換所得的整數(shù)數(shù)值傳遞出去
*
* @return 如果輸入的字符串含有整數(shù),返回一個正整數(shù)(取決于數(shù)字后面的第一個字符,如有);如果不含有合法數(shù)字,返回0;如果遇到EOF,返回-1
*/
int getint(int *pn);
int bgetchcar(void); // 等同于getc
void bungetchar(int n); // 等同于ungetc
int main(int argc, const char * argv[]) {
int x = 0;
int r = getint(&x);
printf("r:%d----x:%d\n", r, x);
return 0;
}
int getint(int *pn)
{
int c, sign;
while (isspace(c = bgetchcar())) { // 跳過空格
;
}
if (!isdigit(c) && c != EOF && c != '+' && c != '-') {
bungetchar(c); // 不是數(shù)字,退回最近讀到的字符到輸入流中
return 0;
}
sign = (c == '-') ? -1 : 1; // 嘗試記錄符號
if (c == '+' || c == '-') { // 如果當前讀到的是符號,則繼續(xù)讀取下一個字符
c = bgetchcar();
}
for (*pn = 0; isdigit(c); c = bgetchcar()) { // 開始逐個讀取數(shù)字,直到讀取到一個不是數(shù)字的字符為止
*pn = (*pn) * 10 + (c - '0'); // *10負責提升位數(shù),c - '0'得出每一位的數(shù)字
}
*pn *= sign; // 糾正數(shù)值的正負
if (c != EOF) { // 如果最后讀取到的字符不是EOF
bungetchar(c); // 則將這個字符退回到輸入流中
}
return c;
}