C/C++配列名とポインタの違いの詳細

12079 ワード


C/C++配列名とポインタの違いの詳細
 
ポインタはC/C++言語の特色であり、配列名はポインタと似ていることが多く、配列名はポインタとして使用できることも多い.そこで、多くのプログラム設計者が混乱しました.多くの大学の先生は、C言語の教育過程でも「配列名はポインタ」と間違って学生に説明しなければならない.幸いなことに、私の大学の先生はその一人です.今日に至るまで、私は日々C/C++プロジェクトの開発を行っていますが、身の回りにはこのようなプログラマーがいっぱいいて、彼らは「配列名はポインタ」という誤解を残しています.
v=`!#*~!
uo^ccVQ
このような誤解の根源は、国内のある有名なCプログラム設計チュートリアルにあると思います.この文章が多くの中国のプログラマーの配列名とポインタに対する誤解を正すことができれば、筆者はあまりうれしくない.この文章を借りて、筆者は無数の知識に飢えている中国のプログラマーの中に立って、国内のコンピュータ図書の編纂者たちに深く希望を寄せて、“深く探求します”の思考方式と精進するまじめな態度で図書の編纂の仕事に対応することができて、市場の上でもっと作者の思考の結晶の心血の作品に溶け込むことを望みます!
5;EYB,7Jk
PdANZ&:[
マジック配列名
S |t6Oc
t_epbzB-
プログラムを見てください(本プログラムはWIN 32プラットフォームでコンパイルされています):
287=n
3i8$p2V{

   
   
   
   
  1. #include <iostream.h>
  2. int main(int argc, char* argv[])
  3. {
  4. char str[10];
  5. char *pStr = str;
  6. cout << sizeof(str) << endl;
  7. cout << sizeof(pStr) << endl;
  8. return 0;
  9. } dS{FJs^<e $ZpuZQ
3 :z)>v< G6f'VbPKy

[SrIWv"oP
CxER/?, }
1、配列名はポインタではありません
zalG4C:7
Dp/n`
まず「配列名はポインタ」という説を覆し、反証法を用いる.
R/S|PKV;.
s^GAmb2Q
配列名がポインタでないことを証明
PU mIqJX #
仮定:配列名はポインタです.
bjKe)^ r
,q$Mi`]tJR
pStrもstrもポインタです.
(@8F,!1c
ywVH4?z0
WIN 32プラットフォームの下で、ポインタの長さは4です.
_&lqL 1rA
itI'lM
したがって、6行目と7行目の出力はいずれも4であるべきである.
4,AG"3vU|
M|7!0!xV
実際の状況は、6行目出力10、7行目出力4;
JdZ| TwT4
TQ 2?{+
だから:仮に成立しないと仮定して、配列名はポインタではありません
R-VZdaM^
_&Md:wD
2、配列名神似ポインタ
?us778L5
Hu0K S2
配列名がポインタではないことを証明しましたが、プログラムの5行目を見てみましょう.このロー・プログラムは、配列名をポインタに直接割り当てます.これは、配列名がポインタであるように見えます.
~_J#\y0J
;YMxaZU
配列名がポインタのように見える例もわかります.
tTx}XIxL.$
LKfp` FH

   
   
   
   
  1. #include <string.h>
  2. #include <iostream.h>
  3. int main(int argc, char* argv[])
  4. {
  5. char str1[10] = "I Love U";
  6. char str2[10];
  7. strcpy(str2,str1);
  8. cout << "string array 1: " << str1 << endl;
  9. cout << "string array 2: " << str2 << endl;
  10. return 0;
  11. } }fvV|sqS !1584vj
o{2Z&a *gVE12s

njw Y~y\
@X s#t(
標準Cライブラリ関数strcpyの関数原形に受け入れられる2つのパラメータはchar型ポインタですが、呼び出しで渡されるのは2つの配列名です.関数の出力:
5 }!x:8
eU\MVcL~$

   
   
   
   
  1. string array 1: I Love U
  2. string array 2: I Love U }+90KWI ^u =qW
