Faf*_*Dog 0 c printf pointers casting memset
所以我正在做一个练习,看看我是否正确使用了 memset。
这是我编写的原始代码,它应该将一些地址设置为值 50:
int main(){
int *block1 = malloc(2048);
memset(block1, 50, 10);
// int count = 0;
for (int *iter = block1; (uint8_t *) iter < (uint8_t *)block1 + 10; iter = (int *) ((uint8_t *)iter + 1) ){
printf("%p : %d\n", iter, *iter);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我希望内存中的每个地址都存储值 50。但是我的输出是:
(地址:值)
0x14e008800 : 842150450
0x14e008801 : 842150450
0x14e008802 : 842150450
0x14e008803 : 842150450
0x14e008804 : 842150450
0x14e008805 : 842150450
0x14e008806 : 842150450
0x14e008807 : 3289650
0x14e008808 : 12850
0x14e008809 : 50
Run Code Online (Sandbox Code Playgroud)
我被这个问题困扰了一段时间,并尝试了很多事情,直到我随机决定也许我的指针有问题。然后我尝试了 uint8_t 指针。
int main(){
uint8_t *block1 = malloc(2048);
memset(block1, 50, 10);
for (uint8_t *iter = block1; iter < block1 + 10; iter++ ){
printf("%p : %d\n", iter, *iter);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud)
我所做的就是将 block1 变量和 iter 变量的类型更改为 uint8_t 指针而不是 int 指针,我得到了正确的结果!
0x13d808800 : 50
0x13d808801 : 50
0x13d808802 : 50
0x13d808803 : 50
0x13d808804 : 50
0x13d808805 : 50
0x13d808806 : 50
0x13d808807 : 50
0x13d808808 : 50
0x13d808809 : 50
Run Code Online (Sandbox Code Playgroud)
那么我的问题是,为什么会产生如此大的差异?
那么我的问题是,为什么会产生如此大的差异?
因为指针的确切类型非常重要。C 中的指针不仅仅是内存地址。指针是内存地址,以及预计在该地址找到什么类型的数据的概念。
如果你写
uint8_t *p;
... p = somewhere ...
printf("%d\n", *p);
Run Code Online (Sandbox Code Playgroud)
然后在最后一行中,*p获取 指向的一个内存字节p。
但如果你写
int *p;
... p = somewhere ...
printf("%d\n", *p);
Run Code Online (Sandbox Code Playgroud)
其中,是的,唯一的变化是指针的类型,然后在完全相同的最后一行中,*p现在获取指向的四个p字节的内存,将它们解释为 32 位int。(这假设int您的机器上有四个字节,这在当今很常见。)
当你打电话时
memset(block1, 50, 10);
Run Code Online (Sandbox Code Playgroud)
您要求将内存中的某些(尽管不是全部)单个字节block1设置为 50。
当您使用int指针跨过该内存块,一次获取(正如我们之前所说的)四个字节的内存时,您会得到 4 字节整数,其中每个 4 字节包含值 50。所以您得到的值曾是
(((((50 << 8) | 50) << 8) | 50) << 8) | 50
Run Code Online (Sandbox Code Playgroud)
恰好是 842150450。
或者,换个角度看,如果您将值 842150450 转换为十六进制(以 16 为基数),您会发现它是 0x32323232,其中 0x32 是十六进制值 50,再次表明我们每个字节有四个字节值 50。
现在,到目前为止,这一切都是有道理的,尽管您在第一个程序中如履薄冰。你有过int *iter,但后来你说
for(iter = block1; (uint8_t *) iter < (uint8_t *)block1 + 10; iter = (int *) ((uint8_t *)iter + 1) )
Run Code Online (Sandbox Code Playgroud)
在那个繁琐的增量表达式中
iter = (int *) ((uint8_t *)iter + 1)
Run Code Online (Sandbox Code Playgroud)
您已设法将地址iter仅增加一个字节。通常,我们说
iter = iter + 1
Run Code Online (Sandbox Code Playgroud)
要不就
iter++
Run Code Online (Sandbox Code Playgroud)
这意味着将地址增加iter几个字节,以便它指向int传统数组中的下一个int。
按照您的方式进行操作会产生三个影响:
int大小为 的子块的滑动窗口block1。也就是说,您获取了int由字节 1、2、3 和 4 构成的一个,然后int获取了由字节 2、3、4 和 5 构成的一个,然后获取了int由字节 3、4、5 和 6 构成的一个,依此类推。字节具有相同的值,您总是得到相同的值,但这是一件奇怪且通常毫无意义的事情。int您获取的值中有四分之三未对齐。看起来您的处理器让您摆脱了这种情况,但有些处理器会给您一个总线错误或某种其他类型的内存访问异常,因为并不总是允许未对齐的访问。