ポインタの関数での受け渡しについて

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
しろ

ポインタの関数での受け渡しについて

#1

投稿記事 by しろ » 15年前

こんにちは。ポインタについてわからないところがありましたので、質問させていただきます。

int x[5]という配列があったっとして、この配列をmain関数内でfunc関数で渡すとし、func関数は次で宣言されているとします。

void func(int *z);

このとき配列の先頭へのポインタxを渡したとすると、z[0]=*z、z[1]=*(z+1)などとなりますよね。

次に同じfunc関数でxではなく、&x(すなわちxという配列全体へのポインタ)を渡すとします。このときfunc関数は次のように宣言さ

れます。

void func(int (*z)[5]);

このとき、(*z)[1]と*(z+1)は違うものだと思うのですが(なぜならzはxの配列全体を指すが、(*z)[1]は配列xの中の1番目の要素に

アクセスしている。しかし*(z+1)はそもそも配列xの外側? にアクセスしている∵zはxの配列全体をさしているので)、どうもこの

あたりのところがはっきりしません。

何を質問しているのかも微妙によくわからないところではあるのですが、このあたりについてご教授いただけますと大変助かります。

何卒よろしくお願いいたします。

ちなみにこのときのfunc関数の宣言void func(int (*z)[5])もvoid func(int (*z)[/url])のように書いてはいけないのでしょうか?

シエル

Re:ポインタの関数での受け渡しについて

#2

投稿記事 by シエル » 15年前

>次に同じfunc関数でxではなく、&x(すなわちxという配列全体へのポインタ)を渡すとします。
>このときfunc関数は次のように宣言さ れます。
>void func(int (*z)[5]);

こんな説明どこに書いてありましたか?

bagu

Re:ポインタの関数での受け渡しについて

#3

投稿記事 by bagu » 15年前

>>&x(すなわちxという配列全体へのポインタ)

これはx[0]のポインタのポインタでは?

へろりくしょん

Re:ポインタの関数での受け渡しについて

#4

投稿記事 by へろりくしょん » 15年前

>このとき、(*z)[1]と*(z+1)は違うものだと思うのですが(なぜならzはxの配列全体を指すが、(*z)[1]は配列
>の中の1番目の要素に
>アクセスしている。しかし*(z+1)はそもそも配列xの外側? にアクセスしている∵zはxの配列全体をさしてい>るので)、どうもこの
>あたりのところがはっきりしません。

(*z)[1] と *(z+1) は同じです。

zの型はint型の配列(要素数5)へのポインタです。 要するに配列へのポインタですね。
ですから、*zの型はint[5]となります。


>ちなみにこのときのfunc関数の宣言void func(int (*z)[5])もvoid func(int (*z)[/url])のように書いてはいけないのでしょうか?

いけません。zはあくまでも要素数5の配列へのポインタですから、要素数を省略することは許されません。
要素数が省略可能なのは配列を渡す時、その最外周の要素だけです。


追記です。
>(*z)[1] と *(z+1) は同じです。

同じな訳ないですね。
zの型はint(*)[5]なわけですから、
(*z)[1]の型はintです。
*(z+1)の型はint[5]です。

失礼いたしました。 画像

しろ

Re:ポインタの関数での受け渡しについて

#5

投稿記事 by しろ » 15年前

>シエルさん、>baguさん

「明解C言語 実践編」という書籍にそのように記載されていました。
他の書籍を見ると、x[0]のポインタのポインタと書かれています。

>へろりさん

「明解C言語 実践編」の別のページでは、zと*(z+i)は同じものとC言語で規約されていると書いてありました。

わたしも上の場合は(*z)[1]と*(z+1)は違うものだと考えました。

すでに書籍内で矛盾が発生しているわけですが……

へろりくしょん

Re:ポインタの関数での受け渡しについて

#6

投稿記事 by へろりくしょん » 15年前

ちょっと分かりにくいので補足します。
void func(int hoge[/url][4])
{
    ...
}

main()
{
    int foo[5][4];

    func(foo);
}
こういう場合に省略可能です。
この時の引数 hoge[0] の型は int[4] となります。
つまり、int型の要素数4の配列の要素数5の配列である変数 foo[5][4] は、関数 func() に渡される時
int型の要素数4へのポインタに読み替えられます。

故に、関数 func() は次のように書くことも出来ます。

void func(int (*hoge)[4])


という事です。

へろりくしょん

Re:ポインタの関数での受け渡しについて

#7

投稿記事 by へろりくしょん » 15年前

>「明解C言語 実践編」の別のページでは、zと*(z+i)は同じものとC言語で規約されていると書いてあり
>ました。
>わたしも上の場合は(*z)[1]と*(z+1)は違うものだと考えました。

zと*(z+i)は同じです。
(*z)[1]と*(z+1)は違います。

そして
(*z)[1] と z も違います。

MNS

Re:ポインタの関数での受け渡しについて

#8

投稿記事 by MNS » 15年前

いろいろな回答があり、混乱されているかもしれませんが、
おっしゃるとおり、int x[5]; と宣言されたとき、
&xは「配列へのポインタ」を示します。
ですから、
void func(int (*z)[5]);
という書き方は正しいはずです。

ただし、&xは、「配列へのポインタ」でも、
「『5つの要素をもつ配列』へのポインタ」です。
ですから、要素数の省略はできません。

ところで、
>(*z)[1]と*(z+1)は違うものだと思うのですが
これは、同じものだとどこかに書いてあったのでしょうか?
ご指摘の通り、zは配列へのポインタであるので、
*(z+1)の型はint*になってしまい、
かつそれは、配列へのポインタであるzのアドレスの、
zのサイズ分の加算したところのアドレスを指しますから、
どうなっても(*z)[1]には相当しないはずです。
恐らく、(*z)[1]に相当するのは*(*z+1)でしょう。

へろりくしょん

Re:ポインタの関数での受け渡しについて

#9

投稿記事 by へろりくしょん » 15年前

>ご指摘の通り、zは配列へのポインタであるので、
>*(z+1)の型はint*になってしまい、

配列へのポインタですから、*演算子によりポインタ解決したら、*(z+1)の型はやっぱりint[5]ではありませんか?


追記です。
>かつそれは、配列へのポインタであるzのアドレスの、
>zのサイズ分の加算したところのアドレスを指しますから、

これはちょっと誤解を招きそうです。

正確には、(z+1) は zのアドレスからsizeof(*z)分の加算したアドレス。 ですね。 画像

シエル

Re:ポインタの関数での受け渡しについて

#10

投稿記事 by シエル » 15年前

zはポインタのポインタなので、*(z+1)の型はint*ではないですかね?
でもそうだとすると、この計算はまったく関係ないメモリを参照してしまう気が。。。
よく分からなくなってきました。。。まだまだ未熟者ですね。

アビゲイル

Re:ポインタの関数での受け渡しについて

#11

投稿記事 by アビゲイル » 15年前

(*z)[1] →z[0][1] 
**(z+1) →z[1][0] #表現が悪かったので修正しました
void func(int (*z)[5]);
z[0]の型はint[5]だとしたら、z[1]は1int[5]分増加しますね
z[0]の型はint[/url]だとしたら、z[1]は1int[/url]分増加し・・・増加分が分かりませんね。
画像

MNS

Re:ポインタの関数での受け渡しについて

#12

投稿記事 by MNS » 15年前

>配列へのポインタですから、*演算子によりポインタ解決したら、
>*(z+1)の型はやっぱりint[5]ではありませんか?
そうでもないみたいなんです。
zが指すのは int (*)[5]なんですが、
では、*zの型はint [5]か、というと、これがint*なんです。
これは恐らく、式の中での配列名がその配列の先頭要素のアドレスのポインタになる、
という原則に従った結果なんでしょうが、
そういうわけで、*(z+1)の型はint*を指すみたいです。

>正確には、(z+1) は zのアドレスからsizeof(*z)分の加算したアドレス。
すいません、アドレスの計算がまだ曖昧なまま書いてしまいました。
補足ありがとうございます。

へろりくしょん

Re:ポインタの関数での受け渡しについて

#13

投稿記事 by へろりくしょん » 15年前

>zはポインタのポインタなので、*(z+1)の型はint*ではないですかね?

zは配列へのポインタです。 たとえば。
void func(int (*z)[5])
{
    ...
}
main()
{
    int x[5];
    func(&x);
}
[pre]

というコードで言うと、zのアドレスと&[0]のアドレスは同じです。
配列xの先頭要素へのポインタのポインタであるとするなら、&(&x[0])となりますが、こんな事は出来ませんね。 誤解を恐れずに言うならば、配列xはスタックに確保されています。 &x[0] はスタック上のアドレスです。
では、そのポインタとなると、いったいどこのアドレスになるのでしょう。 実体を飛び越えてますね。


配列へのポインタですから、ポインタ解決したらやっぱり出てくるのは配列です。

しろ

Re:ポインタの関数での受け渡しについて

#14

投稿記事 by しろ » 15年前

なかなか混乱してきました。
今仕事中ですので、帰宅次第また書き込みます。

それまでに皆様のコメントは再読させていただきます。

へろりくしょん

Re:ポインタの関数での受け渡しについて

#15

投稿記事 by へろりくしょん » 15年前

>これは恐らく、式の中での配列名がその配列の先頭要素のアドレスのポインタになる、
>という原則に従った結果なんでしょうが、

式の中で配列名が現れた場合先頭要素へのポインタに読み替えられる規則にはいくつか例外があります。

1つに、sizeof演算子のオペランドとして現れた場合。
2つに、配列初期化時の文字列定数。
そして最後に、&アドレス演算子のオペランドとして現れた場合です。

今回のケースはこの3番目に該当すると思われますが。

MNS

Re:ポインタの関数での受け渡しについて

#16

投稿記事 by MNS » 15年前

ふうむ、コンパイラの解釈に従うならば、
VS2010の場合ですと、*zの型はint*、同じく*(z+1)の型はint*でした。
C++の仕様がそうなのかは分かりませんが。

どうもこうも、zが配列へのポインタでありながら、
コンパイラがこれを配列として扱うかどうかなんでしょうが、
*zの型がint*と解釈された以上、
VS2010では、zはあくまで配列だという解釈なんでしょう。
(これは例外には当てはまりませんしね)

へろりくしょん

Re:ポインタの関数での受け渡しについて

#17

投稿記事 by へろりくしょん » 15年前

ちょっと、実際に書いてみました。

環境はVisual Studio .NET 2003 です。
void func(int (*z)[5])
{
    *z = NULL;    //......(1)エラー
    (*z)[0] = 0;  //......(2)ok
    z = NULL;     //......(3)ok
}
main()
{
    int x[5];
    func(&x);    //......(4)
}
*zがint*であるなら、(1)で文法エラーになる理由が分かりません。

zはあくまでも配列へのポインタで、*zは配列だと思うんですけどねぇ。
一応IDEのウォッチリストで見てみましたが、*zの型はint[4]と言ってましたし。

先の件で例外として上げたのは、(4)の部分ですね。 ここでは、先頭要素へのポインタのポインタとは読み替えられない。 と。

たかぎ

Re:ポインタの関数での受け渡しについて

#18

投稿記事 by たかぎ » 15年前

> VS2010の場合ですと、*zの型はint*、同じく*(z+1)の型はint*でした。

Visual Studio 2010 Professional Editionで追試してみました。
テストコードは下記のとおりです。

#include <iostream>
#include <typeinfo>

void func(int (*z)[5])
{
std::cout << typeid(*z).name() << std::endl;
std::cout << typeid(*(z+1)).name() << std::endl;
}

int main()
{
int x[5];
func(&x);
}

結果、両方とも int [5] が出力されます。
GCCでも同じ結果が得られました(文字列の形式は異なりますが、意味は同じです)。

MNS

Re:ポインタの関数での受け渡しについて

#19

投稿記事 by MNS » 15年前

すいません、早とちりだったみたいです。