^M7_I&&\ k[B7?,h7C

%I ( @=$+
"zs4&{3
配列名は再びポインタのように見えます!
xCrD=>5t@v
Ui57\u{mh
配列名がポインタではない以上、なぜあちこちで配列名をポインタとして使うのですか?そこで,多くのプログラマーは,配列名(主)がポインタではないポインタ(賓)であると結論した.
6ga%*FFp
`uN:G8F.
悪魔全体.
@3$m'sYd
@ `/d~KsC
配列名を明かす
bO+n%9>F[
^w/aFAw^
配列名の本質を明らかにする時が来た.まず3つの結論を出す.
(PZ?"\+:
E!|vaGG.
(1)配列名の内包は、エンティティがデータ構造であり、このデータ構造が配列であることを指す.
L-S)}oe
iYeA|@Z
(2)配列名のエピタキシャルは、そのエンティティを指すポインタに変換でき、ポインタ定数である.
A&$3jP%7
]=lM RF
(3)配列を指すポインタは別の変数タイプ(WIN 32プラットフォームでは長さ4)であり,配列の格納アドレスのみを意味する.
7sh*PeD
]^`l8'.yUc
1、配列名は一種のデータ構造を指す:配列
Taedh
YG,dEDTaM
1番目のプログラムの6行目の出力が10である理由を説明することができ、結論1によると、配列名strの内包はデータ構造、すなわち長さ10のchar型配列であるため、sizeof(str)の結果はこのデータ構造が占めるメモリサイズ:10ワード節である.
mxR F|5P.
>{WmynIRC+
もう一度見てください.
W!K e%XH?
` 9&yl#>l

   
   
   
   
  1. int intArray[10];
  2. cout << sizeof(intArray) ; :c@{I_, *cmA=Agd8
\RNa ?V ^eJ)xMk

aIWjt6D#
>gbe xR
2行目の出力結果は40(整数配列が占めるメモリ領域サイズ)であった.
%/$j!R
^9#nqvVn
C/C++プログラムがこのように書くことができる場合:
. '2_#
y/g#aBVK

   
   
   
   
  1. int[10] intArray;
  2. cout << sizeof(intArray) ; MJSak+$l %%}|%jC=h
Wv#=6o@9 p '_M}W-/

<1994h`
?j3[(BintArrayはint[10]というデータ構造の一例として定義されていることがわかりますが、残念ながらC/C++は現在この定義方式をサポートしていません.
]=@".o
1V~VKOG
2、配列名はポインタ定数とすることができる
jb\],?d!I
N*Qbh16%?3
結論2によれば、配列名はそのエンティティを指すポインタに変換できるので、プログラム1の5行目の配列名はポインタに直接付与され、プログラム2の7行目は直接配列名をポインタ形パラメータとして成立する.
0G.U!,xx
rL#: W0G
次のプログラムは成立しますか?
`KrwS<{
,)3bf>"

   
   
   
   
  1. int intArray[10];
  2. intArray++; WrtsaXI|g l2$L!pk
@vJ ^.qA7jYD

9|9"

B(_X}8
読者はコンパイルすることができて、コンパイルの間違いを発見します.なぜなら、配列名は、エンティティを指すポインタに変換できますが、ポインタ定数としか見なされず、変更できません.
0OvQmr\
9VX ;,
ポインタは、構造体、配列、基本データ型を指すポインタにかかわらず、元のデータ構造の内包は含まれておらず、WIN 32プラットフォームではsizeof操作の結果は4である.
u^!"%
F C1p 5
ついでに多くのプログラマーのもう一つの誤解を直します.多くのプログラマーはsizeofが関数だと思っていますが、実際にはオペレータですが、その使い方は確かに関数のように見えます.文sizeof(int)はsizeofが確かに関数ではないことを説明することができる.関数はパラメータ(変数)を受け入れ、世界にはintのようなデータ型を「パラメータ」として受け入れるC/C++関数はないからだ.
adskPf]'
Pq%sI^Vr
3、データ名はそのデータ構造の内包を失う可能性がある
jk!W(_EN
QK9 @`]Y
ここまで来ると、配列名のファンタジー問題は円満に解決されたようだが、穏やかな湖面には再び波が巻き起こった.次のプログラムを見てください.
V6{BR! o8V
]s3b&:j


   
   
   
   
  1. #include <iostream.h>
  2. void arrayTest(char str[])
  3. {
  4. cout << sizeof(str) << endl;
  5. }
  6. int main(int argc, char* argv[])
  7. {
  8. char str1[10] = "I Love U";
  9. arrayTest(str1);
  10. return 0;
  11. } )}>~Y;A jv|,@On`QS
+lWz8!Py WhlQ#G

j,&.j+\
&qD5< ='
プログラムの出力結果は4です.ありえないでしょう?
eJ8y;SP
J/`j{1H
恐ろしい数字で、針の長さについて前述しました!
N;(S/T%[1
j~]|BOosC
結論1では,データ名は配列というデータ構造を内包し,arrayTest関数内でstrは配列名であるが,sizeofの結果はポインタの長さであるのはなぜであるかを指摘した.なぜなら、
GIa{N `a
fMmYL8BV>
(1)配列名が関数パラメータとして機能する場合、関数体内では、それ自体の内包を失い、ただのポインタである.
U$f5#KeNpW
dP#w?x|?
(2)残念ながら,その内包を失うとともに,その定数特性を失い,自己増加,自己減少などの操作が可能となり,修正される.
^-KuZxnBXz
+5 _CF#p
したがって,データ名が関数形パラメータとして機能すると,その全体が通常のポインタに転落する!その貴族の身分は奪われ、4バイトしか持たない庶民になった.
;x4xBT6M
.GQF'8A
以上が結論4です.
Eg~#: U
qaI=|lbA
終わりの言葉
sbRn
kmlqO b
最後に、筆者は再び深い希望を表して、私と私の同道の中の人が本当に慎重な研究態度で開発中の問題を真剣に考えることができることを望んで、このようにしてやっと私たちの中でマスター級のプログラマー、トップクラスの開発書籍を生むことができます.米国の鬼子の開発書を手にするたびに、私たちは遅れていると感慨を禁じ得ない.