📝

「ポインタ渡し」なんてものは存在しません!【C言語入門】

に公開
55
5

C言語の解説には歴史的経緯からか、あまり良くない記述が散見されます。
その中でも特に良くないのが「ポインタ渡し」です。

C言語には「値渡し」しかない

プログラミング言語において、引数の渡し方は2種類あります。

  • 値渡し
    変数の値をコピーして渡します。
    関数内で値が編集されても元の変数の値に変更はありません。
  • 参照渡し
    変数を指し示す参照だけを渡し、変数として振舞わせます。
    関数内で値が変数されると元の変数の値が変更されます。

皆さんがC言語を書いている時に、「変数として振舞う参照」とやらを見たことはないと思います。

「ポインタ渡し」なんてものはない

「でも入門書で『ポインタ渡し』っていうの見たよ! 値渡しでもないし、参照渡しでもないならどうなるの!?」と思うかもしれません。

簡単です。

「ポインタ渡し」なんてものは、C言語の仕様には存在しません

そもそも、ないんです。

「ポインタ渡し」の正体

まず、ポインタというものについて思い出してみましょう[1]

ポインタは変数の場所を指し示す値でしたよね。
そして、ポインタ p* をつける(*p)ことで元の変数を間接参照できるんでした。

ここで、「ポインタ渡し」だと思っていたものを見てみましょう。
まず引数にポインタ p を渡して、関数内では引数として受け取ったポインタ p に、 * をつける(*p)ことで、元の変数を間接参照していました。

……ちょっと待て。ただ、ポインタを間接参照しているだけでは?

だとしたら。渡したのはポインタで、受け取ったのも同じポインタです。
これって、値渡しと何が違うんでしょうか。

ポインタの値渡し

そうです。

今まで「ポインタ渡し」と呼んできたものは「ポインタの値渡し」。
つまりはただ、変数の場所を指し示す値(ポインタ)をコピーして渡しているだけだったのです。

なので、最初に「C言語には『値渡し』しかない」と書いたわけですね。

整数値を「値渡し」するか、ポインタを「値渡し」するか、文字を「値渡し」するか。
C言語は常に「値渡し」しかしないのです。

あとがき

個人的には、初心者に「ポインタ渡し」という用語を使って説明するのは、やめた方がいいと思っています。

理由は簡単で、「ポインタを値渡しすれば元の変数を間接参照して読み書きできる」ということがわからないとしたら、それはポインタ自体を理解していないということに他ならず、逆にわかるのであれば、「ポインタの値渡し」と言うことで一発で過不足なく理解できるからです。

もしそれがわからないのであれば、ポインタについてきちんと理解を深めるべきであって、「ポインタ渡し」という新しい語を使ってごまかすべきではありません。
なぜなら、C言語の本質はメモリを操作すること[2]であり、その過程でポインタの理解と利用は不可欠であるためです。

わかっている人同士の間であれば、「ポインタの値渡し」なんてまどろっこしい言い方は面倒ですし、「ポインタ渡し」と言ってもよいと思います。
便宜上そういった用語が使われる、それ自体は悪いことではありません。

しかし、初心者に「ポインタ渡し」という用語を使うと、ポインタについて学ぶ機会を奪う上に、C言語の引数の仕組みについて間違った認識を植え付けてしまいます
私はこれでだいぶ無駄な時間を過ごす羽目になりました。

もし、今後C言語を誰かに教える機会があるかもしれない、そういった人が読んでくれているなら、なるべく「ポインタ渡し」という用語を避けてほしいな、と思います。

脚注
  1. ポインタ自体がよくわからないという方には、先に私が前に書いた「ポインタが怖くなくなる!C言語メモリ操作の基本概念」を読むことをおすすめします。 ↩︎

  2. それこそ「ポインタが怖くなくなる!C言語メモリ操作の基本概念」の冒頭で書いたことそのままですが…… ↩︎

5

Discussion

