「C言語/関数」の版間の差分

削除された内容 追加された内容
Ef3 (トーク | 投稿記録)
{{Nav}}
タグ: 2017年版ソースエディター
Ef3 (トーク | 投稿記録)
タグ: 2017年版ソースエディター
794 行
 
 
== ブロックスコープ ==
== ネストのローカル変数とグローバル変数 ==
複合文( Compound statement )は、ブロックとも呼ばれ、宣言や文を1つの構文単位にまとめたものです。自動保存期間を持つオブジェクトの初期化子や、ブロックスコープを持つ通常の識別子の可変長配列宣言子は、宣言が実行順に到達するたびに、あたかも文のように評価されてオブジェクトに値が格納され(初期化子のないオブジェクトに不確定な値が格納されることも含む)、各宣言内では宣言子が現れる順に評価されます<ref name="jtc1-sc22-wg14-n2596-6.8">{{cite book
なんらかの処理の中で、if文やfor文も使わずに <code>{ }</code> だけで処理をまとめた場合、その動作は難しい。
| url = http://www.open-std.org/jtc1/sc22/wg14/www/docs/n2596.pdf
 
| title = N2596 working draft — December 11, 2020 ISO/IEC 9899:202x (E)
一例として、Linuxでgccおよびclangを標準C言語としてコンパイルした場合、WindowsのVisual C++ で標準C言語のコードを下記のように書いてコンパイル下場合、ともに下記の動作になる。
| page = 122, §6.8 ''Statements and blocks''
 
| publisher = ISO/IEC JTC1/SC22/WG14}}</ref>。
 
本節では便宜上、<code>{ }</code> カッコだけで処理をまとめたものを「ネスト」と呼ぼう。「ネスト」とは「入れ子」の意味である。
 
 
;ネスト中で再宣言の場合
ネスト中で変数を再宣言した場合、その変数はローカル変数的に振る舞うのが、パソコン環境では一般的である。
 
:コード
<syntaxhighlight lang="C">
#include "stdio.h"
#include "string.h"
 
int main() {
 
int a = 3;
 
printf("ネストの前 %d \n", a);
 
{
int a = 10; // int で再度、宣言
printf("ネストの内部 %d \n", a);
}
 
printf("ネストを抜けた %d \n", a);
 
}
</syntaxhighlight>
 
結果
<pre>
ネストの前 3
ネストの内部 10
ネストを抜けた 3
</pre>
:(※ 動作確認: GCCおよびClangで、Fedora32上で2020年6月25日に動作を確認)
:(※ 動作確認: Windows の Visual Studio 2019でも2020年6月25日に動作を確認。 ただしC++なので <code> #include <iostream> </code> が必要。)
 
なおWindows版のclangはvisual studio の言語処理系を呼び出す方式であるので、試行を省略する。
 
 
;ネスト中で再宣言しない場合
ネスト中で変数を再宣言しない場合、その変数はグローバル変数的に振る舞うのが、パソコン環境では一般的である。
 
:コード
<syntaxhighlight lang="C">
#include "stdio.h"
#include "string.h"
 
int main() {
 
int a = 3;
 
printf("ネストの前 %d \n", a);
 
{
a = 10; // int が無くても実行可能
printf("ネストの内部 %d \n", a);
}
 
printf("ネストを抜けた %d \n", a);
 
}
</syntaxhighlight>
 
結果
<pre>
ネストの前 3
ネストの内部 10
ネストを抜けた 10
</pre>
:(※ 動作確認: GCCおよびClangで、Fedora32上で2020年6月25日に動作を確認)
:(※ 動作確認: Windows の Visual Studio 2019でも2020年6月25日に動作を確認。 ただしC++なので <code> #include <iostream> </code> が必要。)
 
 
ネストを抜けた3行目が「10」に置き換わっている事に注目しよう。
 
 
;解説や背景事情など
 
C++ では、構造体やクラスで定義できる「メンバ関数」を用いて、関数の内部に関数を定義する事が可能である。
 
 
それらC++クラスのメンバ関数を使うと、関数のなかで別の関数(メンバ関数である)を、(C++では)定義することができ、しかも動作が(C++のメンバ関数では)保証されている。
 
一方、標準C言語には、そのようなメンバ関数が無い。標準C言語の構造体のメンバに、関数を登録することは不可能である。
 
 
また、パソコンが発展してどんなに高性能になっても、標準C言語はパソコン以外の制御用マイコンチップなどの組み込みOSなどにも用いられる事があるため、C言語の規格では必要最低限の機能しか搭載していない。
 
LinuxやBSDなどのオープンソースOS用の言語処理系であるGCCやClang などで、あるコードの動作が確認できても、あくまでパソコン用のOSでだけ動作が確認されたにすぎず、はたしてそのコードがマイコン組み込みOS上のC言語でも動作するかは未知数である。
 
 
2010年以降、モダンなプログラム言語には、関数の中で関数を定義する機能が搭載されているモノも、実は多い。(ただし、比較的に新しい機能であるので、市販の入門書には紹介されていない。)
 
C言語言語処理系およびC++言語処理系は、他の多くの言語の言語処理系やインタプリタを作るための道具でもあるので、そういったモダンな別言語との互換性も考えて、実は規格にはない機能も、実装された言語処理系には搭載されている場合もある。
 
だが、言語処理系の種類ごとに、規格にない機能が異なる場合もあるので、互換性などの観点からは、あまり規格外の機能は用いないほうが安全である。
 
== 脚註・出典など ==