假设地址0xCF800000可以自由写入:
A)说两个代码产生相同的结果是否正确?
int main( void )
{
volatile unsigned long *pt = (volatile unsigned long *) 0xCF800000;
*pt = 0x00000000;
}
和
int main( void )
{
(*(volatile unsigned long *) 0xCF800000) = 0x00000000;
}
B)在第一个代码上,声明" (volatile unsigned long *)"在需要0xCF800000之前还是冗余?
C)在第一个代码上有一个变量pt,它有自己的地址,我在其中放了一些内容:0xCF800000。通过解除引用pt,计算机将获取pt(0xCF800000)的内容,' locate'该地址,并将0x00000000分配给该位置。在第二个代码上,我无法确切地理解它是如何工作的,因为没有变量。看起来信息0xCF800000是"无处"。
答案 0 :(得分:0)
你进行了转换为(unsigned long *),编译器将此值视为指向unsigned long的指针并且不要抱怨。 但正如@ user3386109所说,检查自己总是好的。
答案 1 :(得分:0)
你实际上发了3个问题,每个问题需要一个单独的答案,所以:
关于A:
取决于您写入此内存位置的值(原文如此!)。
如果这是零或许多小值,那么你是安全的。 但如果此值的最重要部分为1
,和,如果由于某些原因而进行符号扩展(取决于您的演员阵容,请参阅我回答你的问题B) - 然后你会遇到问题。
为了避免这些问题,你应该用u
后缀值来表示它是无符号的(再次,如果它是零则不重要)。
因此,如果你要存储的常数值是0xA0000000(注意最重要的位是1
!)你应该把它写成:0xA0000000u而不是。
BTW:0x00000000只是0,您不需要在其中写入那么多的零,但我知道这是较长代码的一部分,您希望与其他更长的值保持一致 < / p>
答案 2 :(得分:0)
你实际上发了3个问题,每个问题需要一个单独的答案,所以:
关于B:
代码:(volatile unsigned long *)
是演员。它的必要性取决于用于在编译器中存储地址的整数类型的大小(编译器应将此类型typedef
编辑为size_t
。
简而言之:
只要此文字常量与编译器使用的地址一样多,即如果编译器使用32位地址,那么没有强制转换就可以了。
如果你的编译器使用超过32位的地址,那么你需要一个强制转换。这起作用,因为您将此文字常量取消引用作为地址(使用一元*
运算符)。在这种情况下,请记住像0xCF800000这样的文字常量将从当前的32位扩展。因为0xCF800000
的最高位是'1'所以这将导致所谓的签名扩展(前导'1'将被复制到最高位),所以你将得到一个不同于你想要的内存地址!
另一个问题是,如果您需要演员中的volatile
。在某些特定情况下,您只需要volatile
,即某些其他代码/设备可以同时使用内存位置。见Why is volatile needed in C?
答案 3 :(得分:0)
关于第二个代码,我无法确切地理解它是如何工作的,因为没有变量。看起来信息0xCF800000是“无处”。
解决你的问题C):这是一个数字文字,后来由你投射到指针。在大多数计算机系统中,只要(0xCF800000)将文本编译成立即数,而解除引用指针值就是内存读取指令。
让我们看一下你在x86上编译的第二个代码示例:
.section __TEXT,__text,regular,pure_instructions
.macosx_version_min 10, 11
.globl _main
.align 4, 0x90
_main: ## @main
.cfi_startproc
## BB#0:
pushq %rbp
Ltmp0:
.cfi_def_cfa_offset 16
Ltmp1:
.cfi_offset %rbp, -16
movq %rsp, %rbp
Ltmp2:
.cfi_def_cfa_register %rbp
xorl %eax, %eax
movl $3481272320, %ecx ## imm = 0xCF800000
movl %ecx, %edx
movq $0, (%rdx)
popq %rbp
retq
.cfi_endproc
特别是三行
movl $3481272320, %ecx ## imm = 0xCF800000
movl %ecx, %edx
movq $0, (%rdx)
将一个常数值读入寄存器,然后将0写入其地址与该寄存器值相同的存储器。
答案 4 :(得分:0)
你实际上发了3个问题,每个问题需要一个单独的答案,所以:
关于C:
你问怎么样:
(*(volatile unsigned long *) 0xCF800000) = 0x00000000;
工作,但我相信你知道这一点,你只是不知道你碰巧知道。 ;-)
上述说明只是一项任务。要使工作分配,必须首先评估其左侧和右侧
右侧的各种有效表达是巨大的。左侧有许多限制,因为它必须评估到内存位置。此类内存位置的示例为int a
,因此您可以编写:a = 0x00000000
另一个内存位置是int tab[N]
,因此您可以写:tab[0]= 0x00000000
;
另一个例子是int* p
,所以你可以写:*p = 0x00000000
如您所见,我们非常接近您的问题:如果您同意扩展到内存地址的*p
可以在赋值指令的左侧使用,为什么不应该是常量?登记/>
一个常数在这里完全没问题,所以你甚至可以写得很简单:
*(int*)0 = 5
这意味着:取值为零,然后将其转换为(=对待它)一个带int*
的地址,然后使用一元*
运算符取消引用该地址,然后将整数值5赋予此值记忆位置。
当然,我的上一个例子假设内存位置0是可写的。另外我在这个帖子中关于位扩展的前两个答案应该在这里应用,我现在正在跳过强制转换