如何检查内存块中的所有字节是否为零

时间:2011-08-04 08:19:29

标签: c++

我有一个内存块,其中包含固定大小的元素,比如说100个字节,一个接一个地放入,所有内容都有相同的固定长度,所以内存看起来像这样

<element1(100 bytes)><element2(100 bytes)><element3(100 bytes)>...

在某些情况下,我需要确定某个元素的所有字节是否都设置为0字节,因为它具有特殊含义(我没有说这是一个好主意,但这就是我所处的情况)。

问题是,我该如何有效地做到这一点。进一步:有一个简单的功能来做到这一点。为了将字节设置为零,我可以使用memset或bzero,但我不知道检查零的任何函数。

目前我正在使用循环进行检查

char *elementStart = memoryBlock + elementNr*fixedElementSize;
bool special = true;
for ( size_t curByteNr=0; curByteNr<fixedElementSize; ++curByteNr )
{
  special &= (*(elementStart+curByteNr)) == 0;
}

当然,我可以使用更大的偏移量循环,并使用mword或其他更适合的更大类型一次检查几个字节。我想这会很有效率,但我想知道是否有一种功能可以减轻我的负担。

建议的功能:

  • !memcmp(compareBlock,myBlock,fixedElementSize)

10 个答案:

答案 0 :(得分:21)

你可能实际上使用memcmp而不必分配零值数组,如下所示:

static int memvcmp(void *memory, unsigned char val, unsigned int size)
{
    unsigned char *mm = (unsigned char*)memory;
    return (*mm == val) && memcmp(mm, mm + 1, size - 1) == 0;
}

memcmp的标准没有说明重叠内存区域。

答案 1 :(得分:17)

明显便携,高效的方法是:

char testblock [fixedElementSize];
memset (testblock, 0, sizeof testblock);

