为什么我们不能在c或c ++代码中使用直接寻址?

时间:2012-04-26 12:31:35

标签: c++ c pointers

当我连续编译并执行此代码几次时,它会将cc的地址报告为0x0012FF5C。但是当我尝试在foo中使用第二次调用printf打印出该地址的字符串时,它会打印出垃圾而不是打印出“Hello”?为什么这样??当我知道地址位于应用程序的地址空间内时(至少我不重新启动我的PC,或者启动其他需要大量空间的应用程序并导致其他原因)我直接将地址作为参数进行了论证是错误的我的应用程序被分页了?? ??

void foo(char *cc[])
{
    printf("%x\n",cc);
    printf("%s\n",(char *)(0x0012FF5C));
}

int main()
{
    char *c[] = {"Hello","World"};
    foo(c);
}

10 个答案:

答案 0 :(得分:12)

因为C或C ++标准中没有任何内容可以保证这一点。这些地址可能可以根据编译器/操作系统进行预测,但不要依赖它。

#include <stdio.h>

int main(void) {
    char s[] = "okay";
    printf("%p", (void*)s);
    return 0;
}  

我每次都获得一个不同的地址 (linux上的gcc)。不要使用“地址文字”;)

现代操作系统上的进程地址空间随机化,以确保每次执行的安全性:

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

答案 1 :(得分:6)

因为当第二个printf尝试将此char *数组作为字符串打印时,第一个printf为您提供char *数组的地址。

答案 2 :(得分:5)

好的,首先,永远不要假设多次执行会返回相同的地址

现在。让我们说你很幸运并获得相同的地址。请注意,cc是一个指针数组。并且您正在发送此阵列的基地址。您需要发送数组的第一个元素的值

试试这个,如果幸运的话,

printf("%s\n",*((char**)(0x0012FF5C)));

答案 3 :(得分:4)

如果您在没有virtual memorymemory protection的计算机上运行程序,则很可能会成功。这些技术/功能是它不起作用的原因。

每个进程都有自己的虚拟地址空间,处理器的memory-management unit将其地址转换为硬件地址。

答案 4 :(得分:3)

我不认为你的(0x0012FF5C)代表了poiter。尝试(char *)(0x0012FF5C)。

除此之外,像其他人一样告诉你,这没有实际价值,因为每次运行都没有保证字符串位于该地址。这不是一些嵌入式程序集,您可以将此字符串放在此位置,然后直接使用该地址。

答案 5 :(得分:2)

您的进程的内存位置可能在两次执行之间发生了变化。您不能这样做,您正在尝试阅读未由您的流程分配的地址。此外,我有很多警告:

  

test.c:在函数'foo'中:test.c:6:警告:格式'%x'需要类型'unsigned int',但参数2的类型为'char **'

     

test.c:7:警告:格式'%s'需要类型'char *',但参数2的类型为'int'

     

test.c:9:警告:格式'%x'需要类型'unsigned int',但参数2的类型为'char **'

答案 6 :(得分:2)

  

当我知道地址

嗯,当你对地址进行硬编码时,你真的知道关于地址的任何内容,你在猜测

在同一程序的执行之间,事物的地址可能会发生变化的原因有很多。如果您正在更改代码(正如您在此处所做的那样),地址可能会更改的原因更多。

答案 7 :(得分:2)

如前所述,您在内存中的代码位置可能因执行而异,并且变量的位置也会发生相同的情况。尝试运行此代码。它打印您在内存中指向的变量的实际地址。您将看到多次执行会产生完全不同的地址。永远记住!

 #include <stdio.h>

 void foo(char *cc[])
{
    printf("%x\n",cc);
    printf("%s\n",(0x0012FF5C)); //this line will fail, there is no guarantee to that
    cc++;
    printf("%x\n",cc);
}

int main()
{
    char *c[] = {"Hello","World"};
    printf("c array location: %p",c); //print location
    foo(c);
}

答案 8 :(得分:1)

首先,printf("%x\n",cc);不会打印cc的地址。它最好打印cc的值,但行为未定义,因为您为格式%x传递了错误的类型参数。它期望unsigned int,你提供了一个指针。

在实践中最可能的行为是它将打印cc值的最不重要部分(因此在32位机器上它似乎可以工作,而在64位机器上你不会看到整个地址)。但它可能以不同的方式出错。

第二件事,cc的类型为char**,因此cc的值不是指向字符串第一个字符的指针,它是指向第一个元素的指针来自c的数组main%s格式需要指向字符串第一个字符的指针。因此,即使cc的值确实为0x0012FF5C,使用printf格式将该值传递给%s也是错误的。

您看到的“垃圾”是尝试从该指针数组中打印指针数据,就好像它是属于字符串的字符数据一样。事实并非如此。

答案 9 :(得分:0)

我简化了你的例子。如果您知道字符串的地址,则可以从该地址读取。

#include <stdio.h>

void foo(char cc[])
{
    int i;
    printf("%p\n",cc);
    printf("Type the address you would like to read:");
    scanf ("%x",&i);
    printf("Character value at your address: %c\n",*((char *)(i)));
}

int main()
{
    char c[] = "Hello";
    foo(c);
}

这将允许您从命令行中指定的地址进行读取。它将首先打印出字符数组的基址,以便您知道在哪里查找字符串。

示例:

$ ./a.out 
0xbfcaaf3a
Type the address you would like to read:bfcaaf3d
Character value at your address: l

正如预期的那样,它会打印Hello的第四个字母。