9

私は Linux と C について深く掘り下げてきましたが、関数がどのようにメモリに格納されるのかに興味があります。私は次の機能を持っています:

void test(){
    printf( "test\n" );
}

十分に単純です。この機能を持つ実行可能ファイルで objdump を実行すると、次のようになります。

08048464 <test>:
 8048464:       55                      push   %ebp
 8048465:       89 e5                   mov    %esp,%ebp
 8048467:       83 ec 18                sub    $0x18,%esp
 804846a:       b8 20 86 04 08          mov    $0x8048620,%eax
 804846f:       89 04 24                mov    %eax,(%esp)
 8048472:       e8 11 ff ff ff          call   8048388 <printf@plt>
 8048477:       c9                      leave
 8048478:       c3                      ret

これはすべて正しく見えます。興味深いのは、次のコードを実行するときです。

int main( void ) {
    char data[20];
    int i;    
    memset( data, 0, sizeof( data ) );
    memcpy( data, test, 20 * sizeof( char ) );
    for( i = 0; i < 20; ++i ) {
        printf( "%x\n", data[i] );
    }
    return 0;
}

私は以下を取得します(これは正しくありません):

55
ffffff89
ffffffe5
ffffff83
ffffffec
18
ffffffc7
4
24
10
ffffff86
4
8
ffffffe8
22
ffffffff
ffffffff
ffffffff
ffffffc9
ffffffc3

memset( data, 0, sizeof( data ) ); を除外することを選択した場合 行の場合、右端のバイトは正しいですが、先頭の 1 が残っているものもあります。

誰にも理由の説明がありますか

A) memset を使用して配列をクリアすると、関数の表現が正しくなくなります (編集: 不正確)。

解決策: memset( data, 0, 20 * sizeof( unsigned char ) ) ではなく memset( data, 0, sizeof( data ) ) を使用したことが原因でした。配列全体のサイズよりもポインターのサイズのみを調べていたため、メモリが完全に設定されていませんでした。

B) メモリに格納されているこのバイトは何ですか? 整数?チャー?ここで何が起こっているのかよくわかりません。(明確化:メモリ内のそのようなデータをトラバースするためにどのタイプのポインタを使用しますか?)

解決策: 私はばかです。unsigned キーワードを忘れてしまいました。それが問題全体の原因です:(

どんな助けでも大歓迎です-これを探し回っても何も見つかりませんでした。

ニール

PS: これは、x86 の命令がバイト境界またはハーフバイト境界で終わらないことが原因であるとすぐに思いました。しかし、それはあまり意味がなく、問題が発生することはありません。

char 型のエラーを指摘してくれた Will に感謝します。unsigned char である必要があります。ただし、個々のバイトにアクセスする方法についてはまだ興味があります。

4

5 に答える 5

6

あなたcharsは整数の幅に符号拡張されていると思います。値を出力するときに値を明示的にキャストすることで、希望に近い結果が得られる場合があります。

于 2012-12-31T20:41:10.183 に答える
4

これは、実行しようとしたコードのはるかに単純なケースです。

int main( void ) {
    unsigned char *data = (unsigned char *)test;
    int i;    
    for( i = 0; i < 20; ++i ) {
        printf( "%02x\n", data[i] );
    }
    return 0;
}

私が行った変更は、余分なバッファを削除し、代わりにポインタを使用してテストし、charの代わりにunsigned charを使用し、printfを「%02x」を使用するように変更して、常に2文字を出力するようにします[修正されません「負の」数値はffffff89程度として出力されます。これunsignedは、データポインタので修正されます。

x86のすべての命令はバイト境界で終了し、コンパイラーは、効率を上げるために、ブランチターゲットが4、8、または16バイト境界に整列するように追加の「パディング命令」を挿入することがよくあります。

于 2012-12-31T20:46:35.263 に答える
1

問題は、印刷するコードにあります。

データ配列から1バイトがロードされます。(1バイト== 1文字)

バイトは「int」に変換されます。これは、コンパイラが「printf」が必要としていることを認識しているためです。そうするために、符号はバイトを32ビットのダブルワードに拡張します。それが16進数として出力されるものです。(これは、上位ビットが1のバイトが32ビット値に変換され、ビット8〜31がすべて設定されることを意味します。これが表示されるffffffxx値です。)

この場合、私が行うことは、それを自分で変換することです。

 printf( "%x\n", ((int)data[i] && 0xFF) );

その後、正しく印刷されます。(16ビット値をロードする場合は0xffffとANDします。)

于 2012-12-31T20:42:42.130 に答える
1

B) の答え バイトはバイトとしてメモリに格納されます。メモリ位置に正確に 1 バイトが含まれるメモリ位置 (バイトはunsigned char)

ヒント: コンピューターの構成に関する優れた本を手に取ってください (私のお気に入りは Carl Hamachar の本で、メモリが内部でどのように表現されているかをよく理解しています)。

あなたのコードで:

memset( data, 0, sizeof( data ) );// must be memset(data,0,20);
memcpy( data, test, 20 * sizeof( char ) ); 
for( i = 0; i < 20; ++i ) {
    printf( "%x\n", data[i] );// prints a CHARACTER up-casted to an INTEGER in HEX representation, hence the extra `0xFFFFFF`
}
于 2012-12-31T20:41:02.330 に答える
0

符号付きの値を印刷しているため、印刷が奇妙に見えるため、符号拡張されています。

ただし、印刷される機能も少し異なります。文字列のアドレスをEAXにロードしてスタックに詰め込む代わりに、アドレスが直接保存されているように見えます。

push        ebp  
mov         ebp,esp  
sub         esp,18h  
mov         dword ptr [esp],8048610h  
call        <printf>  
leave  
ret  

コードの他の場所で一見良性の変更を加えたときに変更される理由については、許可されています。そのため、未定義の動作に依存しないのが良いのです。

于 2012-12-31T20:45:59.127 に答える