ピン留めされたアイテム
めんくりめんくり
  • 記事内で言及していない内容について、言及されたものとして扱うコメント
  • 記事内で言及した内容について、言及されていないものとして扱うコメント
  • 直接の返信コメントで言及していない内容について、言及されたものとして扱うコメント
  • 直接の返信コメントで言及した内容について、言及されていないものとして扱うコメント

これらにつきましては、読者に無用の誤解を与えるため、非表示とさせていただく場合がございます
ご了承ください

また、Zennのコミュニティガイドラインに沿って、不適切と判断したコメントについても同様の対応を行う場合があります

なお、非表示のコメントも含めて返信は行う予定です
感想・質問など、ご自由にお書きください

追記:
そもそもポインタについて理解するのが難しいという場合は、
ポインタが怖くなくなる!C言語メモリ操作の基本概念
をお読みいただければ、わかりやすくなるかと思います

かろしむろかろしむろ

アドレスの概念が無いとポインタの理解は難しいかもね。

めんくりめんくり

個人的にはアドレスというより、メモリ上にデータをどう置くかというイメージがないことが、ポインタの理解を阻んでいるような気がしますね
アドレスという概念を導入しても、どこの何にアドレスが振られているかわかっていない、ポインタがわからない人にはそういう印象を持っています

藤田望藤田望

ポインタはvoid*でない限りはデータ型があるので「変数の場所を指し示す値(ポインタ)をコピーして渡しているだけ」という説明はむしろ誤解を生じる可能性があると思います。

めんくりめんくり

ポインタにはデータ型があることと、変数の場所を指し示す値であるという記述に問題があるという主張には関係はないと思いますが……
ポインタのデータ型とは単に指し示している変数の型のことであるはずなので、「変数の場所を指し示す値(ポインタ)をコピーして渡しているだけ」に間違いはないかと

藤田望藤田望

int*は単純にアドレスを表しchar*はアドレスと何ビット目から始まるかの情報を含むアーキテクチャがあったり、ポインタのアドレス表現方法が複数あるアーキテクチャも存在するので、C言語一般の話として「変数の場所を指し示す値(ポインタ)をコピーして渡しているだけ」という説明は正しくないです。

めんくりめんくり

アドレスの表現方法が複数あることは、アドレスが変数の場所を指し示す値でないことを意味しません
単に同じ変数の場所を指し示す値が複数あるだけです

アドレスが変数の場所を指し示す値であること、それが値渡しにおいてコピーされることは仕様であり、それはどのアーキテクチャでも変わりません

住所の表現方法が日本語と英語で違うことは、住所がその土地を指し示す値であることを否定するものではないことと、全く同じことです
たとえ緯度と経度で表したところで、その土地を指し示す値であることは全く変わりません

そのため、C言語一般の話として「変数の場所を指し示す値(ポインタ)をコピーして渡しているだけ」という説明は全くもって正しいものです

一度、ポインタについて学習し直されることを強くおすすめします
ポインタとはC言語の概念であって、特定のアーキテクチャに依存するアセンブリや機械語の概念ではありません

藤田望藤田望

関数呼び出しは関数原型と引数の型によっては暗黙の型変換(implicit conversion)が行われます。
環境によってはポインタの表現法が複数あるということは、例えばmemsetの第1引数にvoid*以外のポインタ型を与えた場合、暗黙の型変換によりポインタの表現法の変換が行われることとなるので、「変数の場所を指し示す値(ポインタ)をコピーして渡しているだけ」という説明は正しくないです。

Hidden comment
めんくりめんくり

暗黙の型変換は型の問題であって引数渡しの問題ではありませんし、ポインタの表現法が型によって変化するというのも同様に型の問題です
「値渡し」のような引数渡しの問題はそもそも型の問題ではありませんから、「変数の場所を指し示す値(ポインタ)をコピーして渡しているだけ」という説明は正しいものです

ポインタの値渡しについての「変数の場所を指し示す値(ポインタ)をコピーして渡しているだけ」という説明は、それ以前に型変換が発生する可能性を否定しているわけではありません
しかし、暗黙の型変換だけをポインタの引数渡しの際に発生する特殊処理として扱うのは、学習においても規格の理解においても誤りと言えます

まず、引数渡しの際に型が一致しないことで暗黙の型変換が起こることはありますが、引数渡しでないただの変数の代入でも全く同じように型変換が発生します
その上、暗黙の型変換が起こることがあるのは何もポインタに限った話でもなく、数値型でも条件を満たせば暗黙の型変換は発生します
また、たとえばC++ではC言語より暗黙の型変換が許される範囲が少なく、C言語で暗黙の型変換で通していたコードを明示的なキャストによる型変換に置き換える必要がありますが、この記述の変更は挙動の変更を意味しません

上記の例からもわかるように、型変換自体が型の問題であって引数渡しの問題ではありませんし、ポインタに限って暗黙の型変換が発生するわけでもありませんし、暗黙の型変換だけが明示的な型変換と異なる特殊な処理であるわけでもありません
暗黙の型変換とはあくまで一部の型変換を明示的なキャストなしに自動で行うという機能であり、これは引数渡しとは全く別の問題です

めんくりめんくり

そもそも、この記事は「『ポインタ渡し』という独自の挙動はなく『ポインタ』の『値渡し』である」という話をしている記事です
暗黙の型変換がたとえわかりにくい挙動であったとしても、暗黙の型変換について扱うのはこの記事の主題から完全に外れています
なので、そこにレイヤーの異なる型の問題を混同した状態で記述することは、むしろ誤解を招くため行う予定はありません

また、この記事ではC言語の仕様について語っているため、抽象マシンにおいて定義されていない挙動について記述する予定もありません
実際の実装はコンパイラの実装者が決定することであって、C言語の仕様においては言及されていないためです
そのため、この記事においてはポインタを「変数の場所を指し示す値」と表現していますが、「CPUが扱うメインメモリ上のアドレスのビット列」とは表現しておりません

あくまでこの記事の目的はC言語の一仕様の解説であり、関連のある全ての概念から実装、そのハードウェアレベルの挙動についてまでレイヤー横断的に語ることではありませんし、未定義動作がなぜ未定義動作とされているかを解説することでもありませんし、未定義動作が特定の環境向けの特定のバージョンのコンパイラによって、実際にどのように扱われるかということに言及し、未定義動作を引き起こした場合の挙動を保証することでもありません

少なくともこの記事において、安全なコードを書く方法は未定義動作が発生した後の挙動を考えることではなく、未定義動作を発生させないことであり、これはあなたが私が読んでいないのではないかと言っていたC言語の規格においても同様のはずです
特定の環境における未定義動作を発生させた場合のデバッグ方法の解説が不要だと言っているわけではありませんが、それはこの記事ではなくそれをテーマとして扱った他の記事で行われるべきでしょう

一部では「C言語は高級アセンブリだ」というような風説も流布されていますが、C言語は抽象マシンを前提とした高級言語であり、これは特定のアーキテクチャに依存する挙動を未定義とし、高度に抽象化された共通の仕様の上に書かれることを前提としたものです
抽象化は嘘や方便ではなく必要なレイヤーに注目するための隠蔽であり、C言語においてはこれをアーキテクチャ非依存の挙動を実現するために用いています

そのため、抽象化する前のより低レイヤーの内容はアーキテクチャに依存しており、知識としてもC言語のポインタや引数渡しのような高レイヤーではなく、ハードウェアやアセンブリなどの低レイヤーを前提として行われるものであると考えているため、C言語初心者向けの記事(この記事はタイトルに【C言語入門】を冠しており、その点について疑問はないと思います)においては今後も扱う予定はありません

逆に言えば、組み込みでアセンブリとC言語を連携させたいエンジニアは、高レイヤーを扱ったC言語初心者向けの記事で満足している場合ではない、と言うこともできます(低レイヤーを扱いたいのであれば、そんなことは言わなくてもわかっていてほしいところですが)
また、C言語を具体的に特定のアーキテクチャ向けにコンパイルする、実装をすることを考えているエンジニアは、やはりこのような記事で満足していてはいけません

藤田望藤田望

