具有内存映射I/O的C指针的奇怪行为

具有内存映射I/O的C指针的奇怪行为,c,pointers,kernel,osdev,C,Pointers,Kernel,Osdev,我目前正在写一个基本的操作系统作为一个学习项目。为此,我使用GCC4.9.2交叉编译器。 在尝试使用内存映射I/O时,我偶然发现了C指针的行为,或者可能是我无法理解的内存映射I/O。 当通过下面的代码直接访问I/O内存时,我得到了预期的结果,即左上角的一个AB,黑色背景上的浅灰色字体 *((uint16_t *)0xB8000) = 0x0741; *((uint16_t *)0xB8002) = 0x0742; 然而,当试图通过使用向基址添加2的偏移量来操纵内存时,结果并不像预期的那样是在第

我目前正在写一个基本的操作系统作为一个学习项目。为此,我使用GCC4.9.2交叉编译器。 在尝试使用内存映射I/O时,我偶然发现了C指针的行为,或者可能是我无法理解的内存映射I/O。 当通过下面的代码直接访问I/O内存时,我得到了预期的结果,即左上角的一个AB,黑色背景上的浅灰色字体

*((uint16_t *)0xB8000) = 0x0741;
*((uint16_t *)0xB8002) = 0x0742;
然而,当试图通过使用向基址添加2的偏移量来操纵内存时,结果并不像预期的那样是在第二个位置,而是在第三个位置

*((uint16_t *)0xB8000 + 2) = 0x0741;
添加1而不是2会打印出第二个位置的字母,但我不明白为什么。由于MMIO中的每个字母都由2个字节的数据组成,因此我假设需要将下一个字符写入的内存地址增加2。正如我一开始直接写入0xB8002所做的那样 为了尝试和理解这种行为,我在一个单独的C程序中进行了一些测试,但无法复制这种行为: 注意:此代码是使用普通GCC4.8.2编译的

#include <stdint.h>
#include <stdio.h>

void main(void) {
        printf("0xB8000 + 1 = %x\n", 0xB8000 + 1);
        printf("&(*(uint16_t *)(0xB8000 + 1)) = %x\n", (uint32_t)&(*(uint16_t *)(0xB8000 + 1)));
}
我假设我丢失了C指针的某种行为,这种行为会导致分解由语句编写的数据大小。是这样还是我忽略了另一个原因

关于,Kenshooak

如果向指针添加整数n,则地址将增加sizeof*n字节。 在你的第一个例子中,你有

*((uint16_t *)0xB8000 + 2) = 0x0741;
所以这里发生的事情是:0xB8000被强制转换为unit16\u t*,然后指针增加2,这意味着uint16\u t=4字节的2*sizeof

在你的第二个例子中,你有

 *(uint16_t *)(0xB8000 + 1)
请注意不同的括号:这里您首先将1添加到0xB8000(一个简单的整数操作)并强制转换结果。

如果您将整数n添加到指针,则地址将按sizeof*n字节递增。 在你的第一个例子中,你有

*((uint16_t *)0xB8000 + 2) = 0x0741;
所以这里发生的事情是:0xB8000被强制转换为unit16\u t*,然后指针增加2,这意味着uint16\u t=4字节的2*sizeof

在你的第二个例子中,你有

 *(uint16_t *)(0xB8000 + 1)

请注意不同的括号:在这里,您首先将1添加到0xB8000(一个简单的整数运算)并强制转换结果。

重新读取指针算术基础知识(始终按基础类型缩放)可能会使您受益匪浅。或者,运算符优先级强制转换的优先级可能高于+。此外,您可能希望在此处使用volatile以确保编译器不会尝试智能化并优化赋值。重新读取指针算术基础知识(始终按基础类型缩放)可能会使您受益。或者可能运算符优先级强制转换的优先级高于+。此外,您可能希望在此处使用volatile以确保编译器不会试图变得聪明并优化赋值。非常感谢,我完全没有注意到不同的括号可能会对结果产生影响!非常感谢,我完全没有注意到不同的括号可能会对结果产生影响!