在汇编语言中,我们有这样的指令:
movl ax, [1000]
这允许我们访问特定的内存位置。
但是在C中我们可以做类似的事情吗?
我知道使用asm()
的内联汇编代码将允许您这样做,但是我想知道一些特定于C的技术来实现这一点。
我试了下面的代码,得到分割错误:
int *ptr=0xFE1DB124;
*ptr;
这又是令人困惑的,因为内存位置是由下面给出的代码标识的:
int var;
printf("nThe Address is %x",&var);
所以内存位置是可用的,但我仍然得到一个分段错误。
为什么?
普通的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 = %dn", x);
return 0;
}
显然,这在普通程序中是没有用的;这只是一个示范。只有当您有特殊需要访问某些地址时,才会使用这种行为。
为了从用户空间访问特定内存,我们必须使用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
它适合我:
#include <stdio.h>
int main(int argc, char**argv) {
int var = 7456;
printf("Adress of var = %x, var=%dn", &var, var);
int *ptr = (int*)0x22cd28;
printf(" ptr points to %xn", ptr);
*ptr = 123;
printf("New value of var=%dn", var);
return 0;
}
程序输出:
Adress of var = 22cd28, var=7456
ptr points to 22cd28
New value of var=123
注意:
每次执行时地址通常不相同。当我尝试我的例子时,我必须运行它三次才能得到匹配的地址。
char*
可以指向任何地址(因为sizeof (char) = 1)。指向较大对象的指针通常必须在偶数地址(通常是1能被4整除)上对齐。
如果你在linux/windows/mac/其他系统上运行,你的问题真的没有多大意义
http://en.wikipedia.org/wiki/Virtual_memory只有在编写没有虚拟内存的设备时,或者编写操作系统本身时,才能这样做。
否则,您看到的地址不是RAM上的"真实"地址,操作系统将它们转换为真实地址,如果没有映射将您的虚拟地址转换为真实地址,那么您可能会遇到分段故障。请记住,还有其他原因可能导致分割错误。