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

這個做法是將變數宣告成staticstatic變數是放在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空間的問題。

results matching ""

    No results matching ""