私がそう思ったのは、
int a[5] = {1,2,3,4,5};
int (*pa)[5] = &a;
int* pp = *pa;
というコードが可能だったからです。
(こういった形でしか*paの値を受け取れなかった)
でもよく考えると、
(*pa)はint[5]の配列であり、
その(*pa)自体が配列名として解釈され、
原則に従い先頭要素を指すポインタ(=int*)となった。
ということですかね。

しろ

Re:ポインタの関数での受け渡しについて

#20

投稿記事 by しろ » 15年前

すいません。返信が遅くなりました。

結局zは配列へのポインタで、*zは配列そのもののエイリアスとなるので(*z)[1]、(*z)[2]で、配列の中身にアクセスできるという解釈でよろしいでしょうか?

また*(z+1)はzのアドレスからsizeof(*z)後ろにある配列へのポインタということでしょうか(なにが入っているかはわかりませんが)。

初級者

Re:ポインタの関数での受け渡しについて

#21

投稿記事 by 初級者 » 15年前

zがポインタのとき、*zは*(z+0)とも書けますね。
これは、とりもなおさずz[0]と同じ意味ですね。
つまり、*zはその配列の先頭要素の実体そのものです。
理解できますか?

しろ

Re:ポインタの関数での受け渡しについて

#22

投稿記事 by しろ » 15年前

今回zは配列全体へのポインタで(結果的にzはz[0]のアドレスと同じなんでしょうが)、そこから*zは配列の先頭要素の実体になるのではないのですか?

フリオ

Re:ポインタの関数での受け渡しについて

#23

投稿記事 by フリオ » 15年前

 
 zは&aなので、*zはaです。
ですから、
a == (*z)
です。
したがって、配列の先頭要素の実体は、
(*z)[0] == *(*z) == **z
となります。
 
#include <stdio.h>

int main(void)
{
    int a[5], i;
    int (*z)[5] = &a;
    
    for(i = 0; i < 5; ++ i) a = i;
    for(i = 0; i < 5; ++ i) printf("%d ", *(*z + i));
    printf("\n%d\n", **z);
    printf("%d\n", *z == a);
    return 0;
}

MNS

Re:ポインタの関数での受け渡しについて

#24

投稿記事 by MNS » 15年前

私のせいで、色々と誤解を招いてしまったのかも知れません。
申し訳ないです。

zは配列aへのポインタであり、zとz[0]のアドレスは同じですが、
まず、*zの型はint[5]です。つまり、*zは「配列a」自体を指します。
原則にしたがって、式の中で配列名が登場した場合、一部の例外を除いて、
それはその配列の先頭要素のアドレスを指すポインタとなります。
つまり、*zは、*zが示す配列(=配列a)の先頭要素のアドレスを指すポインタ、
&a[0]となります。("式の中でのa"とも言える)

要するに、*zは、配列aの実体であると言えますが、
式の中では、原則に従って配列の先頭要素を指すポインタとなってしまう、
ということです。

へろりくしょん

Re:ポインタの関数での受け渡しについて

#25

投稿記事 by へろりくしょん » 15年前

>結局zは配列へのポインタで、*zは配列そのもののエイリアスとなるので(*z)[1]、(*z)[2]で、配列の中身にア>クセスできるという解釈でよろしいでしょうか?

そう考えて問題ありません。


>また*(z+1)はzのアドレスからsizeof(*z)後ろにある配列へのポインタということでしょうか(なにが入って
>いるかはわかりませんが)。

正確には、*(z+1)はzのアドレスからsizeof(*z)後ろにある配列の先頭要素へのポインタ。 です。


>今回zは配列全体へのポインタで(結果的にzはz[0]のアドレスと同じなんでしょうが)、そこから*zは配列の
>先頭要素の実体になるのではないのですか?

ちょっと言葉遊びの重隅をつつくようで大変心苦しいのですが、先頭要素の実体と聞くと、まるでポインタの配列のような印象を受けるのは私だけでしょうか。

*z は配列の実体。 とした方が誤解が無いかと。


今回のケースは多次元配列を考えるとわかりやすいかと思います。
void func(int (*z)[5])
{
    ...
}

main()
{
    int x[10][5];

    func(x);
}
この時、*(z+5) は、x[5] であり、&x[5][0] です。
また、(*(z+1))[2] は、x[1][2]です。 この時は、添え字演算子[/url]は間接演算子*よりも優先順位が高いため、(*(z+1))と括弧でくくる必要があることに注意してください。

初級者

Re:ポインタの関数での受け渡しについて

#26

投稿記事 by 初級者 » 15年前

はい。そういう印象を受けるのはあなただけです。

がくせい

Re:ポインタの関数での受け渡しについて

#27

投稿記事 by がくせい » 15年前

初級者なら初級者らしくね

初級者

Re:ポインタの関数での受け渡しについて

#28

投稿記事 by 初級者 » 15年前

では、がくせいの希望に応えて、初級者らしい話をしようかな。

pがある型へのポインターであるとき、*(p+0)、つまり*pはp[0]の
シンタックスシュガーなんだよね。
ってことはだよ、*pはpが指している先頭領域の実体であるってことなの。
わかる?

pがある型へのポインターである、っていうのはね、
例えばint型へのポインターでもいいし、
int型へのポインターへのポインターでもいいってことなの。

つまり、最初に書いた「先頭領域の実体」っていうのは、
(int型のような)即値の場合だってあるし、
(int *型のような)ポインター値の場合だってあるわけ。

実体がポインター値の場合がある、という点を理解できていない人が
いるみたいだよね。誰とは言わないけど。

palladium

Re:ポインタの関数での受け渡しについて

#29

投稿記事 by palladium » 15年前

>pがある型へのポインターであるとき、*(p+0)、つまり*pはp[0]の
シンタックスシュガーなんだよね。

これはvoid型でも成立するのでしょうか

たかぎ

Re:ポインタの関数での受け渡しについて

#30

投稿記事 by たかぎ » 15年前

> これはvoid型でも成立するのでしょうか

それをいうなら、関数型や(void以外の)不完全型でも同じことがいえます。

追記:
同じはいいすぎですね。
関数型の場合、*(p + 0) や p[0] はコンパイルできませんが、*p ならOKなので。 画像

しろ

Re:ポインタの関数での受け渡しについて

#31

投稿記事 by しろ » 15年前

皆様のおかげでようやく飲み込めました。
やはりポインタはなにかと難しいですね。
あとはいろいろとプログラムを書いて、実践しようと思います。

皆様どうもありがとうございました。これにて解決とさせていただきます。

増田

先日

#32

投稿記事 by 増田 » 15年前

先日、こちらへメールさせていただいた増田と申します。

先日、メールしましたが、管理人様から返信がありませんでしたので、こちらに書き込みさせていただきます。

今、アクションゲームを制作していて、Curse'sSoul(仮名)のプログラムを参考にさせていただきたいので、見せていただけないでしょうか?

宜しくお願いします。

バグ

Re:先日

#33

投稿記事 by バグ » 15年前

管理人さんも社会人ですから、すぐに返信が出来ないこともあります。それを理解せずにこんなスレをたてるのはいかがなものかと思いますよ。

どんなに言葉が丁寧だろうが、こんな自己中心的な内容のスレを作成したり、ソースを丸ごと寄越せってのは非常に失礼だというのは理解していますか?

そもそも、どんなことをしたくて参考にしたいの?というか、何が分からないの?

これをきちんと自分の言葉で具体的に質問できないようであれば、ソースもらっても全くの無駄です。

クローバ

Re:先日

#34

投稿記事 by クローバ » 15年前

貴方はプログラムの事以前の基本的なことが欠落していると思うのですが。

メールと言うもの自体そもそも直ぐに確認してもらえるものではないのですから。
書き込みを見ていると相当早い段階で催促しているように思えますが…

他人を助けて飯を食っているような人ならまだしも、管理人さんは社会人ですので忙しいでしょう。
そんな中で毎日こまめにメールをチェックしてかつ返信できるとお思いなのでしょうか?
携帯電話のメールとは違うのですから、そこら辺の事を考えた上でのメールのやりとりは必要かと思いますが。
時には1か月以上待つ事もできなければならない事だって仕事の状況によってはあり得るわけですし。