すいませんが非表示に設定されたコメントについて言及されるのやめてもらえませんか?

Hidden comment
Hidden comment
Hidden comment
Hidden comment
Hidden comment
Hidden comment
めんくりめんくり

Zennの仕様上、非表示に設定されているコメントも全員から閲覧可能です
そのため、非表示の場合でも返信を行っています

ただし、そのコメントの内容が読者に無用の誤解を与える可能性があるため、非表示に設定させていただいております
非表示とする条件はこちらに記載しておりますので、あらかじめご確認の上コメントを投稿していただけると幸いです

せぐふぉせぐふぉ

ポインタはvoid*でない限りはデータ型がある

void*は型じゃなかった!?
じゃあコンパイラはどうやって扱うんですか????(型として扱わないとコンパイル通らないので)

厳密には、メモリのサイズの指定(何をどう読み書きするか)があるかどうかでの違いですよね。

アセンブリ言語で言えば
mov dword ptr [ebp-4].eax
のdword ptrがサイズで、ebp-4がアドレス
それらを合わせて型と呼んでいるのでvoid*はこのdword ptrなしのebp-4的なやつを表している型である、と理解するのが順当じゃないの?って個人的には思いますけどね。

めんくりめんくり

C言語のポインタのデータ型は、ただメモリのサイズだけを指定しているわけではないので、やはり指し示している変数の型であると理解した方がよいかと思います
また、アセンブリにおける挙動は実装の参考にはなりますが、規格の理解にはあまり役立たないように思われます(実際、ポインタの型を規格で許容されていないものに変更した場合は未定義動作であり、コンパイラが同じポインタを違う型で読むようにコードを生成するとは限りません)

ただまあ、void* は間違いなく型ですね

藤田望藤田望

ただまあ、void*は間違いなく型ですね

void*はポインタ型ですが指してる先のvoidincomplete typeでありデータ型にはなりえないという話をしています。

めんくりめんくり

どのみち対応するデータ型の有無は、ポインタが値渡しされているかどうかに関係がないので、この記事で扱う内容ではありませんね

砂糖おじさん砂糖おじさん

ガチのCを使うとなるとアセンブリ言語を勉強するのが近道だと思います。しかしCというとC++も含んでいることの多いこの頃、
void swap(int& a, int& b);
参照が使えたりします。
& と *が入り乱れた式を読むとわけわからなくなります。私は参照が嫌いです。

めんくりめんくり

CとC++はC系言語などとまとめられることはありますし、C++はCを含むというような表現もあります(厳密には仕様面で異なる部分も多いんですが、まあ注意すればそういう運用もできなくはないでしょう)が、CにC++を含むことはできないと思います
まあ、そこの話は何より記事の内容に関係がないので掘り下げても仕方ないですね

また、アセンブリを学んでもC言語においては、その環境での実装という限られた範囲の情報しか得ることができません
少なくとも想定したロジック通りに動くようにC言語を書くには、未定義動作というものを知る必要がありますが、アセンブリにC言語の未定義動作は反映されません(必ず特定の形で反映されることが規格で保証されていれば、それは未定義動作ではないので当たり前のことです)
そのため、アセンブリを知ることは実装の理解には役立ちますが、仕様を知るためには意味がありませんし、アセンブリから概念を掴むことができるという意見を完全に否定するわけではありませんが、アセンブリがわかればC言語がわかるということはなく、C言語の仕様もしっかり確認する必要があります

参照渡しについては、これに関しても、好みの問題にはなると思うのであまり掘り下げても仕方ないですが、C++の参照渡しはあまりいい仕様ではないなとは思います
&* が混ざっていようが私としてはあまり気になりませんが(プログラミングをしていれば様々な記号が式に混ざるのはよくあることですし)、ただ、関数を呼び出して引数を渡す時に値渡しでも参照渡しでも全く同じ見た目にしかならないというのは、安全性の面で問題があると思います
その点、Rustの借用は渡す時点で見た目が変わる仕様になっているのがいいですね

