我有一个void* ptr
而我正在为它做memset
,之后我尝试使用assert()
检查它是否已设置为0,但我得到了Assertion ptr == 0 failed
}
void* ptr = malloc(100);
memset(ptr, 0, sizeof(ptr));
assert(ptr == 0);
答案 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运行 - 但那只是令人困惑)
我在查看我的代码和其他人发布的代码时发现,大多数大学都在教授指针,这导致了很多混乱。所以我认为这个小小的附录可能有所帮助。
首先,基础知识:您可能已经知道数据已写入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);
}