14

在汇编语言中,我们有如下指令:

movl ax, [1000]

这允许我们访问特定的内存位置。

但是在 C 中我们可以做类似的事情吗?

我知道使用内联汇编代码asm()可以让你做到这一点,但我想知道一些 C 特定技术来实现这一点。

我尝试了以下代码并得到分段错误:

int *ptr=0xFE1DB124;
*ptr;

这再次令人困惑,因为内存位置由下面给出的代码标识:

int var;
printf("\nThe Address is %x",&var);

所以内存位置可用,但我仍然遇到分段错误。

为什么?

4

4 回答 4

10

常见的 C 编译器将允许您从整数设置指针并使用它访问内存,它们将为您提供预期的结果。但是,这是 C 标准之外的扩展,因此您应该检查编译器文档以确保它支持它。此功能在必须访问特定地址的内存的内核代码中并不少见。它通常在用户程序中没有用。

正如评论所提到的,您可能遇到的一个问题是您的操作系统每次加载程序时都会将程序加载到随机位置。因此,您在一次运行中发现的地址不会是另一次运行中使用的地址。此外,更改源和重新编译可能会产生不同的地址。

为了演示您可以使用指针访问以数字形式指定的地址,您可以检索该地址并在单个程序执行中使用它:

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


int main(void)
{
    //  Create an int.
    int x = 0;

    //  Find its address.
    char buf[100];
    sprintf(buf, "%" PRIuPTR, (uintptr_t) &x);
    printf("The address of x is %s.\n", buf);

    //  Read the address.
    uintptr_t u;
    sscanf(buf, "%" SCNuPTR, &u);

    //  Convert the integer value to an address.
    int *p = (int *) u;

    //  Modify the int through the new pointer.
    *p = 123;

    //  Display the int.
    printf("x = %d\n", x);

    return 0;
}

显然,这在普通程序中没有用;这只是一个演示。只有当您有特殊需要访问某些地址时,您才会使用这种行为。

于 2013-03-26T13:55:07.750 回答
6

为了从用户空间访问特定内存,我们必须使用mmap()将内存地址映射到程序虚拟地址,下面的 C 代码显示了实现:

获取一个包含“ABCDEFGHIJ”的文件“ test_file ”。

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <fcntl.h>

int main(void)
{
    char *map_base_addr;  // Maping Base address for file
    int fd;         // File descriptor for open file
    int size = 10;

    fd= open("test_file", O_RDWR);  //open the file for reading and writing
    map_base_addr= mmap(NULL, size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);// Maping file into memory

    char *ch= map_base_addr;
    int i;

    /*Printing first 10 char*/
    for(i=0; i<size; i++)
            fputc(*(ch+i),stdout);
    printf("\n");

    *(ch+1) = 'b';
    *(ch+4) = 'z';
    *(ch+7) = 'x';

    /*Printing char after modification*/
    for(i=0; i<size; i++)
            fputc(*(ch+i),stdout);
    printf("\n");
    /* Finally unmap the file. This will flush out any changes. */
    munmap(map_base_addr, size);
    exit(0);
}

输出将是:

ABCDEFGHIJ
AbCDzFGxIJ
于 2016-12-14T12:13:12.967 回答
3

这个对我有用:

#include <stdio.h>

int main(int argc, char**argv) {
  int var = 7456;
  printf("Adress of var = %x, var=%d\n", &var, var);
  int *ptr = (int*)0x22cd28;
  printf(" ptr points to %x\n", ptr);
  *ptr = 123;
  printf("New value of var=%d\n", var);
  return 0;
}

程序输出:

Adress of var = 22cd28, var=7456
 ptr points to 22cd28
New value of var=123

笔记:

  1. 每次执行时地址通常都不相同。当我尝试我的示例时,我必须运行它三遍才能获得匹配的地址。

  2. char*可以指向任何地址(因为 sizeof (char) = 1)。指向较大对象的指针通常必须在偶数地址上对齐(通常可以被 4 整除)。

于 2013-03-26T13:36:20.240 回答
2

如果您在 linux/windows/mac/whatever 上运行,您的问题并没有多大意义

http://en.wikipedia.org/wiki/Virtual_memory

只有在对没有虚拟内存的设备进行编程,或者在对操作系统本身进行编程时,才能这样做。

否则,您看到的地址不是 RAM 上的“真实”地址,操作系统会将它们转换为真实地址,如果没有映射将您的虚拟地址转换为真实地址,那么您可能会遇到分段错误。请记住,还有其他可能导致分段错误的原因。

于 2013-03-26T13:40:49.367 回答