C++ポインタ変数の基本書き方
17805 ワード
ポインタ変数と適用——動的配列
C++には、値を表すことはできませんが、ある要素のアドレスを表すことができ、アドレスを通じてこの要素にアクセスする不思議な変数があります.
例えば、地図と座標があれば、この座標にアクセスすることで、座標が表す要素にアクセスする目的を達成することができます.ポインタ変数はこの「座標」です.
次に、ポインタ変数の適用を具体的に見てみましょう.
1、ポインタ変数の性質
前述したように、ポインタ変数は値を表すことはできませんが、他の要素のアドレスを指し、このアドレスを通じてこの要素の値に間接的にアクセスすることができます.
その性質のため、ポインタ変数は直接1つの要素に=することはできません.値を割り当てるときは注意してください.
具体的な操作は以下に説明します.
2、ポインタ変数の宣言
ポインタ変数を宣言する方法次の式があります.
データ型+「*」+ポインタ名
通常、私たちはこのように値を割り当てます.
これにより、ポインタタイプの変数pを定義します.NULLは空のメモリです.このメモリには何の要素もありません.後でpに要素のアドレスを割り当てることができます.△=NULLは使わなくてもいいですが、これは個人的な習慣で、return 0のような習慣です......)
この文の意味は、intタイプのポインタpを定義し、空のアドレスを指すことです.
では、どのようにして1つの要素のアドレスをポインタ変数に割り当てますか?
次の文があります.
上の2つの主関数の効果は同じです.
この2つのコードの意味について説明します.
皆さんはscanf()を使ったことがあると思います.変数名に記号「&」を入力します.これがアドレス記号で、変数名のアドレスを表します.
私たちのポインタはアドレスを指します.もちろん、ポインタ名=&変数名です.
3、ポインタ変数で要素値を呼び出す
値を割り当てる以上、次は要素値を呼び出しますが、ポインタはアドレスを指しており、直接演算に参加することはできません.この場合、間接演算子「*」を使用します.(定義されたときのアスタリスクです)
もし要素aがあれば、ポインタで出力する必要があります.どのように操作しますか?
この問題について、次の手順があります.
コードの注釈はすでに詳しくて、私達のポインタは1つの要素を指して、“*”+ポインタ名でポインタの指す要素にアクセスすることができます
注意:ポインタによる要素値の操作は、次のコードのように、直接操作する要素値と同じです.
プログラムが与えた結果は30,200であった.
4、ポインタの+、-演算
まず、基本的な定義を示します.
配列を定義すると、a[5]のような連続したアドレスが与えられ、a[0]のアドレスが0であると仮定すると、a[1]のアドレスは1......となる.
このとき,アドレス+1(ポインタ+1)を直接押すと,配列の次の要素にアクセスできる.
p-、同じ理屈です.この文はa[]のすべての変数を出力します.
5、ポインタと配列
配列を指すポインタは配列ポインタと呼ばれ、よく知られているように、1つの配列のアドレスは連続しており、ヘッダアドレスは彼が占有しているいくつかのユニットのヘッダアドレスであり、配列名をポインタに割り当てることもできるし、配列の中のある要素のアドレスを割り当てることもできる.
次の文があります.
次の3つの文
&a[0],a,*pは,いずれも同じセルである配列のヘッダアドレスを指す.
では,&a[i],a+i,p+iは,いずれも配列a中のa[i]のアドレスを指すと推測できる.
次のコードがあります.
私たちは5つの数のように負けました:1 2 3 4 5
システムは5行を示します.
1 1 1 1
2 2 2 2
3 3 3 3
4 4 4 4
5 5 5 5
これは,上の4つの文:*(pa+i),pa[i],a[i],*(a+i)が等価であることを示す.
コードの説明と注意事項:
1、a(配列名)は「*」を付けて定数ポインタにすることができ、aは開始要素であり、ポインタの加減原理により、a+iはi番目の要素のアドレスである.
2、aは定数名であり、ポインタの+、-操作を直接行うことはできない(ここではp+、p-という賦値操作は違法であるが、a+iは合法である)が、paはポインタ変数であり、加減操作を行うことができる.
6、ポインタ申請システム空間
ポインタpにシステム空間を申請するには、次の文を使用します.
このとき*pの内容は不確定です.
この文は動的配列の基礎である.
7、ポインタを配列名とする
1、原理:前に言ったように、一度に複数の空間を申請すると、システムは連続した新しい住所を送ってくれて、配列として使うことができます.
2、具体的な操作
次のコードがあります.
次のように入力します.
5
1 2 3 4 5
システムの提供
1 2 3 4 5
上のコードでは、配列名がポインタ名であり、残りの操作は5番目のプレートと同じであることが理解できます.(配列名+下付きでアクセス)
このように変更することもできます.
ここではポインタアクセスを用い,配列名アクセスを用いずに上のコードと等価である.もちろんprintf("%d",*(p+i);上記のように、これらの書き方は等価です.
8、動的配列と空間複雑度の最適化
あんなに多くのポインタの基本的な定義と書き方を引っ張って、ついに今日の本題になりました--ポインタを利用して動的配列を構築します.
大きな(行列<=1億円)しかしまばらな(要素の大部分が0)行列がある場合、この行列を操作するにはどうすればいいのでしょうか.
明らかに、このようなコードは絶対に通用しません.
このように書くと、あなたの空間の複雑さは絶対に越えられません.
私たちは最適化しなければなりません.
ポインタでスペースを申請できますか?この特性を利用して、無効なデータ(0)を格納することを避けることができます.入力するたびに有効なデータに新しいメモリユニットを開き、メモリが爆発しません.
次の例題を見てみましょう.
1冊の例題8.7:
【問題の説明】
行列はN*Mの二次元配列と考えられる.巨大だがまばらな行列がある.
N,Mの範囲は1<=N,M<=10000000であり,K個の位置にデータがあり,1<=K<=10000000である.
マトリクス入力は、上から下(1行目からN行目)、左から右(1列目からM列目)にスキャンし、データの座標位置(x,y)と値(v)を記録します.これは行優先でデータを保存します.
次に、列優先のデータ、すなわち左から右、上から下へスキャンし、データのある座標と位置を出力することが要求される.
【入力形式】
1行目:3つの整数N,M,K、そのうち1<=N,M,K<=10000000;以下にK行があり、各行の3つの整数:a,b,cであり、a行目b列目にデータcがあることを示す.データはintの範囲内で、行優先の順序であることを保証します.
【出力形式】
1行、K個の整数は、列優先順位で出力される数である
【サンプル入力】
4 5 9
1 2 12
1 4 23
2 2 56
2 5 78
3 2 100
3 4 56
4 1 73
4 3 34
4 5 55
【サンプル出力】
73 12 56 100 34 23 56 78 55
【様式解釈】
0
12
0
23
0
0
56
0
0
78
0
100
0
56
0
73
0
34
0
55
この行列については、次のように保存できます.
73
12
34
23
78
——
56
——
56
55
——
100
——
——
——
——
——
——
——
——
注意:“------”のマークはすべて使っていないメモリで、このように私達は11個のメモリユニットを節約して、大きいデータの時、私達は更に多くのメモリを節約することができて、空間の制限を超えないことを保証します.
この考え方の大まかな意味は、xの値を無視して、y番目の列に最初に入力したデータをy番目の列の最初のデータとして、それから2番目......
次に、コード実装について説明します.
a[i]=new int c[[i]];このポインタにc[i]の空間を新しく申請するという意味で、LP個の1次元のポインタ配列を開いたのと同じで、これらの配列にはそれぞれ専用のポインタa[i]があり、各配列にはc[i]の要素がある.
ここまで、ポインタを利用してダイナミック配列を開く具体的な方法についてお話ししました.そうすれば、あなたのプログラムを効率的に最適化することができます.急いで使いましょう.
C++には、値を表すことはできませんが、ある要素のアドレスを表すことができ、アドレスを通じてこの要素にアクセスする不思議な変数があります.
例えば、地図と座標があれば、この座標にアクセスすることで、座標が表す要素にアクセスする目的を達成することができます.ポインタ変数はこの「座標」です.
次に、ポインタ変数の適用を具体的に見てみましょう.
1、ポインタ変数の性質
前述したように、ポインタ変数は値を表すことはできませんが、他の要素のアドレスを指し、このアドレスを通じてこの要素の値に間接的にアクセスすることができます.
その性質のため、ポインタ変数は直接1つの要素に=することはできません.値を割り当てるときは注意してください.
具体的な操作は以下に説明します.
2、ポインタ変数の宣言
ポインタ変数を宣言する方法次の式があります.
データ型+「*」+ポインタ名
通常、私たちはこのように値を割り当てます.
int main()
{
int *p=NULL;
return 0;
}
これにより、ポインタタイプの変数pを定義します.NULLは空のメモリです.このメモリには何の要素もありません.後でpに要素のアドレスを割り当てることができます.△=NULLは使わなくてもいいですが、これは個人的な習慣で、return 0のような習慣です......)
この文の意味は、intタイプのポインタpを定義し、空のアドレスを指すことです.
では、どのようにして1つの要素のアドレスをポインタ変数に割り当てますか?
次の文があります.
#include
using namespace std;
int main()
{
int a;
int *p=NULL;
p=&a;
return 0;
}
/*int main()
{
int a;
int *p=&a;
return 0;
}*/
上の2つの主関数の効果は同じです.
この2つのコードの意味について説明します.
皆さんはscanf()を使ったことがあると思います.変数名に記号「&」を入力します.これがアドレス記号で、変数名のアドレスを表します.
私たちのポインタはアドレスを指します.もちろん、ポインタ名=&変数名です.
3、ポインタ変数で要素値を呼び出す
値を割り当てる以上、次は要素値を呼び出しますが、ポインタはアドレスを指しており、直接演算に参加することはできません.この場合、間接演算子「*」を使用します.(定義されたときのアスタリスクです)
もし要素aがあれば、ポインタで出力する必要があります.どのように操作しますか?
この問題について、次の手順があります.
#include
using namespace std;
int main()
{
int a;
scanf("%d",&a);
int *p=&a;// p a
printf("%d",*p);// +
return 0;
}
コードの注釈はすでに詳しくて、私達のポインタは1つの要素を指して、“*”+ポインタ名でポインタの指す要素にアクセスすることができます
注意:ポインタによる要素値の操作は、次のコードのように、直接操作する要素値と同じです.
#include
using namespace std;
int main()
{
int a,b,s,t,*pa=NULL,*pb=NULL;
pa=&a,pb=&b;
a=10,b=20;
s=*pa+*pb;
t=*pa**pb;
printf("%d %d",s,t);
return 0;
}
プログラムが与えた結果は30,200であった.
4、ポインタの+、-演算
まず、基本的な定義を示します.
配列を定義すると、a[5]のような連続したアドレスが与えられ、a[0]のアドレスが0であると仮定すると、a[1]のアドレスは1......となる.
このとき,アドレス+1(ポインタ+1)を直接押すと,配列の次の要素にアクセスできる.
#include
using namespace std;
int main()
{
int a[5],*p=&a[0];
for(int i=0;i<5;i++)
scanf("%d",&a[i]);
for(int i=0;i<5;i++)
{
printf("%d ",*p);
p++;
}
return 0;
}
p-、同じ理屈です.この文はa[]のすべての変数を出力します.
5、ポインタと配列
配列を指すポインタは配列ポインタと呼ばれ、よく知られているように、1つの配列のアドレスは連続しており、ヘッダアドレスは彼が占有しているいくつかのユニットのヘッダアドレスであり、配列名をポインタに割り当てることもできるし、配列の中のある要素のアドレスを割り当てることもできる.
次の文があります.
int a[5],*p=a;
次の3つの文
&a[0],a,*pは,いずれも同じセルである配列のヘッダアドレスを指す.
では,&a[i],a+i,p+iは,いずれも配列a中のa[i]のアドレスを指すと推測できる.
次のコードがあります.
#include
using namespace std;
int main()
{
int a[5],*pa=a;
for(int i=0;i<5;i++)
scanf("%d",&a[i]);
for(int i=0;i<5;i++)
printf("%d %d %d %d
",*(pa+i),pa[i],a[i],*(a+i));
return 0;
}
私たちは5つの数のように負けました:1 2 3 4 5
システムは5行を示します.
1 1 1 1
2 2 2 2
3 3 3 3
4 4 4 4
5 5 5 5
これは,上の4つの文:*(pa+i),pa[i],a[i],*(a+i)が等価であることを示す.
コードの説明と注意事項:
1、a(配列名)は「*」を付けて定数ポインタにすることができ、aは開始要素であり、ポインタの加減原理により、a+iはi番目の要素のアドレスである.
2、aは定数名であり、ポインタの+、-操作を直接行うことはできない(ここではp+、p-という賦値操作は違法であるが、a+iは合法である)が、paはポインタ変数であり、加減操作を行うことができる.
6、ポインタ申請システム空間
ポインタpにシステム空間を申請するには、次の文を使用します.
int *p=new(int);
このとき*pの内容は不確定です.
この文は動的配列の基礎である.
7、ポインタを配列名とする
1、原理:前に言ったように、一度に複数の空間を申請すると、システムは連続した新しい住所を送ってくれて、配列として使うことができます.
2、具体的な操作
次のコードがあります.
#include
using namespace std;
int main()
{
int n,*p;
scanf("%d",&n);
p=new int[n+1];// n+1 p
for(int i=1;i<=n;i++)
scanf("%d",&p[i]);
for(int i=1;i<=n;i++)
printf("%d ",p[i]);
return 0;
}
次のように入力します.
5
1 2 3 4 5
システムの提供
1 2 3 4 5
上のコードでは、配列名がポインタ名であり、残りの操作は5番目のプレートと同じであることが理解できます.(配列名+下付きでアクセス)
このように変更することもできます.
#include
using namespace std;
int main()
{
int n,*p;
scanf("%d",&n);
p=new int[n+1];// n+1 a
for(int i=1;i<=n;i++)
scanf("%d",&p[i]);
for(int i=1;i<=n;i++)
{
p++;// p 0 , ++
printf("%d ",*p);
}
return 0;
}
ここではポインタアクセスを用い,配列名アクセスを用いずに上のコードと等価である.もちろんprintf("%d",*(p+i);上記のように、これらの書き方は等価です.
8、動的配列と空間複雑度の最適化
あんなに多くのポインタの基本的な定義と書き方を引っ張って、ついに今日の本題になりました--ポインタを利用して動的配列を構築します.
大きな(行列<=1億円)しかしまばらな(要素の大部分が0)行列がある場合、この行列を操作するにはどうすればいいのでしょうか.
明らかに、このようなコードは絶対に通用しません.
#include
#define N 10000100
using namespace std;
int n[N][N];
このように書くと、あなたの空間の複雑さは絶対に越えられません.
私たちは最適化しなければなりません.
ポインタでスペースを申請できますか?この特性を利用して、無効なデータ(0)を格納することを避けることができます.入力するたびに有効なデータに新しいメモリユニットを開き、メモリが爆発しません.
次の例題を見てみましょう.
1冊の例題8.7:
【問題の説明】
行列はN*Mの二次元配列と考えられる.巨大だがまばらな行列がある.
N,Mの範囲は1<=N,M<=10000000であり,K個の位置にデータがあり,1<=K<=10000000である.
マトリクス入力は、上から下(1行目からN行目)、左から右(1列目からM列目)にスキャンし、データの座標位置(x,y)と値(v)を記録します.これは行優先でデータを保存します.
次に、列優先のデータ、すなわち左から右、上から下へスキャンし、データのある座標と位置を出力することが要求される.
【入力形式】
1行目:3つの整数N,M,K、そのうち1<=N,M,K<=10000000;以下にK行があり、各行の3つの整数:a,b,cであり、a行目b列目にデータcがあることを示す.データはintの範囲内で、行優先の順序であることを保証します.
【出力形式】
1行、K個の整数は、列優先順位で出力される数である
【サンプル入力】
4 5 9
1 2 12
1 4 23
2 2 56
2 5 78
3 2 100
3 4 56
4 1 73
4 3 34
4 5 55
【サンプル出力】
73 12 56 100 34 23 56 78 55
【様式解釈】
0
12
0
23
0
0
56
0
0
78
0
100
0
56
0
73
0
34
0
55
この行列については、次のように保存できます.
73
12
34
23
78
——
56
——
56
55
——
100
——
——
——
——
——
——
——
——
注意:“------”のマークはすべて使っていないメモリで、このように私達は11個のメモリユニットを節約して、大きいデータの時、私達は更に多くのメモリを節約することができて、空間の制限を超えないことを保証します.
この考え方の大まかな意味は、xの値を無視して、y番目の列に最初に入力したデータをy番目の列の最初のデータとして、それから2番目......
次に、コード実装について説明します.
#include
using namespace std;
const int LP=100001;
int n,m,k;
int x[LP],y[LP],d[LP],c[LP];// , n x ,y ,c y 。
int *a[LP];// LP , LP
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=k;i++)
{
scanf("%d%d%d",&x[i],&y[i],&d[i]);// x,y,d
c[y[i]]++;// y[i] ++
}
for(int i=1;i<=m;i++)
a[i]=new int[c[i]];//
for(int i=1;i<=k;i++)
{
*a[y[i]]=d[i];// y
a[y[i]]++;// y ,
}
for(int i=1;i<=m;i++)//
{
a[i]-=c[i];// , ,
for(int j=1;j<=c[i];j++,a[i]++)// 1 ,j i , ,
printf("%d ",*a[i]);// +1,
}
return 0;
}
a[i]=new int c[[i]];このポインタにc[i]の空間を新しく申請するという意味で、LP個の1次元のポインタ配列を開いたのと同じで、これらの配列にはそれぞれ専用のポインタa[i]があり、各配列にはc[i]の要素がある.
ここまで、ポインタを利用してダイナミック配列を開く具体的な方法についてお話ししました.そうすれば、あなたのプログラムを効率的に最適化することができます.急いで使いましょう.