複数の関数間での呼び出しは通常、システムスタックに高位アドレスから低位アドレスに向かって、積まれるそうだ。(例のpush-pop)。この作業に時間がかかり、場合によってはオーバーヘッドの一因になるそうな。(オーバーヘッド?)
上記の場合を避けるために、簡単な処理であればマクロを使ってインライン関数化するのがいいそうだ。
では、どうするか。
組み込み型データのコピーはビットコピーするだけだから、単純な操作であろう。しかし、オブジェクトのコピーはそうはいかないようだ。
Cでは、こんな感じ。
int summation(int a,int b){
int sum=a+b;
return sum;
}
こいつを
#define summation(int a,int b) {(a)+(b)} としてやる。
関数定義の前にインライン関数を定義する方法。
#include<iostream>
using namespace std;
//インライン関数を定義
inline int add(int i,int j){return i+j;}
int main()
{
int result;
result=add(5,3);//add関数を呼び出す
cout<<result<<endl;
return 0;
}
すごいのはインラインが自動的に行われることだ。短かく定義されたメンバ関数をコンパイラによって、自動的にインライン化される。
#include<iostream>
using namespace std;
class tester{
int i,j;
public:
sample(int a,int b);
//クラス定義内で定義されたインライン関数
int add(){return i+j;}
};
sample::sample(int a,int b)
{
i=a;j=b;
}
と簡単に述べてみたが、インライン関数の本当の意味は現時点の筆者のレベルからすると理解しがたい点が多いため、更なる勉強が必要だ。
コピーコンストラクタ
/****コピーコンストラクタを生成、オブジェクトを関数に渡す*****/
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
class stringtype{
char *p;
public:
stringtype(char *s);
stringtype(const stringtype &ob);
~stringtype(){
delete []p;}
char *get(){
return p;
}
};
stringtype::stringtype(char *s)
{
int l;
l=strlen(s)+1;//strlen()method
p=new char[l];
if(!p){
cout<<"unable get a memory field"<<endl;
exit(1);
}
strcpy(p,s);//strcpy()method
}
stringtype::stringtype(const stringtype &ob)
{
int l;
l=strlen(ob.p)+1;
p=new char[l];
if(!p){
cout<<"unable get a memory field"<<endl;
exit(1);
}
strcpy(p,ob.p);//文字列をコピーオブジェクトにコピー
}
void show(stringtype x)
{
char *s;
s=x.get();
cout<<s<<endl;
}
int main()
{
stringtype e("see you"),d("good luck");
show(e),show(d);
return 0;
}
//出力結果
see you
good luck
#include<iostream>
#include<cstring>
#include<cstdlib>
using namespace std;
class stringtype{
char *p;
public:
stringtype(char *s);
stringtype(const stringtype &ob);
~stringtype(){
delete []p;}
char *get(){
return p;
}
};
stringtype::stringtype(char *s)
{
int l;
l=strlen(s)+1;//strlen()method
p=new char[l];
if(!p){
cout<<"unable get a memory field"<<endl;
exit(1);
}
strcpy(p,s);//strcpy()method
}
stringtype::stringtype(const stringtype &ob)
{
int l;
l=strlen(ob.p)+1;
p=new char[l];
if(!p){
cout<<"unable get a memory field"<<endl;
exit(1);
}
strcpy(p,ob.p);//文字列をコピーオブジェクトにコピー
}
void show(stringtype x)
{
char *s;
s=x.get();
cout<<s<<endl;
}
int main()
{
stringtype e("see you"),d("good luck");
show(e),show(d);
return 0;
}
//出力結果
see you
good luck
コピーコンストラクタ
/*****プログラム概要******/
/**動的に配列を割り当て、配列オブジェクトを生成しかつ他の配列オブジェクトを初期化するとき、コピーコンストラクタでメモリ割り当てをする***/
#include<iostream>
#include<cstdlib>
using namespace std;
class array{
int *p;
int size;
public:
//通常のコンストラクタ
array(int s){
p=new int[s];
if(!p)
exit(1);
size=s;
cout<<"通常のコンストラクタを使う"<<endl;
}
~array(){
delete []p;}
//copy constructor
array(const array &a);
void set(int i,int j){
if(i>=0 && i<size)
p[i]=j;
}
int get(int i){
return p[i];
}
};
/*このコンストラクタでコピー用にメモリを確保してそのメモリアドレスをpに当てる。pが元のオブジェクトと同等のメモリを参照することはない**/
array::array(const array &a){
int i;
size=a.size;
p=new int[a.size];
if(!p)
exit(1);
for(i=0;i<a.size;i++)
p[i]=a.p[i];
cout<<"コピーコンストラクタを使う"<<endl;
}
int main()
{
array num(10); //通常のコンストラクタを呼び出す
int i;
for(i=0;i<10;i++)
num.set(i,i);
for(i=9;i>=0;i--)
cout<<num.get(i);
puts("");
array x=num; //他の配列を生成して、numで初期化
for(i=0;i<10;i++)
cout<<x.get(i);
puts("");
return 0;
}
コマンドプロンプトへの出力結果
通常のコンストラクタを使う
9876543210
コピーコンストラクタを使う
0123456789
/**動的に配列を割り当て、配列オブジェクトを生成しかつ他の配列オブジェクトを初期化するとき、コピーコンストラクタでメモリ割り当てをする***/
#include<iostream>
#include<cstdlib>
using namespace std;
class array{
int *p;
int size;
public:
//通常のコンストラクタ
array(int s){
p=new int[s];
if(!p)
exit(1);
size=s;
cout<<"通常のコンストラクタを使う"<<endl;
}
~array(){
delete []p;}
//copy constructor
array(const array &a);
void set(int i,int j){
if(i>=0 && i<size)
p[i]=j;
}
int get(int i){
return p[i];
}
};
/*このコンストラクタでコピー用にメモリを確保してそのメモリアドレスをpに当てる。pが元のオブジェクトと同等のメモリを参照することはない**/
array::array(const array &a){
int i;
size=a.size;
p=new int[a.size];
if(!p)
exit(1);
for(i=0;i<a.size;i++)
p[i]=a.p[i];
cout<<"コピーコンストラクタを使う"<<endl;
}
int main()
{
array num(10); //通常のコンストラクタを呼び出す
int i;
for(i=0;i<10;i++)
num.set(i,i);
for(i=9;i>=0;i--)
cout<<num.get(i);
puts("");
array x=num; //他の配列を生成して、numで初期化
for(i=0;i<10;i++)
cout<<x.get(i);
puts("");
return 0;
}
コマンドプロンプトへの出力結果
通常のコンストラクタを使う
9876543210
コピーコンストラクタを使う
0123456789
文字の配列
Cの文字列は、難しいと思う。
文字列型がないので、文字型charを使って、配列にしてやらなければならないようだ。言わば、文字型配列だ。
加えて、2次元配列などはなく、厳密には配列の配列であり、char str[3][ ]は char *str[ ]は同義だ。
したがって、以下は同じ動きをする。
#include<stdio.h>
int main(void)
{
char str[3][ ]={{"asahikawa"},{"sapporo"},{"hakodate"}};
int i;
char *strp[ ]={{"asahikawa"},{"sapporo"},{"hakodate"}};
for(i=0;i<3;i++)
printf("%s\n",str[i]);
puts("");
for(i=0;i<3;i++)
printf("%s\n",strp[i]);
return 0;
}
文字列型がないので、文字型charを使って、配列にしてやらなければならないようだ。言わば、文字型配列だ。
加えて、2次元配列などはなく、厳密には配列の配列であり、char str[3][ ]は char *str[ ]は同義だ。
したがって、以下は同じ動きをする。
#include<stdio.h>
int main(void)
{
char str[3][ ]={{"asahikawa"},{"sapporo"},{"hakodate"}};
int i;
char *strp[ ]={{"asahikawa"},{"sapporo"},{"hakodate"}};
for(i=0;i<3;i++)
printf("%s\n",str[i]);
puts("");
for(i=0;i<3;i++)
printf("%s\n",strp[i]);
return 0;
}
ポインタを纏めてみた
1.ポインタとは
int *pointa→int型を指すポインタ(注意。間接参照演算子は変数pointaを指しているわけではない)
char *pointa→char型を指すポインタ(注意。間接参照演算子は変数pointaを指しているわけではない)
struct any *pointa→struct型を指すポインタ(注意。間接参照演算子は変数pointaを指しているわけではない)
char *pointa→char型を指すポインタ(注意。間接参照演算子は変数pointaを指しているわけではない)
struct any *pointa→struct型を指すポインタ(注意。間接参照演算子は変数pointaを指しているわけではない)
一般に型Tの変数を指すポインタ型の変数pは T *pointaと定義する
- ポインタには型がある。そして、ポインタ型の変数もポインタ型の値もある。
- ポインタ型の値はメモリのアドレスである。
- ポインタ変数が基本型の変数のアドレス(定数)を保持しているとき、ポインタ変数は対象の基本型を指しているという。
- 基本型のアドレスの値を基本型の値という。
まとめ:
・変数に&(アドレス演算子)を適用すると、その変数のアドレスを取得することができる。このアドレスのことをその変数へのポインタという。
・ポインタ変数が別の基本型変数(一般的に)へのポインタを保持しているとき、ポインタ変数が基本型変数を指しているという。
・ポインタに間接参照演算子(*)を適用すると、そのポインタの指している先の値を表示することができる。また、ポインタに他の値を代入すると、指している変数の値を替えることができる。
気を付けるべき点:
ポインタとは、型はどうであれ、すべての基本型変数、派生型のポインタ変数、関数、構造体などその対象が持っているメモリー上の位置すなわち
アドレスの値(定数)のことを意味する。したがって、ポインタ型変数例えば、int *numとint numはそれぞれ別のポインタの値を持つことになる。
変数のポインタの値を調べるときは、printf("%p",&*num); printf("%p",&num);としてやればよい。ポインタ値は定数であって、ポインタ型変数以外に
その値を変えたり、代入したりすることなぞしてはいけない。ポインタ値を自在に扱って役割を果たすのは、ポインタ型変数のみである。
ポインタ演算:(pointa+sizeof(ポインタが示す型) * 増やす増分・減らす減分)
ポインタ型変数にn加算すると、ポインタはそのポインタが指す型のサイズ×nだけ前に進む。p++;
ポインタ型変数にn減算すると、ポインタはそのポインタが指す型のサイズ×nだけ後ろに進む。p--;
2.配列とポインタの密接な関係
配列名はその配列の先頭要素へのポインタ値と解釈される。
int array[10]は&array[0]と同じ型、同じ値となる。
つまり、array==&array[0]であり、恒等式は常に真である。
また、以上は同値 array[i]==*(array+i);
[ 宣言時と数式との違い ]
配列を宣言する時には、[ ]で要素数を指定し、
配列の要素を使う時は、[ ]で番号を指定するのですが、
実は、この2つも全く別の記号です。
宣言時の[ ]は要素数を指定するという意味を持ちますが、
数式の中で使用する[ ] は、アドレスに足し算する演算子です。
C言語では、似た使い方には同じ記号を使いたがる傾向があり、
その為、異なる意味に同じ記号を割り当てている部分が多いようです。
配列を宣言する時には、[ ]で要素数を指定し、
配列の要素を使う時は、[ ]で番号を指定するのですが、
実は、この2つも全く別の記号です。
宣言時の[ ]は要素数を指定するという意味を持ちますが、
数式の中で使用する[ ] は、アドレスに足し算する演算子です。
C言語では、似た使い方には同じ記号を使いたがる傾向があり、
その為、異なる意味に同じ記号を割り当てている部分が多いようです。
例:int array[5]={0,1,2,3,4};
int i;
int *pointa;
//arrayの各要素の値を表示 //arrayの各要素の値をポインタ型変数を使って表示する2例: pointa=&array[0];
for(i=0;i<5;i++) for(pointa=&array[0]; pointa!=&array[5]; pointa++) for(i=0;i<5;i++)
printf("%d",array[i]); printf("%d",*p); printf("%d\n",*(p+i));
pointa=&array[0]; ⇔pointa=array;
故に以下のコードが成り立つ
pointa=array;
for(i=0;i<5;i++)
printf("%d\n",*(pointa+i)); ⇔ printf("%d\n",pointa[i]); ⇔ printf("%d\n",array[i]);
つまり、
*(pointa+i)==pointa[i]==array[i]
式の中では、配列はその先頭要素へのポインタに読み替えられる。
3つ程の例外はあるが、後ろに添え字演算子[ ]が付くかは問題ではない。
pointa[i]は、*(pointa+i)の簡便記法である。
重要な補足:
配列の宣言時の添え字演算子と式の中のそれは違う。
上記の例はあくまでも、式の中での動きである。
また、宣言時の * と式の中での * も同様である。
関数に配列を渡すときは、配列名だけを引数として渡す。
配列を関数に渡すと、渡した先の関数の定義時に配列は、array[ ]から&array[0]にポインタに変換される。
したがって、呼び出し先の関数でポインタ型の変数を最初に定義してやると、いい。また、複数の戻り値を呼び出し元に返すことが出来るのが、
強い利点である。
//通常の配列名を渡すパターン //ポインタとして渡すパターン
#include <stdio.h> #include<stdio.h>
#define GAKUSEISUU 5 #define GAKUSEISUU 5
double average(int array[ ]); double average(int *pointa);
int main_jdjas(void) int main(void)
{ {
int array[GAKUSEISUU]; int array[GAKUSEISUU];
int goukei=0; double heikin;
int i; int i;
double heikin;
puts("5人の点数を入力");
puts("5人分の点数を入力");
for(i=0;i<GAKUSEISUU;i++) for(i=0;i<GAKUSEISUU;i++)
{ {
scanf("%d",&array[i]); scanf("%d",&array[i]);
} }
heikin=average(array); heikin=average(array);
printf("平均=%.1lf 点",heikin); printf(" 平均=%.1lf点",heikin);
printf("平均=%.1lf 点",heikin); printf(" 平均=%.1lf点",heikin);
}
double average_kkesg(int array[ ]) return 0;
{ }
//int array[5];
int goukei=0; double average(int *pointa)
double heikin; {
int i; int goukei=0;
double heikin;
int i;
int i;
for(i=0;i<GAKUSEISUU;i++)
{ for(i=0;i<GAKUSEISUU;i++)
goukei+=array[i]; {
} goukei+=*(pointa+i);
//heikin=(double)goukei/GAKUSEISUU; }
return heikin; return heikin;
} }
2a.文字列リテラルとポインタ
文字列定数"string"は以下の文字型配列arrayと同値である。
char array[]={'g'.'o','o','d','\0'};
したがって、以下のように文字列型へのポインタCpointaを文字列定数で初期化できる。
char *Cpointa="good";
3.関数とポインタ
関数の引数に配列全体を渡すことはできない。ただし、配列の先頭要素のアドレスをポインタとして渡すことはできる。
関数に配列を渡すときは、配列名だけを引数として渡す。
配列を関数の引数として渡したければ、先頭要素へのポインタを渡す。
関数の引数にポインタがある場合、それはポインタとして扱われる。
以下のプロトタイプ宣言は同じ意味である。
int hoge(char array[ ]);==int hoge(char *array);
3a.関数ポインタ
4.構造体とポインタ
pointaが構造体へのポインタのとき、メンバを参照するには、
(*pointa).name;と表記し、ポインタpointaが指している構造体のメンバnameを参照している。
pointa->name; →上記の省略型( -> はアロー演算子)
注意→演算子の優先度から、( )を省略できない。*pointa.nameと書いたら、*(pointa.name)の意味になってしまう。
構造体の実引数も関数に渡すときは、値渡しで関数に渡される。
注意事項として、構造体のメンバが多いときは、逐次その値がコピーして渡されるため、処理に時間がかかる。
注意事項として、構造体のメンバが多いときは、逐次その値がコピーして渡されるため、処理に時間がかかる。
故にポインタを使うことが望まれる。
//構造体を関数の引数として使う例 //右の例をポインタで処理
#include<stdio.h>
//構造体struct Carの宣言
typedef struct Car
{
int plate_num;
double gas;
}Car;
//show 関数の宣言 //構造体へのポインタを引数にもつ関数宣言
void show(Car c); //構造体を引数にもつ関数 void show(Car *pointa);
int main(void)
{
Car car1={0,0.00};
puts("ナンバープレートの番号は?");
scanf("%d",&car1.plate_num);
puts("残存ガソリン量は?");
scanf("%lf",&car1.gas);
//構造体car1へのアドレスを渡す処理
show(car1); //show関数を呼び出し、構造体 car1の値を渡している←ここがミソ show(&car1);
return 0;
}
//show関数の定義
void show(Car c)
{
printf("ナンバープレートの番号は%d:\t残存ガソリン量は%.1lf\n",c.plate_num,c.gas);
}
//構造体を関数の引数として使う例 //右の例をポインタで処理
#include<stdio.h>
//構造体struct Carの宣言
typedef struct Car
{
int plate_num;
double gas;
}Car;
//show 関数の宣言 //構造体へのポインタを引数にもつ関数宣言
void show(Car c); //構造体を引数にもつ関数 void show(Car *pointa);
int main(void)
{
Car car1={0,0.00};
puts("ナンバープレートの番号は?");
scanf("%d",&car1.plate_num);
puts("残存ガソリン量は?");
scanf("%lf",&car1.gas);
//構造体car1へのアドレスを渡す処理
show(car1); //show関数を呼び出し、構造体 car1の値を渡している←ここがミソ show(&car1);
return 0;
}
//show関数の定義
void show(Car c)
{
printf("ナンバープレートの番号は%d:\t残存ガソリン量は%.1lf\n",c.plate_num,c.gas);
}
Subscribe to:
Posts (Atom)
Nikkei225
28000-28550 up in the early session, down lately.
-
まず、米ドルの反対にある欧州単一通貨ユーロを考えることが米ドルの過去、未来を探るのに相応しいため、ユーロ・ドルの展望してみる。 本来ならば、ドル・円でと考えたが、ご承知のとおり、日本株、日本国債、日本外為市場などの東京市場は外国人から見れば、超巨大なローカル市場にすぎない。また、...
-
ECBによる資金供給や公的資金の投入が奏功し、欧州金融機関の融資余力が増しているようだ。 source- nikkei net