有没有*任何*方法可以获取C ++ / G ++中C样式数组的长度?

问题描述

| 我已经尝试实现lengthof(T * v)函数了一段时间了,到目前为止还没有成功。 对于T v [n]数组,有两种众所周知的基本解决方案,一旦将数组衰减为T * v指针,这两种方法都是无用的,甚至是危险的。
#define SIZE(v) (sizeof(v) / sizeof(v[0]))

template <class T,size_t n>
size_t lengthof (T (&) [n])
{
    return n;
}
有一些解决方法涉及包装器类和容器,例如STLSoft的array_proxy,boost :: array,std :: vector等。它们都有缺点,并且缺乏简单性,语法糖和数组的广泛使用。 关于涉及特定于编译器的解决方案的说法存在误解,当delete []需要知道数组的长度时,编译器通常会使用这些解决方案。根据C ++ FAQ Lite 16.14,编译器使用两种技术来知道要释放多少内存:过度分配和关联数组。在过度分配时,它会分配更多的字量,并将数组的长度放在第一个对象之前。显然,另一种方法将长度存储在关联数组中。是否有可能知道G ++使用哪种方法,并提取适当的数组长度?那开销和填充物呢?对非编译器专用代码有希望吗?甚至非平台特定的G ++内置程序? 我还实现了涉及重载运算符new []和运算符delete []的解决方案:
std::map<void*,size_t> arrayLengthMap;

inline void* operator new [] (size_t n)
throw (std::bad_alloc)
{
    void* ptr = GC_malloc(n);
    arrayLengthMap[ptr] = n;
    return ptr;
}

inline void operator delete [] (void* ptr)
throw ()
{
    arrayLengthMap.erase(ptr);
    GC_free(ptr);
}

template <class T>
inline size_t lengthof (T* ptr)
{
    std::map<void*,size_t>::const_iterator it = arrayLengthMap.find(ptr);
    if( it == arrayLengthMap.end() ){
        throw std::bad_alloc();
    }
    return it->second / sizeof(T);
}
直到出现一个奇怪的错误,它一直运行良好:lengthof找不到数组。事实证明,G ++在此特定数组的开头分配了比应有的多8个字节。尽管运算符new []应该返回了整个数组的开始,将其称为ptr,但调用代码却改为ptr + 8,因此lengthof(ptr + 8)显然因失败而失败了(即使没有,它也可能可能返回了错误的数组大小)。那8个字节是某种开销还是填充?不能被前面提到的过度分配,该功能对于许多数组都能正常工作。假设可以使用G ++特定的调用或欺骗,它是什么以及如何禁用或解决它? 编辑: 由于可以通过多种方式分配C样式的数组,因此通常无法像Oli Charlesworth所建议的那样通过其指针来判断任意数组的长度。但是可能会出现非衰减的静态数组(请参见上面的模板函数),以及根据Ben Voigt的想法用自定义运算符new [](size_t,size_t)分配的数组:
#include <gc/gc.h>
#include <gc/gc_cpp.h>
#include <iostream>
#include <map>

typedef std::map<void*,std::pair<size_t,size_t> > ArrayLengthMap;
ArrayLengthMap arrayLengthMap;

inline void* operator new [] (size_t size,size_t count)
throw (std::bad_alloc)
{
    void* ptr = GC_malloc(size);
    arrayLengthMap[ptr] = std::pair<size_t,size_t>(size,count);
    return ptr;
}

inline void operator delete [] (void* ptr)
throw ()
{
    ArrayLengthMap::const_iterator it = arrayLengthMap.upper_bound(ptr);
    it--;
    if( it->first <= ptr and ptr < it->first + it->second.first ){
        arrayLengthMap.erase(it->first);
    }
    GC_free(ptr);
}

inline size_t lengthof (void* ptr)
{
    ArrayLengthMap::const_iterator it = arrayLengthMap.upper_bound(ptr);
    it--;
    if( it->first <= ptr and ptr < it->first + it->second.first ){
        return it->second.second;
    }
    throw std::bad_alloc();
}

int main (int argc,char* argv[])
{
    int* v = new (112) int[112];
    std::cout << lengthof(v) << std::endl;
}
不幸的是,由于编译器的任意开销和填充,到目前为止,尚无可靠的方法来确定自定义运算符new [](size_t)中动态数组的长度,除非我们假定填充小于一的大小。数组元素。 但是,正如Ben Voigt所建议的那样,还有其他类型的数组也可以进行长度计算,因此应该并且可能希望构造一个在其构造函数中可以接受多种数组(及其长度)的包装器类,并且可以隐式或显式转换为其他包装类和数组类型。不同种类的数组的不同生存期可能是一个问题,但是可以通过垃圾回收解决。     

解决方法

        要回答这个问题:   对非编译器专用代码有希望吗? 没有。 一般来说,如果您发现自己需要这样做,那么您可能需要重新考虑您的设计。例如,使用“ 3”。     ,        您的分析基本上是正确的,但是我认为您忽略了以下事实:带有琐碎析构函数的类型不需要存储长度,因此对于不同类型,过度分配可能会有所不同。 该标准允许ѭ4窃取一些字节以供自己使用,因此您必须对指针进行范围检查,而不是精确匹配。
std::map
可能对此无效,但应该对排序后的向量进行排序(可以进行二进制搜索)。平衡的树也应该很好地工作。     ,前一段时间,我使用了类似的方法来监视内存泄漏: 当要求分配大小字节的数据时,我将分配大小+ 4个字节,并将分配的长度存储在前4个字节中:
static unsigned int total_still_alloced = 0;
void *sys_malloc(UINT size)
{
#if ENABLED( MEMLEAK_CHECK )
  void *result = malloc(size+sizeof(UINT )); 
  if(result)
  {
    memset(result,size+sizeof(UINT ));
    *(UINT *)result = size;
    total_still_alloced += size;
    return (void*)((UINT*)result+sizeof(UINT));
  }
  else
  {
    return result;
  }
#else
  void *result = malloc(size);
  if(result) memset(result,size);
  return result;
#endif
}

void sys_free(void *p)
{
  if(p != NULL)
  {
#if ENABLED( MEMLEAK_CHECK )
    UINT * real_address = (UINT *)(p)-sizeof(UINT);
    total_still_alloced-= *((UINT *)real_address);

    free((void*)real_address);
#else
    free(p);
#endif
  }
}
在您的情况下,检索分配的大小只需将提供的地址移位4并读取值即可。 请注意,如果某处内存损坏...您将得到无效的结果。 还要注意,malloc通常是内部工作的方式:在返回地址之前,将分配的大小放在隐藏字段中。在某些体系结构上,我什至不必分配更多,使用系统malloc就足够了。 这是一种侵入式的方法……但是它可以工作(只要您使用这些修改后的分配例程分配所有内容,并且您知道数组的起始地址)。     

相关问答

依赖报错 idea导入项目后依赖报错,解决方案:https://blog....
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下...
错误1:gradle项目控制台输出为乱码 # 解决方案:https://bl...
错误还原:在查询的过程中,传入的workType为0时,该条件不起...
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct...