Return a string
函數inet_ntoa()
的回傳值是一個字串,可是可能有人會想問說,傳回一個陣列不是會發生錯誤嗎?
這邊先來講C語言的記憶體配置方式:
Text |
Data |
BSS |
Heap ↓ |
↑ Stack |
- Text:程式碼(編譯後的機器碼)放置區。
- Data:全域變數,以及區塊(Block)內的static變數。
- BSS:未初始化的全域變數。
- Heap:
malloc()
、calloc()
和realloc()
配置的記憶體。 - Stack:區域變數、函數返回點、參數...等。
一般我們宣告的變數都是放置在Stack
裡,所以當函數返回時就會清空在那區塊(Block)內宣告的一般變數。
來看例子:
//bad
char *function() {
char s[] = "hello";
return s;
}//end function
這樣的做法錯誤,因為變數s
是放在Stack
裡,當函數返回的時候變數s
就會消失。
一般做法都會使用malloc()
方式來配置一個合法的記憶體。
char *function() {
char *s = malloc(256 * sizeof(char));
memset(s, 0, 256);
snprintf(s, 256, "Hello");
return s;
}//end function
但是這個做法有個缺點,就是傳回來的指標不需要時,需要free()
掉,否則會記憶體外洩(Memory leak)
,但常常會忘記。
那麼來講inet_ntoa()
的做法。
char *function() {
static char s[] = "hello";
return s;
}//end function
這個做法是將變數宣告成static
,static
變數是放在Data
區(等同於全域變數),所以當函數返回時並不會消失,但是有個缺點。
範例
#include <stdio.h>
#include <string.h>
//ok, but not good
char *function(char *a) {
static char buffer[256];
memset(buffer, 0, 256);
snprintf(buffer, 256, "%s", a);
return buffer;
}//end function
int main() {
char *x, *y;
x = function("123");
printf("x = %s\n", x);
y = function("abc");
printf("y = %s\n", y);
printf("\n");
printf("x = %s\n", x);
printf("y = %s\n", y);
return 0;
}
輸出結果:
~ % ./example
x = 123
y = abc
x = abc
y = abc
因為static
變數在程式執行的時候,就會配置一個記憶體位置給它直到程式結束為止(因為他被放在Data
區),每次函數傳回的指標都指向同一塊記憶體。所以每次呼叫函數後必須馬上複製結果到另一個陣列裡,但是這樣非常麻煩,那麼改使用兩層陣列來當作buffer。
修改成:
#define FUNCTION_BUFFER 12
char *function(char *a) {
static char buffer[FUNCTION_BUFFER][256];
static int which = -1;
which = (which + 1 == FUNCTION_BUFFER ? 0 : which + 1);
memset(buffer[which], 0, 256);
snprintf(buffer[which], 256, "%s", a);
return buffer[which];
}//end function
這樣就有12個static buffer空間,所以並不用馬上複製結果到另一個陣列,但是呼叫12次後又會回來到第一個空間,所以最後還是要複製,但是這樣做法解決了malloc()
後需要free()
以及一個static空間的問題。