if (!memcmp (testblock, memoryBlock + elementNr*fixedElementSize, fixedElementSize)
   // block is all zero
else  // a byte is non-zero

大多数实现中的库函数memcmp()将使用最大,最有效的单位大小来进行大多数比较。

为了提高效率,请不要在运行时设置testblock

static const char testblock [100];

根据定义,静态变量会自动初始化为零,除非有初始化程序。

答案 2 :(得分:4)

AFAIK没有自动检查内存的功能。

你可以使用|加速for循环,不需要“==”

char *elementStart = memoryBlock + elementNr*fixedElementSize;
char special = 0;
for ( size_t curByteNr=0; curByteNr<fixedElementSize; ++curByteNr )
{
  special |= (*(elementStart+curByteNr));
}

也可以使用更长的速度

char *elementStart = memoryBlock + elementNr*fixedElementSize;
long special = 0;
for ( size_t curByteNr=0; curByteNr<fixedElementSize; curByteNr += sizeof(long) )
{
   special |= *(long*)(elementStart+curByteNr);
}

警告:上述代码未经过测试。请先测试它,以便sizeof和cast操作符

答案 3 :(得分:3)

我无法相信没有人发布这个......实际上看起来像C ++的解决方案并不是用于破坏别名规则的UB:

#include <algorithm> // std::all_of
#include <cstddef>   // std::size_t

// You might only need this
bool
memory_is_all_zeroes(unsigned char const* const begin,
                     std::size_t          const bytes)
{
    return std::all_of( begin, begin + bytes,
                        [](unsigned char const byte) { return byte == 0; } );
}

// but here's this as a bonus
template<typename T_Element, std::size_t T_count>
bool
array_is_all_zeroes( T_Element const (& array)[T_count] )
{
    auto const begin = reinterpret_cast<unsigned char const*>(array);
    auto const bytes = T_count * sizeof(T_Element);

    return memory_is_all_zeroes(begin, bytes);
}

int
main()
{
    int const blah[1000]{0};

    return !array_is_all_zeroes(blah);
}

这可能无法满足一些人对效率的假设(这只是假设,直到分析),但我认为有效和惯用的代码对它有利。

答案 4 :(得分:2)

无法同时检查所有100个字节。因此,您(或任何实用程序函数)必须在任何情况下迭代数据。但是,除了步长大于1个字节之外,您还可以进行更多优化:例如,只要找到非零值,就可以break。好吧,时间复杂度仍然是 O(n),我知道。

答案 5 :(得分:1)

我已经测试了这里提出的一些解决方案,并检查了memcmp source code,它没有针对OP需求进行优化,因为它还需要执行排序,导致它比较unsigned char一个一个人。

在下文中,我提出了一个优化函数check_memory_zeroed,它对可用的最大对齐int执行大部分检查,使其可移植,并将其与此线程中提出的其他解决方案进行比较。执行时间测量并打印结果。

它表明,所提出的解决方案比wallyk的明显的便携式高效率方法好近两倍,并且不需要创建额外的数组,并且比char更好地比char更好六倍或者mihaif的移位数组,与wallyk相比,可以节省RAM。

我还测试了我的解决方案而没有对齐单词check_memory_zeroed_bigestint_not_aligned,令人惊讶的是,它的表现更好。如果有人有解释,欢迎他。

以下是在1Gb表上进行功能和性能测试的代码(建议的优化函数是fisrt one:check_memory_zeroed):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#include <time.h>

#define BIG_TAB_SIZE 1000000000

typedef intmax_t biggestint;

int check_memory_zeroed (void* ptr, size_t size)
{
    if (ptr == NULL) return -1;
    int bis = sizeof(biggestint);
    char* pc = (char*) ptr;
    biggestint* pbi0 = (biggestint*) pc;
    if ((size_t) pc % bis) /* is aligned ? */
        pbi0 = (biggestint*) (pc + (bis - ((size_t) pc % bis))); /* minimal pointer larger than ptr but aligned */
    assert ((size_t) pbi0 % bis == 0); /* check that pbi0 is aligned */
    for (char* p = pc; p < (char*) pbi0; p++)
        if(*p) return 0; /* check beginning of non aligned array */
    biggestint* pbi = pbi0;
    biggestint* pbiUpper = ((biggestint*) (pc + size)) - 1;
    for (;pbi <= pbiUpper; pbi++)
        if(*pbi) return 0; /* check with the biggest int available most of the array : its aligned part */
    for (char* p = (char*) pbi; p < pc + size; p++)
        if(*p) return 0; /* check end of non aligned array */
    return 1;
}

int check_memory_zeroed_bigestint_not_aligned (void* ptr, size_t size)
{
    if (ptr == NULL) return -1;
    biggestint* pbi = (biggestint*) ptr;
    biggestint* pbiUpper = ((biggestint*) (((char*) ptr) + size)) - 1;
    for (;pbi <= pbiUpper; pbi++)
        if(*pbi) return 0; /* check with the biggest int available most of the array, but without aligning it */
    for (char* p = (char*) pbi; p < ((char*) ptr) + size; p++)
        if(*p) return 0; /* check end of non aligned array */
    return 1;
}

int check_memory_zeroed_by_char (void* ptr, size_t size)
{
    if (ptr == NULL) return -1;
    for (char* p = (char*) ptr; p < ((char*) ptr) + size; p++)
        if(*p) return 0;
    return 1;
}

/* variant of wallyk solution */
int check_memory_zeroed_by_memcmp_and_testblock (void* ptr, size_t size)
{
    void* testblock = malloc(size);
    if (ptr == NULL || testblock == NULL) return -1;
    memset (testblock, 0, sizeof(testblock));
    int res = ! memcmp (testblock, ptr, size);
    free (testblock);
    return res;
}

/* variant of mihaif solution */
int check_memory_zeroed_by_memcmp_with_shifted_array (void* ptr, size_t size)
{
    if (ptr == NULL) return -1;
    char* pc = (char*) ptr;
    return (*pc) || memcmp(pc, pc + 1, size - 1);
}

int test() {
    /* check_memory_zeroed (void* ptr, size_t size) */
    char tab[16];
    for (int i = 0; i < 8; i++)
        for (int j = 0; j < 8; j++) {
            for (int k = 0; k < 16; k++) tab[k] = (k >= i && k < 16 - j) ? 0 : 100 + k;
            assert(check_memory_zeroed(tab + i, 16 - j - i));
            if (i > 0) assert(tab[i-1] == 100 + i - 1);
            if (j > 0) assert(tab[16 - j] == 100 + 16 - j);
            for (int k = i; k < 16 - j; k++) {
                tab[k] = 200+k;
                assert(check_memory_zeroed(tab + i, 16 - j - i) == 0);
                tab[k] = 0;
            }
        }
    char* bigtab = malloc(BIG_TAB_SIZE);
    clock_t t = clock();
    printf ("Comparison of different solutions execution time for checking an array has all its values null\n");
    assert(check_memory_zeroed(bigtab, BIG_TAB_SIZE) != -1);
    t = clock() - t;
    printf ("check_memory_zeroed optimized : %f seconds\n",((float)t)/CLOCKS_PER_SEC);
    assert(check_memory_zeroed_bigestint_not_aligned(bigtab, BIG_TAB_SIZE) != -1);
    t = clock() - t;
    printf ("check_memory_zeroed_bigestint_not_aligned : %f seconds\n",((float)t)/CLOCKS_PER_SEC);
    assert(check_memory_zeroed_by_char(bigtab, BIG_TAB_SIZE) != -1);
    t = clock() - t;
    printf ("check_memory_zeroed_by_char : %f seconds\n",((float)t)/CLOCKS_PER_SEC);
    assert(check_memory_zeroed_by_memcmp_and_testblock(bigtab, BIG_TAB_SIZE) != -1);
    t = clock() - t;
    printf ("check_memory_zeroed_by_memcmp_and_testblock by wallyk : %f seconds\n",((float)t)/CLOCKS_PER_SEC);
    assert(check_memory_zeroed_by_memcmp_with_shifted_array(bigtab, BIG_TAB_SIZE) != -1);
    t = clock() - t;
    printf ("check_memory_zeroed_by_memcmp_with_shifted_array by mihaif : %f seconds\n",((float)t)/CLOCKS_PER_SEC);
    free (bigtab);

    return 0;
}

int main(void) {
    printf("Size of intmax_t = %lu\n", sizeof(intmax_t));
    test();
    return 0;
}

结果:

  

intmax_t = 8的大小   不同解决方案执行时间的比较   检查数组的所有值都为null:   check_memory_zeroed优化:0.331238秒   check_memory_zeroed_bigestint_not_aligned:0.260504秒   check_memory_zeroed_by_char:1.958392秒   wallyk检查check_memory_zeroed_by_memcmp_and_testblock:0.503189秒   由mihaif检查check_memory_zeroed_by_memcmp_with_shifted_array:2.012257秒

答案 6 :(得分:0)

我不记得一个标准的库函数可以为你做这个。如果您不确定这会导致任何性能问题我只是使用循环,可能会将char *替换为已建议的int *。

如果你必须进行优化,你可以展开循环:

bool allZeroes(char* buffer)
{
    int* p = (int*)buffer;   // you better make sure your block starts on int boundary
    int acc = *p;
    acc |= *++p;
    acc |= *++p;
    ...
    acc |= *++p;    // as many times as needed

    return acc == 0;
}

如果缓冲区的大小不是sizeof(int)的倍数,则可能需要为缓冲区末尾添加特殊处理,但是如果将一些填充字节设置为0,则分配稍大的块可能更有效。

如果您的块很大,您可以将它们视为一系列较小的块并循环遍历它们,使用上面的代码为每个小块。

我很想知道此解决方案与std::upper_bound(begin,end,0)memcmp的对比情况。

修改

快速检查一下本土实现与memcmp的比较,使用VS2010。

简而言之:

1)在调试模式下,本地生成的速度可以是memcmp的两倍

