如何检测一块内存是否已经释放

How to detect if a block of memory already freed

我已经知道有 可以知道指针目标是否仍然有效分配它已经被释放,所以我试图使用指向指针的指针来解决这个问题,但它没有用.

我的 objective 只是让 print_block() 检测 block 指针是否为 Null

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

void free_block(u_int8_t **_block) {

    // Dereference
    u_int8_t *block = *_block;

    // Info
    printf("free_block()\t-> %p Point To %p \n", &block, block);

    // Free Block
    free(block);
    block = NULL;
}

void print_block(u_int8_t **_block) {

    // Dereference
    u_int8_t *block = *_block;

    // Detectc if this block is freed
    // This is the objective of this code
    if(block == NULL) {

        printf("print_block()\t-> %p Is Null.\n", block);
        return;
    }

    // Info
    printf("print_block()\t-> %p Point To %p -> ", &block, block);

    // Print byte by byte
    u_int8_t *p = block;
    for(int i = 0; i < 3; i++) {

        printf("0x%02X ", *(u_int8_t *)p);
        p++;
    }
    printf("\n");
}

int main(void) {

    // Allocat a block in the memory
    u_int8_t *block = malloc(3 * sizeof(u_int8_t));

    // Set all to zeros
    memset(block, 0x00, 3);

    // Info
    printf("Main()\t\t\t-> %p Point To %p \n", &block, block);

    // Print the block content
    print_block(&block);

    // Free the block
    free_block(&block);

    // Print the block content gain
    // This shold print Null because
    // we freed the block.
    print_block(&block);

    return 0;
}

结果

Main()          -> 0x7fffd549cc58 Point To 0xfa42a0 
print_block()   -> 0x7fffd549cc28 Point To 0xfa42a0 -> 0x00 0x00 0x00 
free_block()    -> 0x7fffd549cc60 Point To 0xfa42a0 
print_block()   -> 0x7fffd549cc28 Point To 0xfa42a0 -> 0xA4 0x0F 0x00 

没有标准的方法可以判断一个指针是否有效,也没有判断它是否指向已分配的对象,也没有判断它是否已经被释放的方法。

然而,可以在分配函数之上设计一个框架来跟踪所有分配和解除分配并维护有效指针列表。在此列表中查找指针值将告诉您指针是否指向有效的分配块以及可能从堆中请求的大小。

这是一个简单的实现:

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

void track_add_pointer(void *p, size_t size);
int track_find_pointer(void *p, size_t *sizep);
void track_remove_pointer(void *p);

void *tmalloc(size_t size) {
    void *p = (malloc)(size);
    if (p) {
        track_add_pointer(p, size);
    }
    return p;
}

void *tcalloc(size_t size, size_t nmemb) {
    void *p = (calloc)(size, nmemb);
    if (p) {
        track_add_pointer(p, size * nmemb);
    }
    return p;
}

void *trealloc(void *p, size_t size) {
    if (p) {
        void *newp = NULL;
        if (!track_find_pointer(p, NULL)) {
            fprintf(stderr, "trealloc: invalid pointer %p\n", p);
        } else {
            if (size == 0) {
                (free)(p);
            } else {
                newp = (realloc)(p, size);
            }
            if (newp != p) {
                if (p) track_remove_pointer(p);
                if (newp) track_add_pointer(newp);
            }
        }
        return newp;
    } else {
        return tmalloc(size);
    }
}

void tfree(void *p) {
    if (p) {
        if (!track_find_pointer(p, NULL)) {
            fprintf(stderr, "tfree: invalid pointer %p\n", p);
        } else {
            (free)(p);
            track_remove_pointer(p);
        }
    }
}

char *tstrdup(const char *s) {
    char *p = NULL;
    if (s) {
        size_t len = strlen(s);
        p = tmalloc(len + 1);
        if (p) {
            memcpy(p, s, len + 1);
        }
    } else {
        fprintf(stderr, "tstrdup: null pointer\n");
    }
    return p;
}

char *tstrndup(const char *s, size_t n) {
    char *p = NULL;
    if (s) {
        size_t len = strnlen(s, n);
        p = tmalloc(len + 1);
        if (p) {
            memcpy(p, s, len);
            p[len] = '[=10=]';
        }
    } else {
        fprintf(stderr, "tstrndup: null pointer\n");
    }
    return p;
}

你可以使用 Block struct 和一点纪律。

举个例子

typedef struct
{
    size_t   size;
    uint8_t  fill;
    uint8_t* data;

} Block;

Block* free_block(Block*);
Block* get_block(size_t size, uint8_t fill_value);
void   print_block(Block* block, const char* title);

  • 使函数对指向 Block 的指针进行操作,封装数据
  • make free_block() return NULL 以简化使块指针无效的任务
  • 测试里面的块地址print_block()
  • print_block()
  • 中使用可选标题

以main为例

int main(void)
{
    const int test_size = 32;
    Block* block = get_block(test_size, 0);  // zero-filled
    printf(
        "Main()\t\t\t-> Pointer at %p points to data at "
        "%p\n",
        &block, block->data);
    print_block(block, "\nBlock contents:");
    block = free_block(block);
    print_block(block, "\nafter free()");
    return 0;
}

也许您觉得这种方式更容易阅读。

main 的输出如上

Main()                  -> Pointer at 00CFF754 points to data at 00E96C70

Block contents
print_block()-> valid block data of size 32 at 00E96C70
                fill char is 00

00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00

after free()
print_block()   -> block is null.

完整代码

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

typedef struct
{
    size_t   size;
    uint8_t  fill;
    uint8_t* data;

} Block;

