C/C++関数パラメータのタイプ
12391 ワード
関数のパラメータの問題はずっと1つの難点で、多くの地方が理解していないで多くの問題を招くことができて、だからここで私は関数のパラメータのタイプの関数のパラメータのタイプを総括してあります:値、アドレス、引用はまずmain関数の中で他の関数を呼び出す時、スタックの中で1段の空間を開くことができて、最もスタックに入ったのは現在のコードの次の行のコードのアドレスで、それから順番に形のパラメータで、関数の局部の変数、関数のコードはコードのセグメントで、関数が戻る時、スタックの空間を解放して、最後にスタックを出たのは次の行のコードのアドレスで、またmain関数の中で関数の初めの地方に戻って引き続き実行して、多くの人はこの時1つの問題を提出して、どうしてスタックの中の変数は釈放して、関数はまた戻ることができますか?これは、コンパイラが関数に一時的な変数を生成し、関数の戻り値を格納するために使用するためです.ここでは、後述1について詳しく説明します.値伝達これは実パラメータが値を形パラメータに伝達するので、まず次のプログラムを見てみましょう.
結果は
これは簡単なプログラムで、fun関数の目的はvar++ですが、結果はvar++ではなく、形パラメータa++になります.もし形パラメータ++であれば、私たちのプログラムには何の意味もありません.これは、値が伝達されるときに実パラメータの値が形パラメータに伝達されるだけで、実パラメータで形パラメータを初期化することは、fun関数を呼び出すときに、まずa=varを行うことに相当します.そしてa++に対してvarの値はもちろん変化しない.アドレス転送は上のプログラムに対して書き換えることができます
結果は
アドレス伝達の本質も値伝達であるが、それが伝達するのはアドレス値であり、関数呼び出し時にint*a=&varがあり、変数varのアドレスをポインタaに伝達する.このときポインタaは変数varを指し、*aに対する操作はvarに対する操作であり、関数戻り時にポインタaは解放されるが、このときaのアドレスに戻る
3.参照転送C++では、参照によってvarの値を変更することもできます.
結果は
参照は変数に別名を付けて、実際には直接変数を操作しているので、関数パラメータについて少し理解しましたね.
注意:関数はローカルポインタ変数の値を返すことはできません.関数が戻るとスタックが解放されるので、この言葉は多くの人が誤解しやすいです.私も以前は、ローカル変数を返すだけではだめだと思っていましたが、実際にはそうではありませんでした.
次のコードを見てください
結果は次のとおりです.
ローカル変数ポインタの値を返してはいけないという人もいるのではないでしょうか.でもどうして戻れるの!前述のアドレス転送に戻ると、ポインタの値が転送されます.このときpとstrは同じアドレス空間を指し、関数にはp++後ポインタが1ビット後ろに移動し、文字「2」を指します.戻ると、コンパイラは一時変数を生成してこのときpのアドレスを格納し、ポインタtmpに戻ります.このときtmpが定数領域を指す文字列は、自然に出力できます.注意定数文字列領域の値は読み取り専用で変更できません
次のコードを見てみましょう
結果:
ここでローカル変数ポインタを返すことができるのは,主にポインタに初期化された文字列「abcdefg」が定数領域に格納されているためであり,関数が解放された後に定数領域の内容が解放されていないためである.
もう一度直す
結果を見る
ここでエラーが発生したのは、ローカルポインタ変数のアドレスが返されたためで、文字列もスタックに格納され、関数が返された後、スタック内のものは存在しません.すなわち、この場合、戻りポインタはエラーで、解決策はスタックメモリを割り当てることができ、使用する必要がない場合は手動で解放します.
以上、基礎データ型について説明しましたが、C++では関数パラメータがオブジェクトである可能性があります.
次に、対象の注意すべき状況を見てみましょう.
最初のmain関数で実行した結果は
2番目のmain関数で実行した結果は
ここでplay()関数を呼び出すには、(1)渡されたパラメータが整数の場合、関数スタックでパラメータ付きコンストラクション関数を最初に呼び出し、その後一時変数を生成し、関数が戻るとレプリケーションコンストラクション関数を呼び出し、一時オブジェクトを生成します.最後に、この一時オブジェクトは、関数が戻った後に構造を解析します(2)Bクラスオブジェクトが渡された場合、まずレプリケーション構造関数を呼び出して構造パラメータオブジェクトを初期化します.後のステップと同様に、2つの状況の違いは、異なる構造関数を呼び出して構造パラメータを初期化することです.1つは、パラメータ付き構造関数を呼び出し、1つはレプリケーション構造関数を呼び出すことです.
補足
一時的なオブジェクトとは?一時オブジェクトはどのような状況で発生していますか?一時オブジェクトは見えません.プログラムには表示されません.多くの場合、プログラムの実行効率に影響します.したがって、一時オブジェクトの生成を回避したい場合があります.通常、次の2つの場合に発生します.1.パラメータを値で渡す2.戻り値が値で渡されるのを避けるにはどうすればいいですか?値の代わりに参照転送を行うことができます.参照には実際の参照可能なオブジェクトが必要です.そうしないと、参照は間違っています.
一時オブジェクトを生成するときに、レプリケーションコンストラクタを呼び出す必要がある場合、レプリケーションコンストラクタはどのような場合に呼び出されますか?1.1つのオブジェクトが値伝達方式で関数体2に伝達する.1つのオブジェクトは、関数から3を値で返す.1つのオブジェクトを初期化するには、別のオブジェクトが必要です.
#include
int fun(int a)
{
a++;
printf("a=%d
",a);
return a;
}
int main(int argc, char const *argv[])
{
int var=5;
fun(var);
printf("var=%d
",var);
return 0;
}
結果は
[root@localhost gongxiang]# ./a.out
a=6
var=5
これは簡単なプログラムで、fun関数の目的はvar++ですが、結果はvar++ではなく、形パラメータa++になります.もし形パラメータ++であれば、私たちのプログラムには何の意味もありません.これは、値が伝達されるときに実パラメータの値が形パラメータに伝達されるだけで、実パラメータで形パラメータを初期化することは、fun関数を呼び出すときに、まずa=varを行うことに相当します.そしてa++に対してvarの値はもちろん変化しない.アドレス転送は上のプログラムに対して書き換えることができます
#include
int fun(int *a)
{
(*a)++;
printf("*a=%d
",*a);
return *a;
}
int main(int argc, char const *argv[])
{
int var=5;
fun(&var);
printf("var=%d
",var);
return 0;
}
結果は
[root@localhost gongxiang]# ./a.out
*a=6
var=6
アドレス伝達の本質も値伝達であるが、それが伝達するのはアドレス値であり、関数呼び出し時にint*a=&varがあり、変数varのアドレスをポインタaに伝達する.このときポインタaは変数varを指し、*aに対する操作はvarに対する操作であり、関数戻り時にポインタaは解放されるが、このときaのアドレスに戻る
3.参照転送C++では、参照によってvarの値を変更することもできます.
#include
using namespace std;
int fun(int& a)
{
a++;
printf("a=%d
",a);
return a;
}
int main(int argc, char const *argv[])
{
int var=5;
fun(var);
printf("var=%d
",var);
return 0;
}
結果は
[root@localhost c++]# ./a.out
a=6
var=6
参照は変数に別名を付けて、実際には直接変数を操作しているので、関数パラメータについて少し理解しましたね.
注意:関数はローカルポインタ変数の値を返すことはできません.関数が戻るとスタックが解放されるので、この言葉は多くの人が誤解しやすいです.私も以前は、ローカル変数を返すだけではだめだと思っていましたが、実際にはそうではありませんでした.
次のコードを見てください
#include
using namespace std;
char* fun(char* p)
{
return ++p;
}
int main(int argc, char const *argv[])
{
char* str="1234567"; //“1234567”
//char str[]="1234567"; // str
char* tmp=fun(str); //tmp
printf("tmp=%s
",tmp);
return 0;
}
結果は次のとおりです.
[root@localhost c++]# g++ test.cpp
[root@localhost c++]# ./a.out
tmp=234567
ローカル変数ポインタの値を返してはいけないという人もいるのではないでしょうか.でもどうして戻れるの!前述のアドレス転送に戻ると、ポインタの値が転送されます.このときpとstrは同じアドレス空間を指し、関数にはp++後ポインタが1ビット後ろに移動し、文字「2」を指します.戻ると、コンパイラは一時変数を生成してこのときpのアドレスを格納し、ポインタtmpに戻ります.このときtmpが定数領域を指す文字列は、自然に出力できます.注意定数文字列領域の値は読み取り専用で変更できません
次のコードを見てみましょう
#include
using namespace std;
char* fun()
{
char* p="abcdefg";
return ++p;
}
int main(int argc, char const *argv[])
{
char* tmp=fun();
printf("tmp=%s
",tmp);
return 0;
}
結果:
[root@localhost c++]# ./a.out
tmp=bcdefg
ここでローカル変数ポインタを返すことができるのは,主にポインタに初期化された文字列「abcdefg」が定数領域に格納されているためであり,関数が解放された後に定数領域の内容が解放されていないためである.
もう一度直す
#include
using namespace std;
char* fun()
{
char p[]="abcdefg";
return ++p;
}
int main(int argc, char const *argv[])
{
char* tmp=fun();
printf("tmp=%s
",tmp);
return 0;
}
結果を見る
[root@localhost c++]# g++ test.cpp
test.cpp:14:2: :
test.cpp: In function ‘char* fun()’:
test.cpp:6: :
test.cpp:5: : ‘p’
ここでエラーが発生したのは、ローカルポインタ変数のアドレスが返されたためで、文字列もスタックに格納され、関数が返された後、スタック内のものは存在しません.すなわち、この場合、戻りポインタはエラーで、解決策はスタックメモリを割り当てることができ、使用する必要がない場合は手動で解放します.
以上、基礎データ型について説明しましたが、C++では関数パラメータがオブジェクトである可能性があります.
次に、対象の注意すべき状況を見てみましょう.
#include
using namespace std;
class B
{
public:
B()
{
cout<<"default constructor"<cout<<"destructed"<int i) :data(i)
{
cout<<"constructed by para "<private:
int data;
};
B play(B b)
{
return b;
}
// main
// int main(int argc, char const *argv[])
// {
// B t1=play(5);
// B t2=play(t1);
// return 0;
// }
// main
int main(int argc, char const *argv[])
{
B t1=play(5);
B t2=play(10);
return 0;
}
最初のmain関数で実行した結果は
[root@localhost c++]# ./a.out
constructed by para 5( , fun )
destructed (5 fun )
destructed (t1 fun )
destructed (t2 )
destructed (t1 )
2番目のmain関数で実行した結果は
[root@localhost c++]# ./a.out
constructed by para 5 ( , fun )
destructed (5 fun )
constructed by para 10( , fun )
destructed (10 fun )
destructed (t2 )
destructed (t1 )
ここでplay()関数を呼び出すには、(1)渡されたパラメータが整数の場合、関数スタックでパラメータ付きコンストラクション関数を最初に呼び出し、その後一時変数を生成し、関数が戻るとレプリケーションコンストラクション関数を呼び出し、一時オブジェクトを生成します.最後に、この一時オブジェクトは、関数が戻った後に構造を解析します(2)Bクラスオブジェクトが渡された場合、まずレプリケーション構造関数を呼び出して構造パラメータオブジェクトを初期化します.後のステップと同様に、2つの状況の違いは、異なる構造関数を呼び出して構造パラメータを初期化することです.1つは、パラメータ付き構造関数を呼び出し、1つはレプリケーション構造関数を呼び出すことです.
補足
一時的なオブジェクトとは?一時オブジェクトはどのような状況で発生していますか?一時オブジェクトは見えません.プログラムには表示されません.多くの場合、プログラムの実行効率に影響します.したがって、一時オブジェクトの生成を回避したい場合があります.通常、次の2つの場合に発生します.1.パラメータを値で渡す2.戻り値が値で渡されるのを避けるにはどうすればいいですか?値の代わりに参照転送を行うことができます.参照には実際の参照可能なオブジェクトが必要です.そうしないと、参照は間違っています.
一時オブジェクトを生成するときに、レプリケーションコンストラクタを呼び出す必要がある場合、レプリケーションコンストラクタはどのような場合に呼び出されますか?1.1つのオブジェクトが値伝達方式で関数体2に伝達する.1つのオブジェクトは、関数から3を値で返す.1つのオブジェクトを初期化するには、別のオブジェクトが必要です.