Hidden comment
多田夕(おおたゆう)多田夕(おおたゆう)

まあ、「値渡し」と「アドレス渡し」は表現として区別したほうがいいとは思います
その上で「アドレスという値を渡している」と理解できれば、「ポインタのポインタ」も結局「アドレス値渡してるだけじゃん」とスッキリすると思いますし

めんくりめんくり

「ポインタ(アドレス)渡し」という固有名詞は、そういう専用の何かがあるような誤解を与えますが、初学者が理解すべきなのは「ポインタ(アドレス)」と「値渡し」です
なぜなら、「ポインタ(アドレス)渡し」などという専用の挙動はなく、一貫して「ポインタ(アドレス)」を「値渡し」しているだけ、というのが現実だからです

もしそれがわかりにくいというなら、この記事では新しい概念は何も導入されていない(それどころか「ポインタ(アドレス)渡し」という偽の別概念を排除している)ので、「ポインタ(アドレス)」か「値渡し」のどちらかの理解度が足りませんよ、という話をしているということをご理解いただけたらよいのかな、と思います

また、「ポインタのポインタ」(これは一般にダブルポインタと呼ばれます)は「アドレス値渡し」ではないですね
ダブルポインタを理解するには、先にポインタがアドレスを格納した変数としてメモリ上に確保されていることを知る必要があります
そのポインタ変数のアドレスを格納したポインタがダブルポインタ(ポインタのポインタ)です

ポインタについて理解が足りない場合は、「ポインタが怖くなくなる!C言語メモリ操作の基本概念」をお読みいただけるとよいかなと思います

めんくりめんくり

「値渡し」「ポインタ渡し」「参照渡し」とは引数の話です
変数に格納することを指しているわけではありません

Hidden comment
Hidden comment
Hidden comment
寂夜寂夜

ちょっと俯瞰的な1意見です、悪しからず。

整数値も文字もポインタも物理的な実体はありません、実体はメモリーチップの1領域かCPU内部のレジスタ等だけ。そんなものを「渡す」と言うのは、メモリーやレジスタを物理的に引き剝がして渡すのではなく、その内容をコピーして渡すくらいしかない。つまり「値(コピー)渡し」1つしか無いのは確かですね。

人は「取りうる方法が1つしか無い」時にはその事実を省略して推論できます。その省略の上で現在の文脈での実用上の差異が有ればそれを論じられる。「値渡し」「ポインタ渡し」もその文脈で見れば良いかと。

「渡す」対象が大きな構造体や長い文字列であれば、値渡しとポインタ渡しではコピー時間や必要メモリー量が大きく異なり、渡した先で内容改変された場合コピー前の内容にも差異が出る。その実用上の差異で「値渡し」と「ポインタ渡し」の2つの省略語に分類されていると。

「私の愛と真実を貴女に捧げます!」
この告白への返答に
「愛とか真実とか言われる概念を感じている貴方の脳シナプス状態を、私に理解しろと言う事ですか?」
と省略少なく答えるのは粋ではないと思います。

めんくりめんくり

コンピュータのハードウェアレベルの挙動で言えばコピーしかできませんが、この記事でしているのはプログラミング言語のレベルでは「値渡し」と「参照渡し」があり(ハードウェアレベルで言えば、命令の組み合わせが異なってくるということになります)、一方で「ポインタ渡し」というものはないですよ、という話です
上記のように、まずプログラミング言語のレベルにおいては取り得る方法は1つではありません

また、C言語の規格には「ポインタ」を「値渡し」することでオブジェクトを間接参照できる旨の記載があり、「ポインタ渡し」という別の挙動をするとは記載がありません
そのため、「ポインタ渡し」は「値渡し」や「参照渡し」のような、実際に存在する挙動を説明する語のフリをする省略語という、極めて誤解を生みやすいものになっています

実際、「ポインタ渡し」を「値渡し」と完全に違うものとして扱っている入門書や解説記事も多く、これはその誤解をしたままに書いてしまっていることを意味しています
そして、記事で言及したようにこの誤解は「ポインタ」の理解を阻害しており、C言語を書く上で明確に邪魔になっているため、言及することに十分な意味があると考えています

