このような大規模なプロジェクトを見たことがない初心者にとって、すべてのモジュールとさまざまな定義されたデータ構造を捨ててください。とても面倒で、さまざまなデータ構造の意味を考えるだけで気が狂いそうになります。そうですね、構造が悪いと言っているわけではありません。逆に、コードは素晴らしいと思います。 。しばらくの間だけ。 。受け付けられません。くだらない話はやめて、問題の始まりにしましょう╮(╯▽╰)╭
最初のポイント: ngx_cdecl
<code><span>int</span> ngx_cdecl main(<span>int</span> argc, <span>char</span> *<span>const</span> *argv);</code>
はい、ソース コードに __cdecl や __stdcall のようなものが存在することは以前から知っていました。ここでの ngx_cdecl とは何ですか? 論理的には同じですが、定義を調べてみると次のようになります:
<code><span>#<span>define</span> ngx_cdecl</span></code>
はい、これは空の定義です。もちろん、それは便利であり、非常によく使用されています。そうでなければ、将来的に穴が埋まらないようにするための方法を残しておくのは素晴らしいことです。このマクロは、クロスプラットフォームのサポートと関数呼び出しメソッドの調整を容易にするために nginx で使用されます。問題が発生した場合は、上記の定義を次のように変更できます:
<code><span>#<span>define</span> ngx_cdecl stdcal</span></code>
もっとコードを読むことが有益だと言うのはどうですか、これがアイデアです。さて、cdecl と stdcall について説明しましょう:
__cdecl: C Declaration の略で、C 言語のデフォルトの関数呼び出し方法を示します。すべてのパラメーターはスタックに右から左にプッシュされます。これらのパラメーターは呼び出し側によってクリアされます。これは手動スタック クリアと呼ばれます。呼び出される関数では、呼び出し元が渡すパラメーターの数が多すぎても少なすぎても、あるいはまったく異なるパラメーターを渡しても、コンパイル時エラーは発生しません。
プログラムを正常に実行するには、関数を呼び出すコードと呼び出される関数で同じ関数呼び出し規則を使用する必要があります。
__cdecl と __stdcall の違い: __cdecl は呼び出し元のクリーニング パラメーターによって占有されるスタックであり、__stdcall は呼び出された関数のクリーニング パラメーターによって占有されるスタックです。関数 A が __stdcall で、関数 B が関数 A を呼び出すとします。関数宣言を通じてコンパイラに関数 A が __stdcall であることを伝える必要があります。コンパイラは自然に正しい呼び出しコードを生成します。関数 A が __stdcall であるが、関数 A を参照するときに、関数 A が __cdecl モードであることをコンパイラに伝えると、コンパイラは関数 A の呼び出し規則と矛盾する __cdecl モードのコードを生成し、エラーが発生します。
注: __stdcall の呼び出される関数は、コンパイル中に渡されるパラメーターの正確な数を知っている必要があるため (呼び出される関数はスタックをクリアする必要があります)、printf などの可変パラメーター関数はサポートできません。また、呼び出し元が間違った数のパラメーターを使用すると、スタック エラーが発生します。
2 番目のポイント ngx_int_t & ngx_uint_t
<code><span>typedef</span> intptr_t ngx_int_t; <span>typedef</span> uintptr_t ngx_uint_t;</code>
stdint.h で intptr の定義を見つけます:
<code><span>117</span><span>/* Types for `void *' pointers. */</span><span>118</span><span>#<span>if</span> __WORDSIZE == 64 </span><span>119</span><span># ifndef __intptr_t_defined </span><span>120</span> typedef <span>long</span><span>int</span> intptr_t; <span>121</span><span># <span>define</span> __intptr_t_defined </span><span>122</span><span># <span>endif</span></span><span>123</span> typedef unsigned <span>long</span><span>int</span> uintptr_t; <span>124</span><span>#<span>else</span></span><span>125</span><span># ifndef __intptr_t_defined </span><span>126</span> typedef <span>int</span> intptr_t; <span>127</span><span># <span>define</span> __intptr_t_defined </span><span>128</span><span># <span>endif</span></span><span>129</span> typedef unsigned <span>int</span> uintptr_t; <span>130</span><span>#<span>endif</span></span></code>
定義内のコメントは、ポインターの長さとポインタは配列インデックスであるため、システム カーネルはメモリを大きな配列として扱い、カーネル プログラマはこの特別な整数を使用します。ポインタを使用するよりも、メモリ アドレス値を受け入れてメモリを操作する方が直観的であり、間違いが起こりにくいです。
intptr_t この型は void * と integer の間で安全に変換できます。これは、64 ビット プラットフォームでプログラムを作成する場合に非常に重要です。つまり、ポインターを整数として操作する必要がある場合、それを intptr_t に変換すると、操作の完了後に安全にポインター型に戻すことができ、ポインターの逆参照によって引き起こされるバグを回避できます。
コード内のマクロからわかるように、intptr_t の長さはさまざまなプラットフォームに適応されます。コンパイル環境が 64 ビットの場合、intptr_t は long int で、それ以外の場合は int です。
それでは、なぜそれを nginx の ngx_int_t の typedef に使用したいのでしょうか?なぜ int を使わないのでしょうか?
型名からすると普通の int 型であることが分かりますが、intptr は定義時にプラットフォームに適応し、プラットフォームに応じて長さを変えるので作者が必要としないためだと思います。もう一度定義します。
以上、nginx ソースコードの初読 (1) を紹介しました。内容も含めてメインから始めましょう。PHP チュートリアルに興味のある友人の参考になれば幸いです。