官术网_书友最值得收藏!

2.4.3 動態分配函數

第二種內存管理模型(由被調用者分配,由調用者釋放)是由ISO/IEC TR 24731-2定義的動態分配函數實現。ISO/IEC TR 24731-2定義了許多標準C字符串處理函數的替代品,這些替代品使用動態分配的內存,以確保不會發生緩沖區溢出。因為使用這樣的函數需要引入隨后的釋放緩沖區的額外調用,所以這些函數更適用于新的開發,而不是改造現有代碼。

在一般情況下,因為在ISO/IEC TR 24731-2中描述的函數,總是自動調整緩沖區大小以容納所需的數據,所以這些函數更好地確保了不會發生緩沖區溢出問題。但是,使用動態內存分配的應用程序,可能會遭受拒絕服務攻擊,因為其中的數據會一直存在,直到內存耗盡。它們也更容易出現動態內存管理錯誤,這也可能導致安全漏洞。

例2.1可以使用動態分配函數實現,如例2.7中所示。

例2.7 使用函數getline()從stdin中讀入數據


01  #define __STDC_WANT_LIB_EXT2__ 1
02  #include <stdio.h>
03  #include <stdlib.h>
04  
05  void get_y_or_n(void) {
06    char *response = NULL;
07    size_t len;
08  
09    puts("Continue? [y] n: ");
10    if ((getline(&response, &len, stdin) < 0) ||
11        (len && response[0] == 'n')) {
12      free(response);
13      exit(0);
14    }
15    free(response);
16  }

此程序對于任何輸入都具有已定義的行為,包括一個假定,即假定一個非常長的、需要耗盡所有可用內存才能容納的行,應被視為一個“no”回應。因為對getline()函數動態地分配response緩沖區,所以程序必須調用free()來釋放已分配的內存。

ISO/IEC TR 24731-2允許在不相應地打開文件的情況下定義流。這種類型的流從內存緩沖區取得輸入或把輸出寫入到內存緩沖區。例如,GNU C庫使用這些流來實現sprintf()和sscanf()函數。

與內存緩沖區相關的流和與外部文件關聯的文本文件流,具有相同的操作。此外,數據流的方向也是用完全相同的方式確定的。

你可以明確地使用fmemopen()、open_memstream()或open_wmemstream()函數創建一個字符串流。這些函數允許你對字符串或內存緩沖區執行I/O操作。fmemopen()和open_memstream()函數在<stdio.h>中被聲明,如下所示。


1  FILE *fmemopen(
2    void * restrict buf, size_t size, const char * restrict mode
3  );
4  FILE *open_memstream(
5    char ** restrict bufp, size_t * restrict sizep
6  );

open_wmemstream()函數是在<wchar.h>中定義的,并具有以下簽名:


FILE *open_wmemstream(wchar_t **bufp, size_t *sizep); 

fmemopen()函數打開一個流,使你可以讀取或寫入指定的緩沖區。open_memstream()函數打開一個面向字節的流來寫入一個緩沖區,而open_wmemstream()函數創建一個面向寬字符的流。當用fclose()關閉流或用fflush()刷新流時,bufp和sizep被更新,以包含緩沖區的指針及其大小。只要沒有進一步的輸出流發生,這些值仍然有效。如果執行額外的輸出,必須再次刷新流來存儲新的值,才能再次使用它們。一個空字符被寫入緩沖區的末尾,但它存儲在sizep中的size值中不包括它。

通過調用fmemopen()、open_memstream()或open_wmemstream()創建的一個與內存緩沖區相關聯的流的輸入和輸出操作,發生在內存緩沖區的范圍內,受限于實現。對于用open_memstream()或open_wmemstream()打開的流的情況,內存區域動態增長,以適應必要的寫操作。對于輸出,在刷新或關閉操作期間,數據從函數setvbuf()提供的緩沖區移動到內存流。如果沒有足夠的內存來增長內存區域,或者操作需要訪問相關內存區域以外的地方,相關的操作失敗。

例2.8中的程序在第6行打開一個流來寫入到內存。

例2.8 打開一個流來寫入內存


01  #include <stdio.h>
02  
03  int main(void) {
04    char *buf;
05    size_t size;
06    FILE *stream;
07  
08    stream = open_memstream(&buf, &size);
09    if (stream == NULL) { /* handle error */ };
10    fprintf(stream, "hello");
11    fflush(stream);
12    printf("buf = '%s', size = %zu\n", buf, size);
13    fprintf(stream, ", world");
14    fclose(stream);
15    printf("buf = '%s', size = %zu\n", buf, size);
16    free(buf);
17    return 0;
18  }

在第10行把字符串“hello”寫入到流,并且在第11行刷新該流。fflush()的調用更新buf和size,以便第12行的printf()函數輸出:


buf = 'hello', size = 5

在第13行把字符串".world"寫入流后,在第14行關閉流。關閉流的同時也更新buf和size,以便第15行的printf()函數輸出:


buf = 'hello, world', size = 12

size是緩沖區的累計(總數)大小。open_memstream()函數提供了一個更安全的寫入內存機制,因為它采用了根據需要動態分配內存的方法。但是,它確實要求調用者來釋放分配的內存,如例子的第16行所示。

在安全關鍵的系統中,往往是不允許動態分配的。例如,MISRA標準要求,“不得使用動態堆內存分配”[MISRA 2005]。一些安全關鍵系統在初始化過程中可以利用動態內存分配,但在操作過程中不允許。例如,航空電子軟件在初始化飛機時可以動態地分配內存,但在飛行過程中不允許。

動態分配函數從廣泛應用的現有實現中取得,許多這類函數都包含在POSIX中。

主站蜘蛛池模板: 紫金县| 延庆县| 宁武县| 平顺县| 广元市| 屏南县| 岳阳县| 高雄市| 铜梁县| 衡水市| 菏泽市| 绥阳县| 六枝特区| 永年县| 阳城县| 田林县| 长沙市| 皮山县| 商都县| 义乌市| 田东县| 江安县| 南投市| 岑巩县| 普格县| 弥勒县| 鸡西市| 长子县| 兴业县| 红河县| 淮滨县| 香港| 册亨县| 翁源县| 桓台县| 类乌齐县| 开平市| 通辽市| 常德市| 凤凰县| 前郭尔|