繁体   English   中英

断言以检查C中的记忆集

[英]assert to check after memset in C

我有一个void* ptr ,我正在为此做memset ,稍后我尝试使用assert()检查它是否已设置为0,但是我Assertion ptr == 0 failed

void* ptr = malloc(100);
memset(ptr, 0, sizeof(ptr));

assert(ptr == 0); 

您遇到的主要问题是void*指针在指针末尾不附带任何有关数据类型的信息。 一个int*指针或一个对象实例的指针将携带其他信息。 但是,编译器不知道对象在指针另一端的大小,也不知道如何取消对指针的引用。

相反,您需要执行以下操作:

void* ptr = malloc(100);
memset(ptr, 0, 100); // sizeof(ptr) == 4 or 8 (32-bit or 64-bit), so you can't use sizeof() here
int* iptr = (int*)ptr; // We tell the compiler that the values at the end of the pointer should be interpreted as integers
for(int i = 0; i < 100; i++) {
    assert(iptr[i] == 0);
}

编辑

发布此内容后,我发现int超过1个字节,因此实际上会声明太多字节。 您只分配了100个,我们在这里检查了400个。

而不是将void*转换为int* ,应该将其转换为char*或类似的1字节类型(或者for循环仅从0到25运行-但这很令人困惑)

编辑2-指针快速入门

通过查看我的代码和其他人发布的代码,我意识到大多数大学在教指针方面做得很糟糕,这导致了很多混乱。 因此,我认为这个较小的附录可能会有所帮助。

首先,基础知识:您可能已经知道数据已写入RAM。 通常,只要记住它在哪里,它在RAM中写的位置就不太重要。 如果我在内存的第103个“插槽”中保持运行总和,那么我只需要记住继续添加到第103个插槽即可。 在C / C ++中命名变量时,编译器会在内存中选择一个随机且未使用的空间。 您正在有效地为该空间创建易于理解的名称。 所以如果我说int a = 5; ,编译器会在内存中随机选择一个位置(也许是第78个插槽),然后每当我在代码中说出a时,编译器就会知道我指的是内存中的第78个插槽。

请注意, 编译器知道我指的是内存中的第78个插槽。 这意味着在编译代码时,源代码中的a将替换为对内存中第78个插槽的引用,并且生成的实际汇编代码如下所示:

// Code:
int a = 5;
a = a + 1;
int b = a;

// Compiles to (this may be invalid x86 assembly - it just serves as an example):
mov 78, 5 -- move the value "5" to memory address 78
add [78], 1, 78 -- add the value stored in memory address 78 and 1, then store the result in memory address 78
mov 79, [78] -- move the value stored in memory address 78 to memory address 79

注意编译后的代码如何直接引用内存地址。 这是编译器通过允许我们为变量分配名称而避免了我们的工作。

使用指针 ,而不是将数据存储到RAM中,我们将内存地址存储到RAM中-然后我们必须检查该内存地址以找到实际数据。 就像您去朋友街123号的家中,在门上发现一张纸条,上面写着“对不起,我搬家了。您可以在987 Boulevard Avenue上找到我”。 因此,您去了987 Boulevard Avenue并与您的朋友举行了有趣的派对。 因为那是他的真实地址。

我们可能使用指针的原因有很多。 我会掩盖使用它们的可能原因,因为最重要的是您知道它们的工作方式和工作方式。 因此,当我们创建指针时,编译器将再次跟踪内存中的插槽,并为其赋予一个易于阅读的名称。 所以当你说int* a = new int; ,则编译器会在内存中随机选择一个未使用的位置(也许是823rd插槽),每当您在代码中说出a时,编译器就会知道您所指的是823rd插槽。 但是 ,实际上在823rd插槽中存储的不是您要查找的整数-它是一个内存地址,您可以在其中找到您真正想要的整数。

在处理指针时,需要记住三个有用的运算符:

  • * :间接操作(我最近了解到的也称为取消引用运算符)。 这告诉编译器“跟随指针”。 因此,如果您有一个称为aint**a会说“位于a中存储的内存地址中的整数值是什么?” (令人困惑,我知道)
  • & :我猜这将被称为“引用”运算符? 我总是将其称为“内存地址运算符”。 如果将变量传递给该运算符,它将返回该变量在内存中的存储位置。 如果要创建指向已经具有的变量的指针,这将很有用
  • [] :偏移量运算符。 这告诉编译器,而不是去在指针引用的内存位置,去一个地方的记忆刚刚过去的那个。 就像您看着朋友家的“搬家”便笺,而不是去他的新房子时,您去了3个门以下的房子。 这在处理数组时非常有用。 我们连续在内存中存储20个整数。 您有一个指向第一个整数的指针,所有其他整数都只是与第一个整数的偏移量( [1]是起始整数后的单个整数, [2]是起始整数后的两个整数,依此类推)

请注意,偏移量运算符仅在知道指针末尾对象的大小时才能工作。 [1]表示如果指向1字节对象数组,则在内存中再增加一个字节。 [1]表示如果指向8字节对象数组,则在内存中再增加8字节。 这就是为什么int*long*不同的原因。 它们都是指针(因此它们都是指向内存中某个位置的整数),但是当您使用偏移量运算符时,编译器将根据变量的类型跳转不同的数量。

另一方面, void* 没有类型信息。 您实际上是在告诉编译器它不应该知道该指针另一端的内容。 有时候这很有用,但通常会使代码更难,因为它需要编译器为您完成的所有工作,并迫使程序员去做。

我假设您正在检查ptr是否为null,但您的目的是检查ptr的值应为零。

尝试这个

assert(*((int *)ptr) == 0);

逐行检查代码:

void* ptr = malloc(100);

您声明了指针ptr ,分配了100个字节并将ptr指向它。

memset(ptr, 0, sizeof(ptr));

sizeof(ptr)是指针的大小,而不是指针指向的已分配内存。 在大多数机器上,这只是4个字节,这意味着您将前4个字节初始化为0,而其余100字节分配的内存将只是垃圾数据。 我提到这一点是因为这可能不是您想要执行的操作。

assert(ptr == 0); 

这是您的主要问题。 如果malloc失败,则ptr将为0,并且在调用memset时会出错。 如果不是,则指针将为非零,并且断言将失败,因为您断言其应为零。

ptr == 0检查指针是否指向NULL ,而不检查ptr指向的内容是否为0。因此,如果内容为0,则必须逐字节检查。

第二个问题是:

memset(ptr, 0, sizeof(ptr));

sizeof 返回的字节数你分配,它返回一个指针的大小。 在这种情况下,您只需将前8个字节设置为0(假设在目标体系结构上,指针的大小为8)。

你必须做

memset(ptr, 0, 100);

因为只有一个void*指针,所以需要将其char*char*unsigned char*以处理值。 您只需要检查0,因此在这种情况下带符号/无符号无关紧要。 你必须做:

// avoid hardcoding numbers, use variables instead
size_t len = 100;

void *ptr = malloc(len);

if(ptr == NULL)
{
    fprintf(stderr, "Not enough memory\n");
    return 0; // or whatever, do not continue
}

memset(ptr, 0, len);

char *base = ptr;
for(size_t i = 0; i < len; ++i)
{
    // checking byte by byte if 0
    assert(base[i] == 0);
}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM