断言在C中的memset之后检查

时间:2018-04-05 00:28:45

标签: c

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

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

assert(ptr == 0); 

4 个答案:

答案 0 :(得分:5)

您遇到的主要问题是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号&#34;插槽中保留一笔运行金额&#34;在内存中,我只需要记住继续添加到第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&#34;找到我。那么你去987 Boulevard Avenue与你的朋友举办一个有趣的聚会。因为那是他的真实地址。

我们可能会使用指针的原因有很多。我会掩饰使用它们的可能原因,因为重要的是你知道它们做了什么以及它们如何工作。因此,当我们创建指针时,编译器再一次跟踪内存中的一个插槽,并且我们给它一个人类可读的名称。所以当你说int* a = new int;时,编译器在内存中选择一个随机的未使用位置(可能是第823个插槽),并且只要你在代码中说a,编译器就会知道你指的是第823个插槽。 但是,实际存储在第823个广告位中的是您正在寻找的整数 - 它是您可以在哪里找到的内存地址找到你真正想要的整数。

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

  • *:间接操作(我最近学到的也称为解除引用操作符)。这告诉编译器&#34;按照指针&#34;。因此,如果您有一个名为int*的{​​{1}},那么a会说&#34;存储在?&#34;内存地址中的整数值是多少? (令人困惑,我知道)
  • *a:我想这会被称为&#34;参考&#34;运营商?我总是把它称为&#34;内存地址运算符&#34;。如果将变量传递给此运算符,它将返回存储该变量的内存中的位置。如果要创建指向已有变量的指针
  • ,这非常有用
  • &:偏移运算符。这告诉编译器,不要转到指针中引用的内存位置,而是转到内存刚刚过去的位置。如果你看那个&#34;只是感动&#34;请注意你朋友的房子,而不是去他的新房子,你去了3个门的房子。这在处理数组时非常有用。我们将20个整数存储在内存中。你有一个指向第一个整数的指针,所有其他整数只是第一个整数的偏移量([]是一个整数后的单个整数,[1]是起始一个后的两个整数,等等)

请注意,偏移运算符只有在知道指针末尾对象的大小时才能工作。如果你指向一个1字节对象的数组,[2]意味着在内存中再进行一个字节。如果你指向一个8字节对象的数组,[1]意味着在内存中进一步传输8个字节。这就是[1]int*不同的原因。它们都是指针(因此它们只是一个指向内存中某个位置的整数)但是当你使用偏移运算符时,编译器会根据变量的类型跳转不同的数量。

另一方面,

long*具有类型信息。你真的告诉编译器它应该不知道该指针另一端的内容。有时候这很有用,但它通常只会让代码变得更难,因为它需要编译器为你完成的所有工作,并迫使程序员去做。

答案 1 :(得分:1)

你正在检查ptr是否为空,但你的意图是检查ptr的值应为零,我假设。

试试这个

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

答案 2 :(得分:1)

逐行浏览代码:

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时会出错。如果没有,则指针将为非零,并且断言将失败,因为您断言它应该为零。

答案 3 :(得分:1)

ptr == 0检查指针是否指向NULL,它不会检查 ptr指向的内容是否为0.所以你必须逐字节检查if 内容为0。

第二个问题是:

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

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

你必须做

memset(ptr, 0, 100);

因为您只有void*指针,所以需要将其强制转换为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);
}