簡體   English   中英

如何使用 malloc() 和 memset()

[英]How to use malloc() and memset()

我對 C 非常copy_buffer並試圖實現一個copy_buffer函數來分配一個新緩沖區,將現有緩沖區中的內容復制到新緩沖區中,然后返回新緩沖區。

我正在嘗試使用malloc()memset() ,我知道我需要malloc兩次:一次用於結構,一次用於數據。 我是否也需要memset兩次? 請指出我為copy_buffer函數寫錯的copy_buffer

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// Structure to hold a buffer of binary data
// Note that we expect that it could hold null(0) characters,
// so we can't rely on strlen to tell us the size or strcpy to copy the data
typedef struct BufferStruct {
    char *data;
    int size;
} Buffer;

Buffer *copy_buffer(Buffer *buffer) {
    Buffer *new_buffer = (Buffer*)malloc(sizeof(Buffer));
    memset(new_buffer, 0, sizeof(Buffer));
    new_buffer->data = malloc(sizeof(char));
    memset(new_buffer->data, 0, sizeof(char));
    for (int i = 0; i < buffer->size; ++i) {
        new_buffer->data[i] = buffer->data[i];
    }
    new_buffer->size = buffer->size;
    return new_buffer;
}

Buffer *example1() {
    Buffer *buffer = (Buffer*)malloc(sizeof(Buffer));
    buffer->data = "hello world\nthis is a string";
    buffer->size = strlen(buffer->data);
    return buffer;
}

// Example buffer storing 3 totally different strings in the same buffer (note the '\0')
Buffer *example2() {
    Buffer *buffer = (Buffer*)malloc(sizeof(Buffer));
    buffer->data = "this string has null\0characters\0 in the middle, beware!";
    buffer->size = 55;
    return buffer;
}

//
// Use fopen to create a file for writing
// Then fwrite to write the buffer to a file
//
void write_buffer(const char *filename, Buffer *buffer) {
    FILE *file = fopen(filename, "w");
    fwrite(buffer->data, 1, buffer->size, file);
    fclose(file);
}

int main() {
    Buffer *example = example2();
    Buffer *copied = copy_buffer(example);

    write_buffer("example1.bin", copied);
    return 0;
}

初始化

我是否也需要 memset 兩次?

從技術上講不可以,前提是您在使用它們之前正確初始化Buffer結構的所有元素。

然而,我覺得這是一個危險的習慣。 保持一致非常困難,並且在某些情況下,如果您犯了錯誤,程序可能會崩潰。 許多開發人員似乎建議初始化或零初始化變量,尤其是數據緩沖區,但意見(和情況)各不相同。

您可以使用與mallocmemset具有相同效果的calloc

Buffer *buffer = calloc(1, sizeof(Buffer));

這將分配和零初始化內存。

內存問題

正如@Chris Rollins 指出的那樣,

new_buffer->data = malloc(sizeof(char));

只分配一個字節的存儲空間,因為sizeof(char)是 1。當程序將數據從一個緩沖區復制到另一個緩沖區時,它開始覆蓋堆上的未知內存!

for (int i = 0; i < buffer->size; ++i) {
    // new_buffer only has space for one byte,
    // but we can copy several!
    new_buffer->data[i] = buffer->data[i];  // BOOM
}

這是緩沖區溢出 這是嚴重的缺陷,可能/將導致程序崩潰。 你並不孤單; 類似的錯誤導致了許多安全漏洞。

運行時內存分析可以捕獲這樣的錯誤。 如果您使用 Clang 或 gcc 進行編譯,地址清理器(通常稱為 ASAN)會指出具體的錯誤。

用這樣的東西編譯:

gcc main.c -o main -fsanitize=address -fno-omit-frame-pointer -g

您將收到一條錯誤消息,指出錯誤的代碼行:

=================================================================
==28677==ERROR: AddressSanitizer: heap-buffer-overflow on address 0x60200000efb1 at pc 0x000000400d29 bp 0x7fffa8a5db00 sp 0x7fffa8a5daf0
WRITE of size 1 at 0x60200000efb1 thread T0
    #0 0x400d28 in copy_buffer main.c:20
    #1 0x400f5d in main main.c:52
    #2 0x7ff7256cf82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)
    #3 0x400a88 in _start (main+0x400a88)

0x60200000efb1 is located 0 bytes to the right of 1-byte region [0x60200000efb0,0x60200000efb1)
allocated by thread T0 here:
    #0 0x7ff725b11602 in malloc (/usr/lib/x86_64-linux-gnu/libasan.so.2+0x98602)
    #1 0x400c09 in copy_buffer main.c:17
    #2 0x400f5d in main main.c:52
    #3 0x7ff7256cf82f in __libc_start_main (/lib/x86_64-linux-gnu/libc.so.6+0x2082f)

密集但值得破譯:

  • 堆上有緩沖區溢出: heap-buffer-overflow on address
  • 違規函數調用的調用堆棧in copy_buffer main.c:20 in main main.c:52 in copy_buffer main.c:20
  • 分配內存的in copy_buffer main.c:17 in main main.c:52 in copy_buffer main.c:17

行號特定於我的.c文件,可能因您的機器而異。

memcpy

正如@Steve Summit 指出的那樣,您可以替換for循環:

for (int i = 0; i < buffer->size; ++i) {
    new_buffer->data[i] = buffer->data[i];
}

一次調用memcpy

memcpy(new_buffer->data, buffer->data, buffer->size);

這與for循環具有相同的風險,但更簡潔/更易於閱讀。 它也應該一樣快。

解決方案

將這些想法合並到copy_buffer可能看起來像這樣:

Buffer *copy_buffer(Buffer *buffer) {
    Buffer *new_buffer = calloc(1, sizeof(Buffer));
    new_buffer->data = calloc(buffer->size, sizeof(char));
    memcpy(new_buffer->data, buffer->data, buffer->size);
    new_buffer->size = buffer->size;
    return new_buffer;
}
  • calloc在堆上分配和初始化內存
  • new_buffer->data足夠大,可以容納所有bufferdata
  • memcpy()簡潔地將數據從一個緩沖區復制到另一個緩沖區

當然,不要忘記free你分配的內存!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM