You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

C语言中double与复杂类型的泛型实现问题求助

你遇到的问题本质是栈变量的地址在循环中被复用——每次循环里的double x都在栈上的同一个位置,所以你存在数组里的所有&x都是同一个指针,最后自然只会输出循环最后一次的x值。

下面是几个可行的解决方法,按通用性和可靠性排序:

方案1:堆分配独立存储(通用且可靠)

既然栈变量地址会复用,那就给每个元素在堆上分配独立内存,把值存进去,再将堆指针存入数组。同时需要给你的数组库加一个销毁回调函数,让自动释放逻辑能正确释放这些堆内存。

修改数组库的关键部分

首先扩展array结构体,增加销毁回调:

#include <stdlib.h>

// 销毁回调的类型定义
typedef void (*array_dtor_t)(void* elem);

typedef struct {
    void** elements;
    usize length;
    usize capacity;
    array_dtor_t destructor; // 新增:元素销毁函数
} array;

// 修改create函数,接受销毁回调参数
array create(usize init_size, array_dtor_t dtor) {
    array a = {0};
    a.capacity = init_size > 0 ? init_size : 4;
    a.elements = malloc(a.capacity * sizeof(void*));
    a.destructor = dtor;
    return a;
}

// 在自动释放的构造函数中,遍历元素调用销毁回调
__attribute__((constructor))
static void auto_free_arrays() {
    // 假设你用全局链表管理所有创建的array
    for (array* curr = global_array_list; curr; curr = curr->next) {
        if (curr->destructor) {
            for (usize i = 0; i < curr->length; i++) {
                curr->destructor(curr->elements[i]);
            }
        }
        free(curr->elements);
        free(curr);
    }
}

修改double类型的使用代码

#include "kstring.h"
#include "array.h"

// 针对double类型的销毁函数
void destroy_double_elem(void* elem) {
    free(elem);
}

int main(int argc, const string* argv)
{
    array a = create(0, destroy_double_elem);

    for (usize i = 1; i < argc; i++)
    {
        double x = parse(argv[i]);
        // 分配堆内存存储double值
        double* elem_ptr = malloc(sizeof(double));
        *elem_ptr = x;
        append(&a, (type)elem_ptr);
    }

    for (usize i = 0; i < a.length; i++)
        print("%lf\n", *((double*)(a.elements[i])));
}

这样每个元素都有独立的堆地址,不会被循环覆盖,自动释放时也能正确回收内存。

方案2:用Union统一存储基本类型(减少堆开销)

如果你的数组主要存储基本类型(int、double、指针等),可以用union实现值和指针的统一存储,避免堆分配。这种方案的前提是类型大小不超过void*的宽度(64位系统下是8字节,刚好覆盖大部分基本类型)。

修改数组库的关键部分

typedef union {
    void* ptr;      // 存指针类型(比如字符串)
    int i;          // 存int
    double d;       // 存double
    usize u;        // 存无符号整数
    char raw[sizeof(void*)]; // 存不超过指针大小的小结构体
} array_elem_t;

typedef struct {
    array_elem_t* elements;
    usize length;
    usize capacity;
    // 同样可以加销毁回调,处理需要堆释放的指针类型
    void (*destructor)(array_elem_t elem);
} array;

// create函数调整为返回这种结构
array create(usize init_size, void (*dtor)(array_elem_t)) {
    array a = {0};
    a.capacity = init_size > 0 ? init_size : 4;
    a.elements = malloc(a.capacity * sizeof(array_elem_t));
    a.destructor = dtor;
    return a;
}

修改double类型的使用代码

#include "kstring.h"
#include "array.h"

int main(int argc, const string* argv)
{
    array a = create(0, NULL); // 基本类型不需要销毁回调

    for (usize i = 1; i < argc; i++)
    {
        double x = parse(argv[i]);
        array_elem_t elem;
        elem.d = x;
        append(&a, elem); // append函数需要调整为接受array_elem_t
    }

    for (usize i = 0; i < a.length; i++)
        print("%lf\n", a.elements[i].d);
}

这个方案对基本类型更高效,不需要堆分配,但如果要存超过指针大小的结构体,还是得回到方案1,用堆指针存在ptr字段并配合销毁回调。

方案3:强制值转void*(移植性差,不推荐通用场景)

如果你的目标平台上,double的大小等于void*的大小(比如64位系统),可以用memcpy直接把值复制到void*变量中,避免指针:

#include "kstring.h"
#include "array.h"

int main(int argc, const string* argv)
{
    array a = create(0);

    for (usize i = 1; i < argc; i++)
    {
        double x = parse(argv[i]);
        void* elem;
        memcpy(&elem, &x, sizeof(elem));
        append(&a, elem);
    }

    for (usize i = 0; i < a.length; i++)
    {
        double x;
        memcpy(&x, &a.elements[i], sizeof(x));
        print("%lf\n", x);
    }
}

这种方法完全依赖平台的类型大小匹配,移植性很差,只适合特定场景临时用,不适合作为通用库的方案。


内容的提问来源于stack exchange,提问作者KianFakheriAghdam

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.10 14:20:34