それと、公開されていないソースにはそれなりの事情があってのことだと思います。
ソースコードが見つからなかったり、すでに消失していることだってあり得るでしょうし。
色々な事情があるわけです。
それを欲しいとメールを送り、尚且つ返信がすぐに来ないからと掲示板でトピックまで立てる。

もし私の所でその様な事をされたとしたら、たとえソースを探し出して渡してあげようと思っていたとしても渡す気はなくなるでしょう。

プログラミングの前に色々と一般社会について学ばれてはいかがでしょうか?

増田

Re:先日

#35

投稿記事 by 増田 » 15年前

>バグさん cloverさん

ご指摘ありがとうございます。
そして、すみませんでした。
提出があるので焦ってしまい、自分の都合だけでこのスレを立ててしまいました。

一応、書いてあるとおりメールさせていただいてあるので、待ってみたいと思います。
そして、自分のできる限りの範囲は、制作しておきたいと思います。 画像

Dixq (管理人)

Re:先日

#36

投稿記事 by Dixq (管理人) » 15年前

> 増田さん

遅くなってすみません。
10月の頭までに提出の必要があるゲーム制作がある為、早急にソースが必要だという事ですね。

専門学校か何かでしょうか?
アクションゲームでなければならないという決まりはあるのでしょうか?

私はゲーム制作を専門に学んだ人間ではなく、単に好きで作っているだけなので、
アクションゲームについては試しに作った程度でしかありません。
なのでソースコードは参考にされない方が良いように思います;

ゲーム制作って需要があるわりにウェブに参考になる情報が少ないですよね。
しかし、参考になるジャンルもありますし、書籍であればある程度充実しています。
本を買うとか、参考にできるジャンルに変更するなどは不可能でしょうか?

Dixq (管理人)

Re:先日

#37

投稿記事 by Dixq (管理人) » 15年前

あれ、のんびり書いてたら行き違いになってしまいました、すみません;

あ、後、これは参考になるかどうかは解りませんが、
どんなコードであれ、動作する物が存在していさえすればいい!というのであれば
サンプルプログラミングの館に(東方+魔界村)÷2というゲームが置いてありますので
どうしても見たいという事であればどうぞ。。。
ボツシリーズなのでサポートは出来ませんが;

softya

Re:先日

#38

投稿記事 by softya » 15年前

Curse'sSoul(仮名)の基本的な所は龍神録にも搭載されていると思います。
http://dixq.net/rp/
アクションゲームの基本的な処理は、DXライブラリ本家のサンプルとか。
http://homepage2.nifty.com/natupaji/DxL ... ogram.html
Dixq (管理人)さんの「ゲームプログラミングの館」が参考になると思います。
http://dixq.net/g/

Dixq (管理人)

Re:先日

#39

投稿記事 by Dixq (管理人) » 15年前

私もDXライブラリのサンプルプログラムはよく熱心に見ました^^;
Cを学び始めたころ、ワイプなどのプログラムは知らなかったので、非常に参考になった覚えがあります。

ただ、

サンプルとしてのプログラム

大きなゲームのパーツとしてふさわしいプログラム

にするには大きな壁があり、よく戸惑いました。
DXライブラリのサンプルプログラムも一応「動く」ことは動きますが、
必ずしもそのままゲームに使えるプログラムでは無い事が多かったように思います。
(例えばジャンプとか)

参考書のような本も、
サンプルとしてのプログラム

ゲームのパーツとしてのプログラム
はステップアップが大きすぎることが多いですよね。

ということで、「DXライブラリのサンプルを見ただけではゲームが作れない」と悩むことがあったとしたらそれは自然な事だと思います。
サンプルはあくまでサンプルですからね。
一つ一つの事を十分理解しながら、自分のゲームに少しずつ機能を追加していくのが一番だと思います。

閉鎖

“C言語何でも質問掲示板” へ戻る