2)在具有完全优化的版本中,memcmp在以非0开头的块上具有边缘。随着零填充前导码的长度增加,它开始丢失,然后以某种方式神奇地变得几乎和本土的一样快,大约只慢10%。

因此,根据您的数据模式和优化需求/愿望,您可以通过推出自己的方法获得额外的性能,但memcmp是一个相当合理的解决方案。

将代码和结果放在github上,以防你可以使用它们。

答案 7 :(得分:0)

以下内容将遍历结构的内存。 唯一的缺点是它进行逐字节检查。

#include <iostream>

struct Data { int i; bool b; };

template<typename T>
bool IsAllZero(T const& data)
{
    auto pStart = reinterpret_cast<const char*>(&data);
    for (auto pData = pStart; pData < pStart+sizeof(T); ++pData)
    {
        if (*pData)
            return false;
    }

    return true;
}

int main()
{
    Data data1;// = {0}; // will most probably have some content
    Data data2 = {0}; // all zeroes

    std::cout << "data1: " << IsAllZero(data1) << "\ndata2: " << IsEmptyStruct(data2);

    return 0;
};

答案 8 :(得分:-1)

如果您只想确定单个元素是否全是0,您可以create a 100byte element with all 1s。现在,当你想检查一个元素是否全是0只是binary AND (&)元素的内容和你创建的元素(全1)。现在if the result of binary AND is zero您检查的元素全部为0,否则不是全部为

创建一个包含所有1的100字节元素似乎很昂贵,但如果要检查大量元素,那么它实际上更好

你可以用void *elem; elem=malloc(100);创建所有1的100字节元素 现在将所有位设置为1(使用~(elem&0)

答案 9 :(得分:-1)

如何使用long int和二进制or运算符。

unsigned long long int *start, *current, *end, value = 0;
// set start,end
for(current = start; current!=end; current++) {
value |= *current;
}
bool AllZeros = !value;