KDOC 141: Cのポインタ操作をアセンブリで見る
この文書のステータス
- 作成
- 2024-04-29 貴島
- レビュー
- 2024-05-06 貴島
概要
ポインタ型の引数に代入すると、関数をまたいで変更を反映できる(参照渡し)。C言語では返り値を複数返せないので、複数必要なときはこれを使う。
#include <stdio.h>
void inc(int *a) { // アドレスを受け取る
*a = *a+1; // アドレスが参照する値を反映する
}
int main(void){
int a = 0;
inc(&a); // アドレスを渡す
inc(&a);
printf("%d",a); // 2
}
何が起きているのか、生成されたアセンブリを見て確かめる。
アセンブリで見ると
↓こうなっている。即値の代入とじつはあまり変わらない。スタックに入っている値を使うか、スタックのアドレスを使うかだけの違い。
// ↓新しいスタック割当。sp -> スタックの底
addi sp,sp,-32
// ↓元々のレジスタの値をスタックに退避する
sw ra,28(sp)
sw s0,24(sp)
// ↓退避したのでs0を上書きして新しいスタックフレームに移動させる。s0: スタックの一番上、開始アドレス
addi s0,sp,32
// ↓積んだスタックの一番上の**アドレスを**計算してa5に格納
addi a5,s0,-24
// ↓a5(アドレスが入ってる)をスタックに保存
sw a5,-20(s0)
// ↓a5をスタックからロード(storeとloadで、結果としてa5の値は変わらない)
lw a5,-20(s0)
// ↓即値41をa4にロード
li a4,41
// ↓41番地にa5を保存
sw a4,0(a5)
nop
;; レジスタを元に戻す
lw ra,28(sp)
lw s0,24(sp)
addi sp,sp,32
;; 呼び出し先に戻る
jr ra
関連
抽象。
- 違う操作に見えても、内部では扱う対象が値か置き場所かの差しかない
関連。
- 補足: KDOC 129: 『ポインタ理解のためのアセンブリ入門』。Cのプリミティブと生成されたRISC-Vのアセンブリを並べて比べる本
- 検証: KDOC 140: 『自作エミュレータで学ぶx86アーキテクチャ』。出てきた内容を確かめた