Block* free_block(Block*);
Block* get_block(size_t size, uint8_t fill_value);
void   print_block(Block* block, const char* title);

int main(void)
{
    const int test_size = 32;
    Block* block = get_block(test_size, 0);  // 3 bytes, zero filled
    printf(
        "Main()\t\t\t-> Pointer at %p points to data at "
        "%p\n",
        &block, block->data);
    print_block(block, "\nBlock contents:");
    block = free_block(block);
    print_block(block, "\nafter free()");
    return 0;
}

Block* free_block(Block* block)
{
    if (block == NULL) return NULL;
    if (block->data == NULL) return NULL;
    free(block->data);  // data is free
    free(block);
    return NULL;
}

Block* get_block(size_t size, uint8_t fill)
{
    Block* block = malloc(sizeof(Block));
    if (block == NULL) return NULL;
    block->data = malloc(size * sizeof(uint8_t));
    if (block->data == NULL) return NULL;
    memset(block->data, fill, size);
    block->fill = fill;
    block->size = size;
    return block;
}

void print_block(Block* block, char* title)
{
    if (title != NULL) printf("%s\n", title);
    if (block == NULL)
    {
        printf("print_block()\t-> block is null.\n");
        return;
    }
    printf(
        "print_block()->\tvalid block data of size %d at "
        "%p\n\t\tfill char is %02X\n\n",
        block->size, block->data, block->fill);
    // Print byte by byte
    const int nc = 16; // 16 bytes per line
    size_t    j  = 1;
    for (size_t i = 0; i < block->size; i += 1)
    {
        printf("%02X ", block->data[i]);
        if (j >= nc)
            printf("\n"), j = 1;
        else
            j += 1;
    }
    if ( j != 1 ) printf("\n");
    return;
}

首先,您需要知道 free(p) 标记内存块可用于任何新分配,仅此而已;并且指针 p 它本身仍然是一个 Valid 指针,您可以 ReadWrite 从中

关于您的问题“如何检测内存块是否已被释放?”,C 中的简短回答是没有办法。要解决这个问题,您肯定需要一个指针跟踪器,这并不难做到,这是一个示例:

void *ptr_list[64];
int ptr_position = 0;

bool ptr_exist(void *p) {
    if(p == NULL)
        return false;
    for(int i = 0; i < ptr_position; i++) {
        if(ptr_list[i] == p)
            return true;
    }
    return false;
}

void ptr_add(void *p) {
    if(p == NULL)
        return;
    if(!ptr_exist(p)) {
        for(int i = 0; i < ptr_position; i++) {
            if(ptr_list[i] == NULL) {
                ptr_list[i] = p;
                return;
            }
        }
        ptr_list[ptr_position] = p;
        ptr_position++;
    }
}

void ptr_free(void **p) {
    if(*p == NULL)
        return;
    for(int i = 0; i < ptr_position; i++) {
        if(ptr_list[i] == *p) {
            ptr_list[i] = NULL;
        }
    }
    for(int i = ptr_position; i >= 0; i--) {
        if(ptr_list[i] == NULL) {
            ptr_position = i;
            break;
        }
    }
    free(*p);
    *p = NULL;
}

要在分配内存块后使用它,请使用 ptr_add() 指针 添加到跟踪器,当你想释放它时使用 ptr_free().最后,您可以随时从任何线程检查此内存块是否仍然有效使用 ptr_exist().

结果和完整代码:

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

// -- Pointers Tracker --
void *ptr_list[64];
int ptr_position = 0;

bool ptr_exist(void *p) {
    if(p == NULL)
        return false;
    for(int i = 0; i < ptr_position; i++) {
        if(ptr_list[i] == p)
            return true;
    }
    return false;
}

void ptr_add(void *p) {
    if(p == NULL)
        return;
    if(!ptr_exist(p)) {
        for(int i = 0; i < ptr_position; i++) {
            if(ptr_list[i] == NULL) {
                ptr_list[i] = p;
                return;
            }
        }
        ptr_list[ptr_position] = p;
        ptr_position++;
    }
}

void ptr_free(void **p) {
    if(*p == NULL)
        return;
    for(int i = 0; i < ptr_position; i++) {
        if(ptr_list[i] == *p) {
            ptr_list[i] = NULL;
        }
    }
    for(int i = ptr_position; i >= 0; i--) {
        if(ptr_list[i] == NULL) {
            ptr_position = i;
            break;
        }
    }
    free(*p);
    *p = NULL;
}
// ----------------------

void free_block(u_int8_t *block) {

    // Info
    printf("free_block()\t-> %p Point To %p \n", &block, block);

    // Free Block
    // free(block);
    // block = NULL;
    ptr_free((void *)&block);
}

void print_block(u_int8_t *block) {

    // Detectc if this block is freed
    // This is the objective of this code
    if(!ptr_exist(block)) {

        printf("print_block()\t-> %p Is Null.\n", block);
        return;
    }

    // Info
    printf("print_block()\t-> %p Point To %p -> ", &block, block);

    // Print byte by byte
    u_int8_t *p = block;
    for(int i = 0; i < 3; i++) {

        printf("0x%02X ", *(u_int8_t *)p);
        p++;
    }
    printf("\n");
}

int main(void) {

    // Allocat a block in the memory
    u_int8_t *block = malloc(3 * sizeof(u_int8_t));

    // Add it to the tracker
    ptr_add((void *)block);

    // Set all to zeros
    memset(block, 0x00, 3);

    // Info
    printf("Main()\t\t\t-> %p Point To %p \n", &block, block);

    // Print the block content
    print_block(block);

    // Free the block
    free_block(block);

    // Print the block content gain
    // This shold print Null because
    // we freed the block.
    print_block(block);

    return 0;
}