arraylist->;arraylist_append函数中的cap没有意义

arraylist->;arraylist_append函数中的cap没有意义,c,arrays,C,Arrays,我正在尝试编程我自己的动态大小阵列系统。但是存储在arraylist_append函数中的array->cap值毫无意义。我猜我有一个缓冲区溢出,但我找不到它。 数组.c: 一个小测试程序: #include <stdio.h> #include "array.h" int main() { int *int_array = arraylist_create(2, sizeof (int)); int_array[0] = 28; int_array[1]

我正在尝试编程我自己的动态大小阵列系统。但是存储在arraylist_append函数中的array->cap值毫无意义。我猜我有一个缓冲区溢出,但我找不到它。
数组.c:

一个小测试程序:

#include <stdio.h>
#include "array.h"

int main() {
    int *int_array = arraylist_create(2, sizeof (int));
    int_array[0] = 28;
    int_array[1] = 20;
    for(int i = 0; i < 100; ++i) int_array = arraylist_append(int_array, &i);
    for(int i = 0; i < 102; ++i) printf("Index: %d Value: %d\n", i, int_array[i]);
}

如您所见,arraylist->cap值异常高

您在C中遇到了运算符优先级问题:

char *arraylist_char = (char*)arraylist+1;
类型转换的优先级高于
+
运算符

这意味着您首先强制转换为
字符*
,然后添加
+1
。 这将指向标头开始后的1字节

您可以通过添加以下打印来检查这一点:

printf("arrylist: %p\n", (void*)arraylist);
printf("arrylist+1: %p\n", (void*)(arraylist+1));
printf("(char*)arrylist+1: %p\n", (void*)((char*)arraylist+1));
printf("(char*)(arrylist+1): %p\n", (void*)((char*)(arraylist+1)));
这将导致此输出:

appending at array with address 0x55ecf3a3e260
arraylist len: 2
arraylist cap: 2
Realloc array. new address 0x55ecf3a3e6a0
arrylist: 0x55ecf3a3e6a0
arrylist+1: 0x55ecf3a3e6b8
(char*)arrylist+1: 0x55ecf3a3e6a1   <<=== WRONG!
(char*)(arrylist+1): 0x55ecf3a3e6b8 <<=== CORRECT
除此缺陷外,您的代码还有其他问题:

  • arraylist=realloc(arraylist,…

    永远不要将realloc的返回值赋给传入函数的指针。如果出现错误,将返回NULL,并且指针的旧值已丢失

  • 您的方法对于所有类型都不安全。在某些体系结构上可能会遇到对齐问题。如果要存储在该数组中的数据类型的对齐限制大于arraylist_meta的对齐限制(该限制与大小的对齐限制基本相同),则返回的指针可能没有正确对齐r存储的数据类型

    例如,您可以考虑一台机器,其中
    size\u t
    仅为4个字节,导致标头的大小总共为12个字节,对齐方式为4个字节。现在让我们假设您要存储一个具有一些大数据类型的结构,比如说
    long double
    ,它可能需要8个字节的对齐。而
    malloc
    realloc
    normalle返回一个对齐的指针,以满足体系结构的最大对齐要求,为标头添加12个字节将导致指针不满足存储结构所需的8个字节的对齐要求。您应该放大该标头大小,以满足存储结构的最大对齐需要体系结构或至少满足存储元素的大小

    执行此操作的一个选项如下所示: union应该使包含的较大数据类型正确对齐

  • 或者,如果你使用C11(正如Georgen先生所发现的那样),甚至更优雅一点:

    #包括
    结构arraylist_meta{
    联合{
    最大对齐虚拟机;
    结构{
    尺寸透镜;
    尺寸(t)帽;;
    一个元素的大小;
    };
    };
    };
    

    为了获得尽可能大的对齐,这更为通用,我在结构中添加了额外的嵌套级别。这允许在函数声明和受影响函数的现有调用方中保留数据类型。

    对齐问题的解决方案

    #include <stddef.h>
    union arraylist_meta {
            max_align_t dummy;
            struct {
                size_t len;
                size_t cap;
                size_t sizeof_one_element;
            };
        }
    
    我知道为什么要在那里使用struct。可能这个类型只代表了基类型的最大大小,而不是基类型的最大大小。所以Gerhardh之前提出的解决方案实际上更好,因为max_align_t不必要地大。

    另一种可能是使用
    \u属性__((uuu aligned_uuu
    正如您在上面的
    max_ualign_ut
    的类型定义中所看到的那样。问题是这些属性没有被任何C标准定义,您可以在程序上运行以检测溢出。安装并运行非常简单欢迎这样做。因为很明显,这些值很快就会变得奇怪开始追加到数组后。如果在2或3次调用后可以看到错误,则无需使用prints for 100 append操作来填充帖子。这行似乎错误:
    struct arraylist\u meta*arraylist\u new=malloc(array\u size*sizeof\u one\u element+sizeof*arraylist\u new)
    不应该是:
    struct arraylist\u meta*arraylist\u new=malloc(array\u size*sizeof\u one\u element+sizeof(arraylist\u meta)
    @Jean MarcVolle不太可能。首先,没有类型
    arraylist\u meta
    它必须是
    struct arraylist\u meta
    。除此之外,
    *arraylist\u new
    是类型
    struct arraylist\u meta
    在大小上没有任何区别。感谢您的帮助和回复,但是有没有办法扩大标题ze要满足所有架构的最大一致性我不是100%确定这是否对所有架构都有帮助,但你可以从我的更新中看到一个建议a简单的方法,这不是很有效的内存使用伪类型,因为你可以这样做。对不起,在我写这篇文章的时候,我不明白联合实际上是什么。没问题。你的解决方案看起来更优雅。我不知道那种类型。很好的解决方案。在我的系统中,我的初始结构的大小为24,max_align_t的大小为32。但是……我不知道什么数据类型可能需要32个字节……)我已经仔细查看了stddef.h头,max_align_t是一个结构,如上所示
    printf("arrylist: %p\n", (void*)arraylist);
    printf("arrylist+1: %p\n", (void*)(arraylist+1));
    printf("(char*)arrylist+1: %p\n", (void*)((char*)arraylist+1));
    printf("(char*)(arrylist+1): %p\n", (void*)((char*)(arraylist+1)));
    
    appending at array with address 0x55ecf3a3e260
    arraylist len: 2
    arraylist cap: 2
    Realloc array. new address 0x55ecf3a3e6a0
    arrylist: 0x55ecf3a3e6a0
    arrylist+1: 0x55ecf3a3e6b8
    (char*)arrylist+1: 0x55ecf3a3e6a1   <<=== WRONG!
    (char*)(arrylist+1): 0x55ecf3a3e6b8 <<=== CORRECT
    
    char *arraylist_char = (char*)(arraylist+1);
    
        union arraylist_meta {
            double dummy_double;
            long double dummy_long_double;
            long long dummy_long_long;
            void *dummy_ptr;
            void (*dummy_func_ptr)(void);
            struct {
                size_t len;
                size_t cap;
                size_t sizeof_one_element;
            };
        };
    
        #include <stddef.h>
        struct arraylist_meta {
            union {
                max_align_t dummy;
                struct {
                    size_t len;
                    size_t cap;
                    size_t sizeof_one_element;
                };
            };
        };
    
    #include <stddef.h>
    union arraylist_meta {
            max_align_t dummy;
            struct {
                size_t len;
                size_t cap;
                size_t sizeof_one_element;
            };
        }
    
    typedef struct {
      long long __max_align_ll __attribute__((__aligned__(__alignof__(long long))));
      long double __max_align_ld __attribute__((__aligned__(__alignof__(long double))));
      /* _Float128 is defined as a basic type, so max_align_t must be
         sufficiently aligned for it.  This code must work in C++, so we
         use __float128 here; that is only available on some
         architectures, but only on i386 is extra alignment needed for
         __float128.  */
    #ifdef __i386__
      __float128 __max_align_f128 __attribute__((__aligned__(__alignof(__float128))));
    #endif
    } max_align_t;