如何检查内存块中的所有字节是否为零
How to check whether all bytes in a memory block are zero
我有一块固定大小的元素,比如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)
实际上可以使用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的标准没有提到任何关于内存区域重叠的问题。
显而易见的便携、高效的方法是:
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];
根据定义,静态变量自动初始化为零,除非有初始化式。
我真不敢相信居然没有人发这个…一个解决方案,实际上看起来像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);
}
这可能不能满足一些人对效率的假设(这只是假设,直到分析),但我认为有效和习惯的代码是非常有利的。
恐怕没有自动检查内存的功能。
您可以使用|来加速for循环,不需要使用"=="
char *elementStart = memoryBlock + elementNr*fixedElementSize;
char special = 0;
for ( size_t curByteNr=0; curByteNr<fixedElementSize; ++curByteNr )
{
special |= (*(elementStart+curByteNr));
}
,也可以使用long来获得更快的速度
char *elementStart = memoryBlock + elementNr*fixedElementSize;
long special = 0;
for ( size_t curByteNr=0; curByteNr<fixedElementSize; curByteNr += sizeof(long) )
{
special |= *(long*)(elementStart+curByteNr);
}
警告:上面的代码没有被测试。请先测试它,以便sizeof和cast操作符工作
我已经测试了这里提出的一些解决方案,并检查了memcmp
源代码,该源代码未针对OP需求进行优化,因为它有额外的要求来执行排序,导致它逐个比较unsigned char
。
在下文中,我提出了一个优化的函数check_memory_zeroed
,它对可用的最大对齐int执行大部分检查,使其可移植,并将其与本线程中提出的其他解决方案进行比较。执行时间测量并打印结果。
结果表明,所提出的解决方案比wallyk的明显的可移植高效方法好近两倍,且不需要创建额外的数组,比char by char比较或mihaif的移位数组比wallyk的节省内存的方法好6倍。
我也测试了我的解决方案,没有对齐单词check_memory_zeroed_bigestint_not_aligned
,令人惊讶的是,它的性能甚至更好。如果有人有解释,欢迎。
下面是在1Gb表上进行功能和性能测试的代码(建议的优化函数是第一个: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 nulln");
assert(check_memory_zeroed(bigtab, BIG_TAB_SIZE) != -1);
t = clock() - t;
printf ("check_memory_zeroed optimized : %f secondsn",((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 secondsn",((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 secondsn",((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 secondsn",((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 secondsn",((float)t)/CLOCKS_PER_SEC);
free (bigtab);
return 0;
}
int main(void) {
printf("Size of intmax_t = %lun", sizeof(intmax_t));
test();
return 0;
}
和不同解决方案的比较结果检查数组的所有值是否为null的执行时间:
- intmax_t = 8的大小
- check_memory_zeroed optimized: 0.331238 seconds
- check_memory_zeroed_bigestint_not_aligned: 0.260504 seconds
- check_memory_zeroed_by_char: 1.958392秒
- check_memory_zeroed_by_memcmp_and_testblock by wallyk: 0.503189秒
- check_memory_zeroed_by_memcmp_with_shifted_array by mihaif: 2.012257秒
不可能同时检查所有100个字节。因此,在任何情况下,您(或任何实用程序函数)都必须遍历数据。但是,除了让步长大于1字节之外,您还可以进行更多的优化:例如,您可以在找到非零值时立即执行break
。我知道,时间复杂度仍然是O(n)
我想不起一个标准库函数可以为您做到这一点。如果你不确定这会导致任何性能问题,我只是使用循环,也许用int*代替char*,正如已经建议的。
如果你必须优化,你可以展开循环:
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上,以防你可以使用它们。
下面的代码将遍历结构体的内存。唯一的缺点是按字节检查。
#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;
};
如何使用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;
如果你只是想决定一个元素是否都是0,你可以create a 100byte element with all 1s
。现在,当您想要检查一个元素是否全部为0时,只需binary AND (&)
元素的内容和您创建的元素(全部为1)。现在你选中的if the result of binary AND is zero
元素全是0否则就不全是0
创建一个全是15的100字节的元素似乎很昂贵,但如果你有大量的元素要检查,那么实际上更好
您可以创建100字节的元素,所有的1s作为void *elem; elem=malloc(100);
现在将所有位设置为1(使用~(elem&0)
)
- 是否可以保证按字节的零 int 是零的表示形式?
- 将对象的字节复制到数组并再次复制回来是否安全
- 是否通过向封闭对象的地址添加字节偏移量来访问子对象
- C++11:16 字节原子<>变量是否在 16 字节边界上自动对齐,从而允许CMPXCHG16B指令?
- 我是否正确打包字节以进行放气压缩?
- 在 c++ 中使用联合时是否可以跳过字节?
- 如果由不同的线程写入 8 字节,那么现代英特尔 x86 上的 8 字节读取是否保证理智?
- 是否强制转换void**并将第一个字节设置为nullptr
- 检查 IP 第一个八位字节是否不以 127 / 224 或 255 开头
- 使用字节数组具有单字节对齐方式的结构是否安全
- 是否值得使用位移在单个字节中存储多个小数据成员?
- 额外的字节是否在C++中初始化为 0?
- 当字节在文件上写入0时,是否可以保持写作时间稳定
- 在字节数组上转换具有虚函数的结构是否安全?
- 将二进制字节 (char*) 转换为 uint64_t*,是否安全
- 位在字节序中是否重要
- 字节序是否会影响写入奇数字节
- x86-64上检查指针范围是否跨越N字节对齐地址的最快方法
- 如何检查准备从windows套接字读取的字节是否超过1个
- 如何检查内存块中的所有字节是否为零