1 D配列パラメータ問題
4149 ワード
1 D配列パラメータ問題は関数に配列を渡すことができますか?例:
コンパイル中に警告が表示されますが、エラーは発生しません.分析:第1:b[10]は存在せず、コンパイル時に実際のアドレスに値を取らなかったためエラーはなかったが、実行時にはb[10]の実際のアドレスが計算され、値が取られる.このとき境界を越えたエラーが発生します. 第2:コンパイラの警告は、コンパイラが必要とするのはchar*タイプのパラメータであり、過去に渡されたのはcharタイプのパラメータであり、fun関数が入力したcharタイプのデータをアドレスとして処理し、同様にエラーが発生することを示しています.
どうすればいいの?
fun(b[10]);fun(b)に変更する.
成功しました.は、1つの配列 を関数に渡すことができません.
完全に検証できます.
配列bが本当に関数内部に渡される場合、iの値は10であるべきである.しかし、私たちはテストした後、iの値が4であることを発見しました.どうしてこんなことになったの?配列bは本当に関数内部に伝達されていませんか?はい、確かに伝達されていません.これは、C言語では、1次元配列が関数パラメータである場合、コンパイラが常にそのヘッダ要素のヘッダアドレスを指すポインタに解析するためです.
このようにするには理由がある.C言語では、すべての非配列形式のデータの実パラメータは、値伝達形式です.(実パラメータをコピーして呼び出された関数に渡すと、関数は実パラメータとしての実際の変数の値を変更することはできず、渡されたコピーのみを変更することができます)呼び出しです.しかし、配列全体をコピーするには、空間的にも時間的にもコストがかかります.さらに重要なのは、ほとんどの場合、全体を必要としません.配列のコピーは、関数がその瞬間にどの特定の配列に興味を持っているかを伝えたいだけです.これにより,時間と空間を節約し,プログラムの実行効率を向上させるために,上記のルールが得られる.同様に、関数の戻り値も配列ではなく、ポインタのみです.ここで明確にする概念の一つは、関数自体にタイプがなく、関数の戻り値だけがタイプがあるということです.
上記の説明を経て、上記の規定とその来由を理解していると信じています.上のコンパイラからのヒントは、関数のパラメータがchar*タイプのポインタであることも理解できると信じています.それならfun関数を次のように書き換えることができます.
同じように、このようなことを試してみることもできます.
運転は全く問題ありません.実際に渡される配列サイズは、関数パラメータで指定された配列サイズとは関係ありません.それなら、次のように書き直すこともできます.
このように書き換えたほうがいいかもしれませんが、少なくとも1つの10要素しか伝わらない配列と誤解されません.
コンパイラは配列のヘッダアドレスをポインタと見なし、ポインタ自体が転送されるとコピーされますが、元のポインタとコピーポインタは同じメモリを指しているので、同じメモリを操作できます(次のポインタパラメータと同じ原理です).
一級ポインタパラメータポインタ変数自体を1つの関数 に渡すことができるかどうか
前のセクションで議論した列を書き直します.
この関数呼び出しは、本当にp 2自体をfun関数の内部に渡したのでしょうか.p 2はmain関数内の局所変数であり,main関数内部でのみ有効であることを知っている.(ここでは、main関数内の変数はグローバル変数ではなくローカル変数であることを明らかにする必要があります.ただし、そのライフサイクルはグローバル変数と同じ長さにすぎません.グローバル変数は関数の外部に定義されているに違いありません.初心者はよくこの点を間違えます.)ローカル変数である以上、fun関数はp 2の本体を使用できないに違いありません.関数呼び出しはどうすればいいですか.実パラメータをコピーし、呼び出された関数に渡します.すなわち、p 2をコピーし、コピー名を_とするp2.それが関数の内部に伝わるのは_p 2はp 2そのものではない.
p 2は本物ではなく、p 2のコピーを再コピーして転送されるが、ポインタp 2とpは「abcdefg」という文字列のヘッダアドレスを指すが、p 2とpのポインタアドレスである&p 2と&pは異なる(pが新たに割り当てられたp 2コピーであることが証明されている).ポインタ変数自体を1つの関数 に渡すことができない.
これは孫悟空がサルの毛を1本抜いて自分の姿になって妖怪をからかうようなものですが(サルが再分配されたことを説明しています)、サルは孫悟空に似ています(サルが孫悟空を指していることを説明しています).だからfun関数の実際の動作では、p 2そのものではなく_p 2という変数が使われています.このように、次の例を見てみましょう(この例も定番のよくある面接問題で、本人は以前何回か面接を受けました):
strcpy(str,“hello”)文を実行中にエラーが発生しました.このときstrの値を観察すると,依然としてNULLであることが分かった.つまりstr自体は変わっていませんが、mallocのメモリのアドレスはstrに割り当てられていません.str.そしてこれはstrはコンパイラによって自動的に割り当てられ、回収され、私たちはまったく使用できません.だから、このようにメモリを取得するのはだめです.どうする?2つの方法:1つ目はreturnを使うことです.
この方法は簡単で、分かりやすいです.第二:二次ポインタで.
ここでのパラメータはstrではなく&strであることに注意してください.これで伝達されたのはstrのアドレスで、値です.関数内部では、キー(「*」)でロックを解除します:*(&str)、その値がstrです.だからmallocが割り当てたメモリアドレスはstr自体に本当に割り当てられています.
参考原文:
http://c.biancheng.net/cpp/html/478.html
void fun(char a[10])
{
char c = a[3];
}
int main()
{
char b[10] = "abcdefg";
fun(b[10]);
return 0;
}
コンパイル中に警告が表示されますが、エラーは発生しません.分析:
どうすればいいの?
fun(b[10]);fun(b)に変更する.
成功しました.
完全に検証できます.
void fun(char a[10])
{
int i = sizeof(a);
char c = a[3];
}
配列bが本当に関数内部に渡される場合、iの値は10であるべきである.しかし、私たちはテストした後、iの値が4であることを発見しました.どうしてこんなことになったの?配列bは本当に関数内部に伝達されていませんか?はい、確かに伝達されていません.これは、C言語では、1次元配列が関数パラメータである場合、コンパイラが常にそのヘッダ要素のヘッダアドレスを指すポインタに解析するためです.
このようにするには理由がある.C言語では、すべての非配列形式のデータの実パラメータは、値伝達形式です.(実パラメータをコピーして呼び出された関数に渡すと、関数は実パラメータとしての実際の変数の値を変更することはできず、渡されたコピーのみを変更することができます)呼び出しです.しかし、配列全体をコピーするには、空間的にも時間的にもコストがかかります.さらに重要なのは、ほとんどの場合、全体を必要としません.配列のコピーは、関数がその瞬間にどの特定の配列に興味を持っているかを伝えたいだけです.これにより,時間と空間を節約し,プログラムの実行効率を向上させるために,上記のルールが得られる.同様に、関数の戻り値も配列ではなく、ポインタのみです.ここで明確にする概念の一つは、関数自体にタイプがなく、関数の戻り値だけがタイプがあるということです.
上記の説明を経て、上記の規定とその来由を理解していると信じています.上のコンパイラからのヒントは、関数のパラメータがchar*タイプのポインタであることも理解できると信じています.それならfun関数を次のように書き換えることができます.
void fun(char *p)
{
char c = p[3];// char c = *(p+3);
}
同じように、このようなことを試してみることもできます.
void fun(char a[10])
{
char c = a[3];
}
int main()
{
char b[100] = "abcdefg";
fun(b);
return 0;
}
運転は全く問題ありません.実際に渡される配列サイズは、関数パラメータで指定された配列サイズとは関係ありません.それなら、次のように書き直すこともできます.
void fun(char a[ ])
{
char c = a[3];
}
このように書き換えたほうがいいかもしれませんが、少なくとも1つの10要素しか伝わらない配列と誤解されません.
コンパイラは配列のヘッダアドレスをポインタと見なし、ポインタ自体が転送されるとコピーされますが、元のポインタとコピーポインタは同じメモリを指しているので、同じメモリを操作できます(次のポインタパラメータと同じ原理です).
一級ポインタパラメータ
前のセクションで議論した列を書き直します.
void fun(char *p)
{
char c = p[3];// char c = *(p+3);
}
int main()
{
char *p2 = "abcdefg";
fun(p2);
return 0;
}
この関数呼び出しは、本当にp 2自体をfun関数の内部に渡したのでしょうか.p 2はmain関数内の局所変数であり,main関数内部でのみ有効であることを知っている.(ここでは、main関数内の変数はグローバル変数ではなくローカル変数であることを明らかにする必要があります.ただし、そのライフサイクルはグローバル変数と同じ長さにすぎません.グローバル変数は関数の外部に定義されているに違いありません.初心者はよくこの点を間違えます.)ローカル変数である以上、fun関数はp 2の本体を使用できないに違いありません.関数呼び出しはどうすればいいですか.実パラメータをコピーし、呼び出された関数に渡します.すなわち、p 2をコピーし、コピー名を_とするp2.それが関数の内部に伝わるのは_p 2はp 2そのものではない.
p 2は本物ではなく、p 2のコピーを再コピーして転送されるが、ポインタp 2とpは「abcdefg」という文字列のヘッダアドレスを指すが、p 2とpのポインタアドレスである&p 2と&pは異なる(pが新たに割り当てられたp 2コピーであることが証明されている).
これは孫悟空がサルの毛を1本抜いて自分の姿になって妖怪をからかうようなものですが(サルが再分配されたことを説明しています)、サルは孫悟空に似ています(サルが孫悟空を指していることを説明しています).だからfun関数の実際の動作では、p 2そのものではなく_p 2という変数が使われています.このように、次の例を見てみましょう(この例も定番のよくある面接問題で、本人は以前何回か面接を受けました):
void GetMemory(char * p, int num)
{
p = (char *)malloc(num*sizeof(char));
}
int main()
{
char *str = NULL;
GetMemory(str,10);
strcpy(str,"hello");
free(str);//free ,
return 0;
}
strcpy(str,“hello”)文を実行中にエラーが発生しました.このときstrの値を観察すると,依然としてNULLであることが分かった.つまりstr自体は変わっていませんが、mallocのメモリのアドレスはstrに割り当てられていません.str.そしてこれはstrはコンパイラによって自動的に割り当てられ、回収され、私たちはまったく使用できません.だから、このようにメモリを取得するのはだめです.どうする?2つの方法:1つ目はreturnを使うことです.
char * GetMemory(char * p, int num)
{
p = (char *)malloc(num*sizeof(char));
return p;
}
int main()
{
char *str = NULL;
str = GetMemory(str,10);
strcpy(str,"hello");
free(str);
return 0;
}
この方法は簡単で、分かりやすいです.第二:二次ポインタで.
void GetMemory(char ** p, int num)
{
*p = (char *)malloc(num*sizeof(char));
return p;
}
int main()
{
char *str = NULL;
GetMemory(&str,10);
strcpy(str,"hello");
free(str);
return 0;
}
ここでのパラメータはstrではなく&strであることに注意してください.これで伝達されたのはstrのアドレスで、値です.関数内部では、キー(「*」)でロックを解除します:*(&str)、その値がstrです.だからmallocが割り当てたメモリアドレスはstr自体に本当に割り当てられています.
参考原文:
http://c.biancheng.net/cpp/html/478.html