断言在C中的memset之后检查

pav*_*thi 1 c

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

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

assert(ptr == 0); 
Run Code Online (Sandbox Code Playgroud)

ste*_*esu 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);
}
Run Code Online (Sandbox Code Playgroud)

编辑

在发布这个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
Run Code Online (Sandbox Code Playgroud)

注意编译代码如何直接引用内存地址.这就是编译器通过允许我们为变量赋值来保存我们的.

使用指针,我们不是将数据存储到RAM中,而是将存储器地址存储到RAM中 - 然后我们必须检查此存储器地址以查找实际数据.这就好比如你去了街道123街的朋友家,发现门口有一张纸条上写着"抱歉,我感动了.你可以在987 Boulevard Avenue找到我".那么你去987 Boulevard Avenue与你的朋友举办一个有趣的聚会.因为那是他的真实地址.

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

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

  • *:间接操作(我最近学到的也称为解引用操作符).这告诉编译器"跟随指针".所以,如果你有一个int*被叫,a那么*a会说"存储在一个存储器中的整数值是多少?" (令人困惑,我知道)
  • &:我想这会被称为"参考"运算符?我总是称它为"内存地址运算符".如果将变量传递给此运算符,它将返回存储该变量的内存中的位置.如果要创建指向已有变量的指针,这非常有用
  • []:偏移运算符.这告诉编译器,而不是去在指针引用的内存位置,去一个地方的记忆刚刚过去的那个.就好像你在朋友家里看到那个"刚刚移动"的笔记,而不是去他的新房子,你去了3个门的房子.这在处理数组时非常有用.我们将20个整数存储在内存中.你有一个指向第一个整数的指针,所有其他整数只是第一个整数的偏移量([1]在起始值之后是一个整数,在起始值之后[2]是两个整数,等等)

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

void*另一方面,没有类型信息.你实际上告诉编译器它应该不知道该指针的另一端是什么.有时这很有用,但它通常只会使代码变得更难,因为它需要编译器为您完成的所有工作并强制程序员执行此操作.