Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/61.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
如何从C中的指针获取数组的大小?_C_Memory Management_Pointers_Malloc - Fatal编程技术网

如何从C中的指针获取数组的大小?

如何从C中的指针获取数组的大小?,c,memory-management,pointers,malloc,C,Memory Management,Pointers,Malloc,我已经分配了一个大小为n的mystruct数组,如下所示: if (NULL == (p = calloc(sizeof(struct mystruct) * n,1))) { /* handle error */ } 后来,我只能访问p,而不再有n。有没有一种方法可以在给定指针p的情况下确定数组的长度 我想这一定是可能的,因为freep就是这么做的。我知道malloc跟踪它分配了多少内存,这就是它知道长度的原因;也许有办法查询这些信息?类似于 int length = askMallocL

我已经分配了一个大小为n的mystruct数组,如下所示:

if (NULL == (p = calloc(sizeof(struct mystruct) * n,1))) {
 /* handle error */
}
后来,我只能访问p,而不再有n。有没有一种方法可以在给定指针p的情况下确定数组的长度

我想这一定是可能的,因为freep就是这么做的。我知道malloc跟踪它分配了多少内存,这就是它知道长度的原因;也许有办法查询这些信息?类似于

int length = askMallocLibraryHowMuchMemoryWasAlloced(p) / sizeof(mystruct)

我知道我应该重新编写代码,这样我就知道了n,但如果可能的话,我宁愿不这样做。有什么想法吗?

自己跟踪阵列大小;free使用malloc链来释放分配的块,它不一定与您请求的数组大小相同

我不知道有什么方法,但我可以想象它会处理malloc内部的混乱,这通常是一个非常非常糟糕的主意

为什么不能存储分配的内存大小


编辑:如果你知道你应该重新编写代码,这样你就知道了,那就去做吧。是的,尝试轮询malloc可能很快也很容易,但肯定知道n将最大限度地减少混乱并加强设计。

malloc将返回至少与您请求的大小相同但可能更大的内存块。因此,即使可以查询块大小,也无法可靠地提供数组大小。因此,您只需修改代码以自己跟踪它。

您不能询问malloc库块有多大的原因之一是,分配器通常会将请求的大小取整,以满足某些最小粒度要求,例如16字节。所以如果你要5个字节,你会得到一个大小为16的块。如果你取16除以5,你会得到三个元素,而实际上你只分配了一个。malloc库需要额外的空间来跟踪您首先请求的字节数,因此您最好自己跟踪这些字节。

不,如果不强烈依赖malloc的实现细节,就无法获得这些信息。特别是,malloc可能会分配比您要求的更多的字节,例如,为了特定内存体系结构中的效率。最好重新设计代码,以便明确地跟踪n。另一种方法至少是同样多的重新设计和更危险的方法,因为它是非标准的,滥用了指针的语义,并且对于后面的人来说将是一场维护噩梦:将长度存储在malloc地址,然后是数组。分配将是:

void *p = calloc(sizeof(struct mystruct) * n + sizeof(unsigned long int),1));
*((unsigned long int*)p) = n;
n现在存储在*unsigned long int*p,数组的开头现在是

void *arr = p+sizeof(unsigned long int);
编辑:只是为了扮演魔鬼代言人。。。我知道这些解决方案都需要重新设计,但让我们一起来尝试吧。 当然,上面介绍的解决方案只是一个打包良好的结构的黑客实现。您不妨定义:

typedef struct { 
  unsigned int n;
  void *arr;
} arrInfo;
传递arrInfos而不是原始指针

现在我们在做饭。但只要你在重新设计,为什么要停在这里?您真正想要的是抽象数据类型ADT。算法和数据结构类的任何介绍性文本都可以。ADT定义数据类型的公共接口,但隐藏该数据类型的实现。因此,数组的ADT可能看起来像

typedef void* arrayInfo;
(arrayInfo)newArrayInfo(unsignd int n, unsigned int itemSize);
(void)deleteArrayInfo(arrayInfo);
(unsigned int)arrayLength(arrayInfo);
(void*)arrayPtr(arrayInfo);
...
换句话说,ADT是数据和行为封装的一种形式。。。换言之,它是接近于使用直C的面向对象编程的,除非你被困在没有C++编译器的平台上,否则你可以去全HOG,只使用STL STD::vector。
<>在这里,我们对C进行了一个简单的问题,最后在C++中结束。上帝保佑我们所有人。

