指向C中特定地址的指针

时间:2016-05-19 23:37:33

标签: c pointers memory-address

假设地址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是"无处"。

5 个答案:

答案 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是可写的。另外我在这个帖子中关于位扩展的前两个答案应该在这里应用,我现在正在跳过强制转换