5

このコードを数回連続してコンパイルして実行すると、cc のアドレスが 0x0012FF5C としてレポートされます。しかし、foo で printf への 2 番目の呼び出しを使用してそのアドレスの文字列を出力しようとすると、"Hello" を出力する代わりにガベージが出力されます?? なんでそうなの??アドレスがアプリケーションのアドレス空間内にあることがわかっているときに、アドレスを引数として直接渡すと何が問題になりますか (少なくとも、PC を再起動しないか、多くのスペースを必要とし、原因となる他のアプリケーションを開始しない限り)アプリケーションがページアウトされる)??

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

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

10 に答える 10

12

それを保証する C または C++ 標準には何もないからです。 これらのアドレス、コンパイラ/OS によっては予測できる場合がありますが、当てにしないでください。

#include <stdio.h>

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

毎回異なるアドレスを取得します(Linuxのgcc)。「アドレスリテラル」を使用しないでください ;)

最新の OS のプロセス アドレス空間は、実行ごとにセキュリティのためにランダム化されます。

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

于 2012-04-26T12:37:49.500 に答える
6

最初のprintfは、2番目のprintfがこのchar *の配列を文字列として出力しようとすると、char *の配列のアドレスを提供するためです。

于 2012-04-26T12:43:08.033 に答える
5

OK、最初に、複数の実行が同じアドレスを返すと想定しないでください

今。あなたが幸運で、同じアドレスを取得したとしましょう。ccがポインタの配列であることに注意してください。そして、この配列のベースアドレスを送信しています。配列の最初の要素の値を送信する必要があります

これを試してみて、運が良ければ、

printf("%s\n",*((char**)(0x0012FF5C)));
于 2012-04-26T12:40:09.333 に答える
4

仮想メモリメモリ保護のないマシンでプログラムを実行すると、おそらく成功するでしょう。これらのテクノロジー/機能が機能しない理由です。

各プロセスには独自の仮想アドレス空間があり、そのアドレスはプロセッサのメモリ管理ユニットによってハードウェア アドレスに変換されます。

于 2012-04-26T12:40:56.150 に答える
3

あなたの (0x0012FF5C) はポイターを表しているとは思いません。(char*)(0x0012FF5C) を試してください。

それに加えて、他の人が言うように、実行ごとに文字列がそのアドレスに配置されるという保証がないため、これには実用的な価値はありません。これは、この文字列をこの場所に配置し、後でアドレスを直接使用するという埋め込みアセンブリではありません。

于 2012-04-26T12:38:27.907 に答える
2

住所がわかったとき

アドレスをハードコーディングしているときは、実際にはアドレスについて何も知りません。

同じプログラムの実行間でモノのアドレスが変わる理由はたくさんあります。コードを変更している場合にアドレスが変更される可能性がある理由は他にもあります(ここで行っているようです)。

于 2012-04-26T12:38:37.907 に答える
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);
}
于 2012-04-26T12:39:35.177 に答える
2

プロセスのメモリの場所は、両方の実行間で変更されている可能性があります。それはできません。プロセスによって割り当てられていないアドレスを読み取ろうとしています。さらに、私は多くの警告を持っています:

test.c: 関数 'foo' 内: test.c:6: 警告: 形式 '%x' は型 'unsigned int' を想定していますが、引数 2 の型は 'char **' です</p>

test.c:7: 警告: フォーマット '%s' はタイプ 'char *' を想定していますが、引数 2 のタイプは 'int' です</p>

test.c:9: 警告: フォーマット '%x' はタイプ 'unsigned int' を想定していますが、引数 2 のタイプは 'char **' です</p>

于 2012-04-26T12:36:31.730 に答える
1

まずprintf("%x\n",cc);、のアドレスを出力しませんcc。せいぜい の値を出力しますがcc、 format に間違った型引数を渡したために動作は未定義です%x。それは期待unsigned intしています、あなたはポインタを提供しました。

実際に最も可能性の高い動作は、値の最下位部分を出力することですcc(したがって、32 ビット マシンでは機能しているように見えますが、64 ビット マシンではアドレス全体が表示されません)。しかし、それはさまざまな方法でうまくいかない可能性があります。

2 つ目ccは typechar**であるため、 の値はcc文字列の最初の文字へのポインターではなく、配列cfromの最初の要素へのポインターmainです。この%s形式では、文字列の最初の文字へのポインターが必要です。したがって、 の値が true であっても、ccその値をformat で0x0012FF5Cに渡すのは間違っています。printf%s

あなたが見ている「ガベージ」は、あたかも文字列に属する文字データであるかのように、ポインターの配列からポインターデータを出力しようとする試みです。そうではありません。

于 2012-04-26T12:59:43.457 に答える
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

予想どおり、これは の 4 番目の文字を出力しHelloます。

于 2012-04-26T13:17:17.763 に答える