「C言語/関数」の版間の差分
削除された内容 追加された内容
→構造体を使う方法: s/strcpyは、文字列をコピーするための命令/strcpyは、文字列をコピーするための関数/、Fix typo タグ: 2017年版ソースエディター |
→swap関数: 外部のスコープのインスタンスがより内側のスコープで同じ識別子を持つインスタンスによって隠されてしまう事を、シャドーイング(Shadowing)と呼ぶ。s/#include "stdio.h"/#include <stdio.h>/、{{コラム|新たに変数を用意せず2つの変数を入れ替える }} タグ: 2017年版ソースエディター |
||
646 行
<syntaxhighlight lang="C">
//例 誤ったswap関数
#include
// グロ-バル変数を使って、交換用の変数をswap外部と共有しようと意図しているが・・・
653 行
int b;
void swap(int a, int b)
{ x = a;
a = b;
659 ⟶ 660行目:
}
int main()
{ printf("swap前のa=%d, b=%d\n", a, b);
swap(a, b);
675 ⟶ 678行目:
と表示され、なにも交換されていない。
こう失敗する原因は、呼び出され側のswap()の引数で用いられている値を格納する変数(上記コード例の<code> swap(int a,int b) </code> のaおよびb )
:この様に、外部のスコープのインスタンスがより内側のスコープで同じ識別子を持つインスタンスによって隠されてしまう事を、シャドーイング(Shadowing)と呼ぶ。
▲こう失敗する原因は、呼び出され側のswap()の引数で用いられている値を格納する変数(上記コード例の<code> swap(int a,int b) </code> のaおよびb )は、必ず型をつけて再宣言しないといけないのでローカル変数になってしまうので、その変数をグローバル変数に出来ないから、である。
▲たとえグローバル領域で「a」「b」と同名の変数があっても、swap関数内では同名なだけの別変数としてのローカル変数「a」「b」が新たに関数swap内では用意されてしまう。
ユーザ定義関数内で変数の値を交換させるためには、一例として、下記のようにポインタを使ってmain関数の変数a,bを書き換える方法がある。
687 行
<syntaxhighlight lang="C">
//例 正しいswap関数
#include
void swap(int *a, int *b)
{ int x = *a;
*a = *b;
*b = x;
}
int main()
{ int a = 1, b = 2;
printf("swap前のa=%d, b=%d\n", a, b);
711 ⟶ 712行目:
と表示される。
ポインタを使うと成功する理由は、main
<!-- ポインタを使った例は「参照渡し」ではなく「ポインタの値を用いた値渡し」である。(C言語に参照渡しはない)。これを間違えると C++ で本物の参照渡しに出会ったとき混乱する。
なお、
718 ⟶ 720行目:
:ポインタを使った例「正しいswap関数」のように実引数のアドレスを仮引数にコピーすることを''参照渡し''と呼ぶ。
<ref>値渡しと参照渡しという用語はよく使われるが、『JISX3010:2003』には出てこない。</ref>
----
ユーザ定義関数を何も書かずに全てmain関数に書く方法を使えば、ポインタを使わずとも交換できる。
標準C言語では考える必要は無いが、別のプログラム言語だと、言語によってはポインタが標準設定では使用禁止されていたり(たとえばC#がそうである)、そもそもポインタに相当する機能の無い言語も考えられる<ref>最近は Fortran ですらポインタがあるのでポインタのない言語は非常に限られ、ポインタがなくてもVariant型に類する型がある場合が多い。</ref>。そのような場合でも、下記コードのようなアルゴリズムで、変数の交換は可能である。
<syntaxhighlight lang="C">
//例 swap関数を使わない場合
#include
int main()
{ int a = 1, b = 2;
printf("swap前のa=%d, b=%d\n", a, b);
int x = a;
a = b;
b = x;
748 ⟶ 749行目:
</pre>
この処理を、マクロを使って一般化すると
▲----
<syntaxhighlight lang="C">
//例
#include
#define swap_int(a,b) do {int __temp=a;a=b;b=__temp;}while(0)
int
{
int a = 1, b = 2;▼
}▼
int main(){▼
printf("swap前のa=%d, b=%d\n", a, b);
swap(a, b);▼
swap_int(a,b);
▲ b = gb;
printf("swap後のa=%d, b=%d\n", a, b);
}
</syntaxhighlight>
※ 一度も反復しない<syntaxhighlight lang="C" inline>do { ... } while(0)</syntaxhighlight>は、変数__temp のスコープを切るため。
<pre>▼
swap後のa=2, b=1
</pre>▼
{{コラム|新たに変数を用意せず2つの変数を入れ替える|2=
<syntaxhighlight lang="C">
▲#include "stdio.h"
{
int a = 1, b = 2; /* a の初期値を A、b の初期値を B として... */
▲ int a = 1, b = 2;
printf("swap前のa=%d, b=%d\n", a, b);
a ^= b; /* a = A ^ B */
▲ swap(a, b);
b ^= a; /* b = B ^ A ^ B ... A */
a ^= b; /* a = A ^ B ^ A ... B */
printf("swap後のa=%d, b=%d\n", a, b);
}
</syntaxhighlight>
※ <code>^</code>は、排他的論理和演算子。
結果
▲<pre>
swap
▲}}
▲swap後のa=1, b=2
▲</pre>
=== 関数の引数として配列を渡す ===
|