加えて言えば、「ハードウェアレベルになるとコピーしかないから、プログラミング言語においてどう言われていても最終的には一緒だ」という主張もナンセンスでしょう

あなたの言っていることは、

「私の愛と真実を貴女に捧げます!」

に対して

「愛も真実も貴方の脳のシナプス状態だから、最終的には同じですよね」

と言っているのと同じです
そういう主張をするのは、あなたのコメントによれば「粋ではない」ということになるのではないでしょうか

少なくとも、これは「俯瞰的」ではなく、ただの「レイヤーの混同」であると言えます

寂夜寂夜

なるほど、意図はよく分かりました。
C言語の引数の仕組みとしては値渡ししかなく、「ポインタ渡し」という独立した仕組みがあるわけではない、という点には私も異論ありません。

そのうえで、私が言いたかったのは、実務や会話の文脈では「ポインタを渡して、その先の対象を間接的に読み書きする」という使い方を、便宜上「ポインタ渡し」と呼ぶこと自体には一定の実用性もあるのでは、ということです。

もちろん、それを初心者に対して「値渡しとは別の何か」として説明してしまうと誤解の元になる、というご指摘はその通りだと思います。
ただ一方で、呼び方だけを厳密にしても、ポインタや間接参照の理解そのものが伴わなければ、結局どこかで同じ混乱は起きるだろう、とも感じています。

なので私としては、
「Cの仕組みとしてはあくまで値渡し」
「ただし実務上は、ポインタを渡して元の対象に作用させる使い方を略してそう呼ぶことはある」
くらいに整理しておくのが、いちばん穏当なのかなと思いました。

以前の私の例えは少し回り道だったかもしれませんが、言いたかったのは「概念はいつもフルネームで名乗るとは限らない」という程度のことです。
愛も真実も脳シナプス状態に還元できるとしても、告白の返事としてはたぶん少し野暮なので……そのくらいの意味で読んでいただければ幸いです。

めんくりめんくり

まあ、最終的には記事のあとがきに書いた内容が全てです
そのあたりのことは、だいたい全部書いていると思います

Hidden comment
kemiikemii

変数が領域にラベル付けしただけのものということがわかっていれば、ポインタ渡しも理解できるはず。

新しい領域を作成するのではなく、使っていい領域のうち一定領域に名前をつける。その名前をつけた領域の先頭がポインタとして保持されているだけ。

エクセルでいうA1 にcountと命名するようなもの。

A1として書いてもいいし、countと書いてもいい。

ただそれだけな気がします。

めんくりめんくり

変数は領域にラベル付けをしただけのものではなく、型の情報なども保持しています

いずれにせよこの記事で扱っているのは変数やポインタそれ自体の理解ではなく、ポインタを引数に渡す時の問題です
ポインタの変数名をどう考えるかといった問題を扱っているわけではありません

qenginqengin

横から失礼しますが変数はただの名前です.

一度形式的なラムダ計算の基礎の基礎からお勉強なさっては?

Hidden comment
DIODIO

たしかに!
ポインタ渡しではなくポインタを値渡ししてるのか
この説明は「アドレスを渡してるだけ」とは意味していなくて、ポインタそのものを値渡ししてるから型の情報は失われてないのか。
抽象機械を前提に話してるし規格の話だからアセンブリや機械語の話は特定の実装に依存していてレイヤーが違うってことか

Tatsunori UchinoTatsunori Uchino

やってることは値渡しでも低水準表現に直すと(関数内部の処理が特に)途端に参照渡しと区別つかなくなるんですよねこの「ポインタ渡し」
値渡しと参照渡しを中途半端に橋渡しすることによって両者の区別を曖昧にする急所的存在
コイツさえなければ値渡しと参照渡しは全くの別物と簡単に理解できるのに中途半端な挙動見せちゃうから
別物ではなく派生として分類するにも 値渡し→「ポインタ渡し」→参照渡し なのか 値渡し × 参照渡し→「ポインタ渡し」 なのかわからなくなってきますよ

