【问题标题】:Can we assign a value to a given memory location?我们可以为给定的内存位置分配一个值吗?
【发布时间】:2012-03-11 11:46:21
【问题描述】:

我想将一些值(例如 2345)分配给内存位置(例如 0X12AED567)。这个可以吗?

也就是说,如何实现以下功能?

void AssignValToPointer(uint32_t pointer, int value)
{

}

【问题讨论】:

  • *(int*)0x12AED567 = 2345 但你最好知道你在做什么......
  • 除非您确定内存未使用,否则最好不要尝试
  • 不仅不能将内存用于任何其他目的,而且还必须进行映射。至少在 *nix 上,如果您尝试写入未映射的页面,您的应用程序会出现段错误并崩溃。
  • @jweyrich 他们赞成它,因为他们是桌面程序员。
  • @Mysticial 帖子对我来说似乎很好。在桌面应用程序中几乎没有听说过像这样的显式内存位置,但对于具有内存映射外围寄存器的 uController 来说几乎是必需的。因此,如果您阅读了冗长的用户手册并理解了晦涩难懂的外设寄存器内容,那么您就会知道自己在做什么(有时,无论如何)。

标签: c memory pointers


【解决方案1】:

您提出这个问题的事实表明您已经不知所措了。但是给你:

*(int *)0x12AED567 = 2345;

【讨论】:

  • @jweyrich 是的。我在我的 Teensy 板上的 ATMEGA32U4 CPU 上运行它。不过它并没有起到多大作用。为什么要问?
  • 我在我的测试程序中确实使用了 0 而不是大写的 O 作为十六进制数字的第一个字符。这就是你所担心的吗?
  • 我没有对 Gopinath 的环境做出任何假设。我的代码是一种分配给硬编码内存地址的方法。分配的效果(例如成功、无操作、SIGBUS)取决于系统,但如果需要,我会让 Gopinath 询问它们。
  • 这在通用 C 程序中非常好(尽管字面量应该写成0x12AED567)。它对平台没有任何假设。某些操作系统可能不允许直接寻址,但 是系统特定的假设,与通用的标准 C 程序无关。
  • @Lundin - 是的,问题标记为 C、mmory、指针。没有提及操作系统、CPU、平台。
【解决方案2】:

答案取决于一些因素。您的程序是否在现代操作系统中运行?

如果是,尝试访问未映射的内存区域将导致SIGSEGV。为此,您必须使用系统特定的函数来映射包含该确切地址的内存区域,然后再尝试访问它。

【讨论】:

  • 您假设它在特定系统上运行。据我所知,SIGSEGV 只发生在基于 POSIX 的操作系统中。
  • 我不假设特定的操作系统。但是,我假设现代操作系统实现了 POSIX 信号,或者至少对其信号使用相同的术语——这对于 Windows 来说是正确的。这就是第一行问题的原因。
  • en.wikipedia.org/wiki/List_of_real-time_operating_systems。在 PC 编程分支之外还有一个完整的世界。
  • 公平点 :) 但除非 OP 披露他正在使用的系统/环境,否则我将避免进一步讨论这个问题。对于投反对票的人:如果您有什么要补充的,或者甚至想修正我使用的术语,请随时编辑。
【解决方案3】:

只需将内存位置视为指针

int* pMemory =  OX12AED567;
*pMemory = 2345;

注意:仅当您的程序可以访问和写入该内存位置时,这才有效。像这样写入任意内存位置本质上是危险的。

【讨论】:

  • 如果这个内存区域没有被映射,那会导致SIGSEGV
  • 如果地址不在进程内存的范围内,会不会崩溃?
  • @Gopinath 它依赖于实现,但一般来说它会崩溃。
  • @jweyrich 我很确定世界上任何嵌入式系统上都没有 SIGSEGV 这样的东西。我相当肯定在 90% 的操作系统中也不存在这种情况。
  • @Lundin:我不知道 OP 是否在谈论嵌入式系统。你?我所知道的所有 Unix/Linux 都使用SIGSEGV。它由 POSIX 定义,也被微软在其操作系统上采用。据我所知,这些占所有现有操作系统的 90% 以上。
