「C言語/中級者向けの話題」の版間の差分

削除された内容 追加された内容
Ef3 (トーク | 投稿記録)
タグ: 2017年版ソースエディター
Ef3 (トーク | 投稿記録)
マークアップ修正
タグ: 2017年版ソースエディター
26 行
for (int i = 0; i < sizeof a / sizeof *a; i++)
</syntaxhighlight>
の <syntaxhighlight lang="c" inline>sizeof a / sizeof *a</syntaxhighlight> は、「配列の要素数」を得る時のイデオムです。
 
{{コラム|配列の一括代入|2=この節で触れたように、C言語では配列同士の代入は出来ません。が、構造体同士の代入はできます。
84 行
</syntaxhighlight>
 
のようにプログラムを実行した場合、example.exeプログラムのmain関数に対して、「example.exe」、「ABC」、「DEF」、「GHI」という4個の文字列が渡される。
example.exeプログラムのmain関数に対して、
「example.exe」、「ABC」、「DEF」、「GHI」という4個の文字列が渡される。
 
 
プログラム仮引数を利用するには、ソースコードのmain関数に
179 ⟶ 176行目:
 
inline関数は、その関数を呼び出した部分に展開して直接埋め込む。
関数呼び出しにかかる処理を短縮することができるが、コードを複数の部分に展開するためファイルサイズが大きくなる。
コードを複数の部分に展開するためファイルサイズが大きくなる。
 
;inline関数の使用例:<syntaxhighlight lang="C">
//例 inline関数の使用例
#include <stdio.h>
 
inline int function(int a, int b)
{
return a + b;
}
 
int main(void){
int r = function(1,2);
r=function(1,2);
}
</syntaxhighlight>
200 ⟶ 194行目:
_Noreturn関数指定子は、関数が呼び出し元に戻らないことを示します。
_Noreturn関数指定子と<stdnoreturn.h>ヘッダファイルは、C11で追加されました<ref>『ISO/IEC 9899:2011』p.91「6.7.4 Function specifiers」</ref>。
:<syntaxhighlight lang="C">
_Noreturn void f (void)
{
210 ⟶ 204行目:
=== 関数の応用 ===
==== 関数へのポインタ ====
関数へのポインタとは、ある関数のメモリアドレスを格納し、その関数に間接的にアクセスする方法です。
関数へのポインタは、次のような場合に使われる。
ある関数のメモリアドレスを格納し、
ひとつは、関数を呼び出す際に、関数へのポインタを引数として渡し、呼び出した関数の内部で、関数へのポインタが指す関数を実行する場合。
その関数に間接的にアクセスする方法です。
もうひとつは、関数へのポインタの配列をつくり、if文や、switch文で関数を呼び出すのをやめ、コードを単純にする場合。
関数へのポインタは、
次のような場合に使われる。
ひとつは、関数を呼び出す際に、
関数へのポインタを引数として渡し、
呼び出した関数の内部で、
関数へのポインタが指す関数を実行する場合。
もうひとつは、関数へのポインタの配列をつくり、
if文や、switch文で関数を呼び出すのをやめ、
コードを単純にする場合。
 
関数へのポインタの宣言の記述は次のようになっている。
229 ⟶ 215行目:
</syntaxhighlight>
 
この宣言では、代入する関数と同じ返却値のデータ型と引数のリストを指定する必要がある。
この宣言では、
また、演算子の優先順位のため、「*関数へのポインタ名」を囲う「()」を省略はできない。
代入する関数と同じ返却値のデータ型と引数のリストを指定する必要がある。
また、演算子の優先順位のため、
「*関数へのポインタ名」を囲う「()」を省略はできない。
 
関数のメモリアドレスを関数へのポインタに格納する記述は次のようになっている。
:<syntaxhighlight lang="C">
 
関数へのポインタは、名 = 関数名;
<syntaxhighlight lang="C">
関数へのポインタ名=関数名;
</syntaxhighlight>
 
関数名の後ろに、「()」はいらないことに注意。
 
関数へのポインタを使って、間接的に関数を呼び出すには、次のように記述します。
間接的に関数を呼び出すには、
次のように記述します。
 
<syntaxhighlight lang="C">
270 ⟶ 251行目:
</syntaxhighlight>
 
;関数へのポインタの配列:<syntaxhighlight lang="C">
//例 関数へのポインタの配列
#include <stdio.h>
 
299 ⟶ 279行目:
</syntaxhighlight>
 
==== 再帰的呼出し ====
再帰(さいき)的呼出しとは、ある関数がその関数自身を呼び出すことです。
 
典型的な用途として、数学の階乗(5×4×3×2×1のような計算)などが、よくあげられる。
 
再帰的呼出しに向いた計算に再帰的呼出しを使うと、ソースコードを簡潔に書ける場合がある。
 
ただし、再帰的呼出しではハードウェア資源的には弱点があり、スタックのためにメモリを多く占有するので負担になるという弱点もある。
そのため、再帰的呼出しで桁数の多すぎる計算などをすると、与えられたスタック領域を使いはたてしまうと異常終了などのエラーを引きおこす場合もある。
これは、いわゆる「スタック・オーバーフロー」(stack overflow)というエラーの一種です。
 
//例 ;再帰を使った階乗の計算:<syntaxhighlight lang="C">
再帰では、
ある関数がその関数自身を呼び出すたびに、
引数と制御が戻るべきアドレス(リターンアドレス)が、
スタックと呼ばれる領域に格納される(プッシュ)。
 
また、ローカル変数もスタックと呼ばれる領域に格納されている。
関数の処理が終わるたびに、
スタックに最後に格納したデータを取り出して(ポップ)、
関数を呼び出したときの状態を復元し、
処理を続ける。<ref>矢沢久雄、原田英生『日経 BP パソコンベストムック C言語とC++がわかる本』日経ソフトウェア、2013年5月15日発行、112項</ref>
 
再帰を行うには、その関数がリエントラントである必要がある。
リエントラントであるためには、使用する変数を共有しないように気を付ける必要がある。<ref>『再入可能とは 「リエントラント」 (reentrant) さいにゅうかのう: - IT用語辞典バイナリ』http://www.sophia-it.com/content/%E5%86%8D%E5%85%A5%E5%8F%AF%E8%83%BD</ref>
 
<syntaxhighlight lang="C">
//例 再帰を使った階乗の計算
#include <stdio.h>
 
339 ⟶ 305行目:
}
</syntaxhighlight>
この例では再帰的呼出しを使って、入力された整数の階乗を計算している。
3を入力した場合、処理の流れは以下のようになる。
 
353 ⟶ 319行目:
</syntaxhighlight>
 
===== 再帰的呼出しを使って2進数表示 =====
;[https://paiza.io/projects/jyBYGklf_W7x1M3gKXjyVA?language=c 再帰的呼出しを使って2進数表示]:<syntaxhighlight lang="C" highlight="6,11"line>
#include <stdio.h>
 
379 ⟶ 345行目:
print_binary は、リエントラントでない関数 puts と putchar を呼び出しているのでリエントラントでは有りませんが、問題なく再帰呼び出しが行なえます。
このように、再帰可能性(Recursivity)と再入可能性(Reentrancy)は別個の概念で、再入可能性(Reentrancy)はマルチスレッドやシグナルハンドラーで問題になります。
<!-- この記事にも、再帰可能性(Recursivity)と再入可能性(Reentrancy)を混同している記述が見られるので修正が必要 -->
 
== 構造体のビットフィールド ==