めんくりめんくり

そもそもハードウェアレベルでは「参照渡し」であろうが「ポインタ」の「値渡し」であろうがアドレスのコピーが一般的な挙動となるため、「ポインタ渡し」が「参照渡し」と類似した挙動をするわけではなく、「参照渡し」が「ポインタ」の「値渡し」に変換されるという方が理解として正解に近いものとなります
実際、C++の「参照渡し」はCの「ポインタ」の「値渡し」(ポインタ渡し)と全く同じアセンブリに変換される、ただの糖衣構文です

つまり、「ポインタ渡し」という認識は言語仕様の「ポインタ」の「値渡し」を理解しづらくするだけでなく、ある意味では「参照渡し」のハードウェアレベルでの挙動の理解も阻むものと言えます

そのため、まず「ポインタ渡し」は独自の概念ではなく単なる省略語であり、実態はただの「ポインタ」の「値渡し」であることを認識することこそ、「参照渡し」がどのような実装で実現されているかを理解することに繋がるはずです

Tatsunori UchinoTatsunori Uchino

最初は参照渡しを一旦見なかったことにして

値渡し(全ての基本)

ポインタ/アドレス(値)渡し(別物ではなく応用例

参照渡し(上のアドレス渡しという応用例を、ポインタを完全隠蔽して元の型であるかのように見せかけたもの)

みたいに順番に説明するのがよいのかなと思いました

めんくりめんくり

「参照渡し」はプログラミング言語の仕様上は異なるものであるため、この記事では「値渡し」と異なるものとして軽く触れています
C / C++ はあくまで抽象マシン上で定義された言語仕様を持つ高級言語であるため、その抽象マシンの実装そのものがどうなっているかは、本来はコンパイラの領分です
そのため、C言語の規格について言及する場合は一応は別物ということになります

一方で、実際の実装を気にする場合の解説としては、「値渡し」の説明をした後に「値渡し」の渡す値が「ポインタ」になった応用例を説明し、そしてそれを隠蔽した糖衣構文として「参照渡し」を紹介するのはとてもいい案だと思います

基本的には「ただ変数の中身を書き換えられる形で引数を渡したいというだけで、いちいちポインタという低レイヤーの表現を利用したくない」という要望を叶えるための糖衣構文が「参照渡し」であるため、「ポインタ」の「値渡し」を説明した後に「参照渡し」を説明すれば実装のイメージだけでなく、その具体的なメリット・デメリットも含めて理解しやすくなるでしょうね

qenginqengin

横から失礼しますが何故挙動とか変換とか実装の話をされてるのかわかりません.

構文・意味論・実装と区別ができていません.

仕様や規格に記述されてるのは構文と意味論です.CやC++は非形式的に記述されているので読みにくいですが.

藤田望藤田望

X 3010:2003 (ISO/IEC 9899:1999) のドラフト(https://kikakurui.com/x3/X3010-2003-01.html)では「6.5.2.2  関数呼出し」の注釈で

(78) 関数は,その仮引数の値を変更してもよいが,これらの変更が実引数の値に影響を与えることはできない。一方,オブジェクトへのポインタを渡すことは可能であり,関数はそれによって指されるオブジェクトの値を変更してもよい。

とポインタ渡しについて説明しており、同様に、ISO/IEC 9899:2024のドラフト(https://www.open-std.org/jtc1/sc22/wg14/www/docs/n3220.pdf)でも、 6.5.3.3 Function calls の注釈で

  1. A function can change the values of its parameters, but these changes cannot affect the values of the arguments. On the other hand, it is possible to pass a pointer to an object, and the function can then change the value of the object pointed to.

ポインタ渡しについて説明があります。
この記事の主張は C言語の関数の引数の仕組みはcall by value(値渡し)なのだからポインタ渡しは存在しないというものだと思いますが、規格(のドラフト)ではcall by valueを説明した上でその中の特別の場合としてポインタ渡しを説明している気がしていますがどうでしょうか?

ログインするとコメントできます
5