【解决方案4】:

C99 标准草案

如果没有实现定义的行为,这可能是不可能的。

关于演员表:

*(uint32_t *)0x12AED567 = 2345;

C99 N1256 standard draft“6.3.2.3 指针”说:

5 整数可以转换为任何指针类型。除先前规定外, 结果是实现定义的,可能没有正确对齐,可能不指向 引用类型的实体,并且可能是陷阱表示。 56)

GCC 实施

GCC 将其 int 指向指针的实现记录在:https://gcc.gnu.org/onlinedocs/gcc-5.4.0/gcc/Arrays-and-pointers-implementation.html#Arrays-and-pointers-implementation

如果指针表示小于整数类型,则从整数到指针的转换丢弃最高有效位,如果指针表示大于整数类型,则根据整数类型的符号进行扩展,否则位不变.

所以转换将按预期工作。我希望其他编译器也能做类似的事情。

mmap

在 Linux 上,您可以请求分配特定的虚拟内存地址](How does x86 paging work?),第一个参数为mmapman mmap 读取:

如果 addr 为 NULL,则内核选择创建映射的(页面对齐的)地址;这是创建新映射的最便携的方法。如果 addr 不为 NULL,则内核将其作为关于在何处放置映射的提示;在 Linux 上,内核将选择一个附近的页面边界(但总是高于或等于 /proc/sys/vm/mmap_min_addr 指定的值)并尝试在那里创建映射。如果那里已经存在另一个映射,内核会选择一个新地址,该地址可能依赖于提示,也可能不依赖于提示。新映射的地址作为调用的结果返回。

所以你可以请求一个地址并断言你得到了你想要的。

【讨论】:

    【解决方案5】:

    就 C 而言,这是未定义的行为。我的以下建议也是未定义的行为,但避免了所有基于类型和基于别名的问题:使用字符。

    int a = get_value();
    char const * const p = (const char * const)&a;
    char * q = (char *)0x12345;
    
    memcpy(q, p, sizeof(int));
    

    或者,您可以直接访问字节q[i]。 (这是 UB 的部分:指针 q 不是作为实际对象的地址或作为分配函数的结果获得的。有时这是可以的;例如,如果你正在编写一个独立的在实模式下运行并访问图形硬件的程序,您可以直接在众所周知的硬编码地址处写入图形内存。)

    【讨论】:

    • 哦,哎呀,好的。将需要进行微不足道的修改,编辑。
    【解决方案6】:

    您已经指出,该地址是一个物理地址,并且您的代码正在一个进程中运行。

    如果你是

    1. 在某种高级操作系统中,例如Linux,您必须映射到物理地址空间。在 Linux 中,/dev/mem 会为您执行此操作。

    2. 在内核中或没有操作系统且使用 MMU 时,您必须将物理地址转换为虚拟地址。在 Linux 内核中,phys_to_virt() 会为您完成这些工作。我假设在内核中,这个地址总是被映射的。

    3. 在内核或没有操作系统和没有 MMU 的情况下,您可以直接写入该地址。根本不需要考虑映射。

    现在,您有一个有效的映射或物理地址本身,您可以将其传递给您的函数。

    void AssignValToPointer(uint32_t pointer, int value)
    {
        * ((volatile int *) pointer) = value;
    }
    

    您可能需要添加 volatile 关键字,因为如果您之后不从该位置读取,编译器可能会优化写入操作(可能是写入内存映射硬件的寄存器时的情况)。

    您可能还想使用 uintptr_t 数据类型而不是 uint32_t 作为指针。

    【讨论】:

      【解决方案7】:

      附带条件是它不便携或不安全(根本):

      *((int *)0x12AED567) = 2345;
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2017-05-30
        • 2020-10-04
        • 2020-05-16
        • 1970-01-01
        • 1970-01-01
        • 2013-11-16
        • 1970-01-01
        • 2014-12-21
        相关资源
        最近更新 更多