只是为了确认前面的答案:仅仅通过研究指针,无法知道返回该指针的malloc分配了多少内存

如果成功了呢? 这是为什么不可能的一个例子。让我们想象一下,代码中有一个名为get_sizevoid*的假设函数,该函数返回为指针分配的内存:

typedef struct MyStructTag
{ /* etc. */ } MyStruct ;

void doSomething(MyStruct * p)
{
   /* well... extract the memory allocated? */
   size_t i = get_size(p) ;
   initializeMyStructArray(p, i) ;
}

void doSomethingElse()
{
   MyStruct * s = malloc(sizeof(MyStruct) * 10) ; /* Allocate 10 items */
   doSomething(s) ;
}
为什么即使成功了,也不会成功? 但是这种方法的问题是,在C语言中,您可以使用指针算法。让我们重写doSomethingElse:

void doSomethingElse()
{
   MyStruct * s = malloc(sizeof(MyStruct) * 10) ; /* Allocate 10 items */
   MyStruct * s2 = s + 5 ; /* s2 points to the 5th item */
   doSomething(s2) ; /* Oops */
}
get_size应该如何工作,因为您向函数发送了一个有效指针,但不是malloc返回的指针。即使get_size不厌其烦地查找大小(即以一种低效的方式),在这种情况下,它也会返回一个在您的上下文中是错误的值

结论
总是有办法避免这个问题,在C语言中,您可以编写自己的分配器,但同样,当您只需要记住分配了多少内存时,这可能太麻烦了。

我可以推荐一种糟糕的方法吗

按如下方式分配所有阵列:

void *blockOfMem = malloc(sizeof(mystruct)*n + sizeof(int));

((int *)blockofMem)[0] = n;
mystruct *structs = (mystruct *)(((int *)blockOfMem) + 1);
然后您可以将数组强制转换为int*并访问-1 st元素

确保释放该指针,而不是数组指针本身


此外,这可能会导致可怕的虫子,让你撕掉头发。也许你可以把alloc函数包装成API调用之类的东西

对于指针数组,可以使用以NULL结尾的数组。然后,长度可以像用字符串一样确定。在您的示例中,您可以使用structure属性来标记then end。当然,这取决于是否存在不能为NULL的成员。假设您有一个属性名,需要为数组中的每个结构设置该属性名,然后您可以通过以下方式查询大小:


int size;
struct mystruct *cur;

for (cur = myarray; cur->name != NULL; cur++)
    ;

size = cur - myarray;

顺便说一句,在您的示例中应该是callocn、sizeofstruct、mystruct。

一些编译器提供了msize或类似的函数_msize等,让您能够准确地实现这一点。

其他编译器讨论了普通c指针的限制和malloc的stdlib.h实现。一些实现提供的扩展返回的分配块大小可能大于请求的大小

如果必须具有这种行为,则可以使用或编写专门的内存分配器。最简单的方法是围绕stdlib.h函数实现一个包装器。比如:

void* my_malloc(size_t s);     /* Calls malloc(s), and if successful stores 
                                  (p,s) in a list of handled blocks */
void my_free(void* p);         /* Removes list entry and calls free(p) */
size_t my_block_size(void* p); /* Looks up p, and returns the stored size */
...

这是对我的例行测试。它设置7个变量来保存浮点值,然后将它们分配给一个数组,该数组用于查找最大值

神奇之处在于调用myMax:

float mmax=myMaxfloat*&arr,int-sizeofar/sizeofar[0]

这很神奇,不是吗

myMax需要一个浮点数组指针float*,因此我使用&arr获取数组的地址,并将其转换为浮点指针

myMax还希望数组中的元素数为int。我通过使用sizeof来获得该值,以给出数组和数组第一个元素的字节大小,然后将总字节数除以每个元素中的字节数。我们不应该猜测或硬编码int的大小,因为它在某些系统上是2字节,在某些系统上是4字节,而在其他系统上可能是其他的

注意:当您的数据可能有不同数量的样本时,所有这些都很重要

以下是测试代码:

#include <stdio.h>

float a, b, c, d, e, f, g;

float myMax(float *apa,int soa){
 int i;
 float max = apa[0];
 for(i=0; i< soa; i++){
  if (apa[i]>max){max=apa[i];}
  printf("on i=%d val is %0.2f max is %0.2f, soa=%d\n",i,apa[i],max,soa);
 }
 return max;
}

int main(void)
{
 a = 2.0;
 b = 1.0;
 c = 4.0;
 d = 3.0;
 e = 7.0;
 f = 9.0;
 g = 5.0;
 float arr[] = {a,b,c,d,e,f,g};

 float mmax = myMax((float *)&arr,(int) sizeof(arr)/sizeof(arr[0]));
 printf("mmax = %0.2f\n",mmax);

 return 0;
}

实际上,您的问题是-我可以找出malloc'd或calloc'd数据块的大小吗。正如其他人所说:不,不是以标准方式

但是,有一些自定义的malloc实现可以做到这一点,例如,在中有一个malloc_SIZE宏:


事实上,这是一个完美的理由,为什么你应该能够问malloc库一个块有多大。对于我来说,设计C语言时没有这样的查询函数是毫无意义的。我曾经在一个系统上工作过,其中标准分配函数返回块及其实际大小>=当然是请求的大小。适用于缓冲区和缓存之类的东西,在这些地方您可以充分利用任何多余的空间。c语言是一种方便的汇编表达式。标准库是最小的,因为它最初在嵌入式系统上运行,现在仍然在嵌入式系统中运行。如果你想要一个能提供很多铃声和口哨的分配器,那就用一个吧。虽然答案都是正确的,但这是一个非常好的问题。因此,我们有一个投票权;对于可移植代码没有好处,因为如果mystruct包含任何对齐要求大于sizeofint的成员,那么它就不起作用。显然,在sizeofint是任何类型中最大对齐要求的倍数的平台上,这不是一个问题,但会与SPARC上的eg-mfaster结构发生冲突。get_size必须传递一个指向已分配块开头的指针,这一事实并不妨碍获得它。只是不要传入无效的值。free也有同样的限制,当然,这是存在的,但是free通常是在使用时考虑到这一点,以及分配内存的malloc。get_size可以在任何地方使用,包括用户不应该知道如何通过池在堆栈上分配内存的地方,等等…+1是一个很好的解释。我唯一的缺点是:如果它有效,并且对你能用它做什么有限制怎么办?正如dmkee在评论的其他地方指出的,在OSX我的平台上,它被称为malloc_size,并且它完全按照预期工作。有一点是你做不到的,还有一点是,如果你要这样做,你应该非常小心——两件完全不同的事情它在OSX上被称为malloc_size。@Joel-有没有想过delete[]*p是如何调用p指向的数组中的所有析构函数的?这是因为new做了与bary建议的相同的事情。new将数组中的项数存储在数组的开头,并将指针指向第一个位置。@computinglife-不一定,分配器可以轻松地将元数据保存在内存中与其分发的位不同的部分,以防止缓冲区溢出损坏内部数据结构,实际上,glibc的默认分配器将大小放在返回指针的前面,但使用lo
wer位表示元数据-因此,数字必须被屏蔽才能准确。你不能在这样的空格*p上做算术。我认为你需要再次阅读这个问题。在您的答案中,您使用的是静态分配数组的名称arr,问题是只有一个指向动态分配数组的指针。
/* The size of a malloc allocation is stored in a size_t word
   MALLOC_HEADER_SIZE bytes prior to the start address of the allocation:

     +--------+---------+-------------------+
     | SIZE   |(unused) | allocation  ...   |
     +--------+---------+-------------------+
     ^ BASE             ^ ADDR
     ^ ADDR - MALLOC_HEADER_SIZE
*/

/* The amount of extra space used by the malloc header.  */
#define MALLOC_HEADER_SIZE          \
  (MALLOC_ALIGNMENT < sizeof (size_t)       \
   ? sizeof (size_t)                \
   : MALLOC_ALIGNMENT)

/* Set up the malloc header, and return the user address of a malloc block. */
#define MALLOC_SETUP(base, size)  \
  (MALLOC_SET_SIZE (base, size), (void *)((char *)base + MALLOC_HEADER_SIZE))
/* Set the size of a malloc allocation, given the base address.  */
#define MALLOC_SET_SIZE(base, size) (*(size_t *)(base) = (size))

/* Return base-address of a malloc allocation, given the user address.  */
#define MALLOC_BASE(addr)   ((void *)((char *)addr - MALLOC_HEADER_SIZE))
/* Return the size of a malloc allocation, given the user address. */
#define MALLOC_SIZE(addr)   (*(size_t *)MALLOC_BASE(addr))