質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ポインタ

ポインタはアドレスを用いてメモリに格納された値を"参照する"変数です。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

Q&A

解決済

6回答

15816閲覧

C言語のポインタを使った要素のコピーのプログラムがわかりません

ababab

総合スコア47

C

C言語は、1972年にAT&Tベル研究所の、デニス・リッチーが主体となって作成したプログラミング言語です。 B言語の後継言語として開発されたことからC言語と命名。そのため、表記法などはB言語やALGOLに近いとされています。 Cの拡張版であるC++言語とともに、現在世界中でもっとも普及されているプログラミング言語です。

ポインタ

ポインタはアドレスを用いてメモリに格納された値を"参照する"変数です。

配列

配列は、各データの要素(値または変数)が連続的に並べられたデータ構造です。各配列は添え字(INDEX)で識別されています。

0グッド

0クリップ

投稿2015/07/21 12:58

「配列x1[ ]の各要素を順番に調べ、奇数のみをx2[ ]にコピーしたいとき、次のプログラムの空欄を埋めなさい」
という問題で、

int *p1,*p2;
p1 = x1;
p2 = x2;
while ( (1) > 0){
if( (1) %2 == 1){
(2);
(3)++;
}
(4) ++;
}

となっているのですが、
(3)(4)は参照するアドレスを1移動するという意味で
(3)*p2
(4)*p1
と考えていました。

ですが、正答は
(1)*p1
(2)*p2 = *p1
(3)p2
(4)p1

です。
(1)と(2)に関してはなぜ直接p1、p2の要素数を調べるのではなくポインタの変数を使うのか、
(3)と(4)に関してはなぜアドレスを1移動させてはいけないのか、
(文章が少しおかしいかもしれませんが...)よく理解できませんでした。

なぜこのような解答になるのか、教えていただきたいです。
よろしくお願いいたします。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答6

0

この問題が分からないということは、
ポインタのアドレスと指示先の変数の区別がつかないということです。

p1とp1の区別ができれば、もうほとんど解けている問題です。
p1と
p1の具体的な値は何か、書き出すだけで理由も分かります。

C言語のポインタは体育の逆上がりのようなつまづきポイントなので、
小手先だけで答えを合わせるだけでなく、じっくり腰をすえて取り組んでください。

番地のたとえはもうご存じだとは思いますが、ただ知っているというだけでなく、
コードを見ても瞬時にイメージできるようになってください。

投稿2015/07/21 13:19

LLman

総合スコア5592

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

なんだか、何を基準に判断して良いのか。という部分から完全に混乱しているように見受けられます。

まず、変数を使うには宣言が必要です。
そして宣言によって用意された変数は、それぞれ記述によって振る舞いを変化させます。
ですので、それぞれの記述の見分けが必要になります。

C

1int value1 = 1; //変数(int):value1 に 1を代入 2int value2 = 2; //変数(int):value2 に 2を代入 3int* pValue1 = &value1; //変数(intのポインタ):pValue1に value1のアドレスを代入 4int* pValue2 = &value2; //変数(intのポインタ):pValue2に value2のアドレスを代入 5 6//この2つは同じ意味になる。 7value1 = value2; 8*pValue1 = *pValue2; 9 10//これはアドレスの代入 (この後に *pValue1と書くと value2の値を指すようになる) 11pValue1 = pValue2;

いろいろ書いてますが、上記が基本です。
指定した型の変数には、値やアドレスを1つ代入できます。

上記の場合に、「int変数の値」を表現するには2つの形式があります。
1つめは「value1, value2」で、これは "int変数の値" を意味します。
2つめは「*pValue1, *pValue2」で、こっちは "intのポインタ変数のアドレスの中身(=int変数の値)" です。

「int変数のアドレス」を表現するにも2つの形式があります。
1つめは「&value1, &value2」で、これは "int変数のアドレス" を意味します。
2つめは「pValue1, pValue2」で、こっちは "intのポインタ" です。

通常の変数とポインタ変数とで、修飾のされ方と修飾された場合の意味合いが異なる点に注意してください。

&修飾によるアドレス参照は、指定型の変数が格納されているアドレスを表します。
その意味で、基本的にポインタと等価のものです。
(※アドレス参照はポインタ変数ではなく、単に変数の中身を格納するアドレスを得られるだけです。ここにアドレスを渡しても変数ポインタのようには使えません。変数と値の区別も忘れないでください。)

ポインタ変数の方は、指定型の変数の格納されたアドレスそのものを格納する変数です。
つまり、変数のようになにかの値(データ)を格納しているけれども、実際に扱っている内容はデータではなくアドレスになります。

ポインタの理解をするには、これらの違いを区別できないと先に進めません。
まずは、この区別をつけられるようにしてください。


次に、配列の場合。

C

1int array[] = {0,1,2,3,4}; //変数(intの配列):array に 0〜4を設定 2int* pArray = array; //変数(intのポインタ):pArrayに arrayの先頭アドレスを代入 3 4//配列の変数とポインタは同じように振る舞う 5if(pArray[1] == array[1]) 6 printf("true¥n"); 7else 8 printf("false¥n");

配列の場合は、指定の型の変数の場合と少し趣が異なります。
添え字の括弧が付く・付かないの違いで、配列に格納された値を示したりポインタになったりします。
またポインタ変数の方は、配列の添え字なしのものと同じ振る舞いをします。

このあたりの区別がつけられるようになりましょう。
分かっていないと、ポインタの加減算は意味不明になると思います。

投稿2015/07/21 19:35

編集2015/07/21 22:10
退会済みユーザー

退会済みユーザー

総合スコア0

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ababab

2015/07/23 12:12

回答ありがとうございます。 while文では、配列の要素数を2で割ったとき..という処理ですが、個数ならばなぜアドレスが格納されている*p1が当てはまるのでしょうか? それなら、配列が格納されているp1で割れば良いのではないかと思ったのですが...
退会済みユーザー

退会済みユーザー

2015/07/24 13:02 編集

返答が遅くなってすみません。 設問は、配列に格納された値について奇数要素のみ x2[] に収めるということですので、 「*p1」つまり「x1[n]」に入っている値が奇数だったら…というのが、if文の判定です。 ただ、while文の *p1>0 はちょっと微妙な判定ですよね。 文字列だったら終端文字で ¥0 が入るので(←僕も最近使ってないので、明示しないと入らなかったかも…)終端判定できますが…そこは僕も納得がいきません。 ちなみに、「配列の中に入っている要素」というのは「値」のことで「要素数, 個数」という意味ではないと思いますよ。 また、p1を半分にするということはアドレス値を半分にするという処理になります。 配列の先頭アドレスは0番地から始まると決まっているわけではないので、例えば100〜199番地の100バイトにデータが入っている時に、先頭アドレスを半分に割ったとすると50番地になってしまいます… これは違いますよね。 配列にしろ変数にしろ、メモリの空きのどこかに適宜データが配置されます。 仮に、質問者さんの言うように要素数の半分のところを計算しようとした場合に前述の100バイトのデータの半分の位置のアドレスはというと… 100番地+(要素数:100 / 2)って感じになります。(答え:150番地) while文の判定は、僕にもよく分かりません。 お手数なのですが、できれば質問者さんの力で解明して、僕にも教えていただければ助かります。 よろしくお願いいたします。
guest

0

(3)と(4)に関してはなぜアドレスを1移動させてはいけないのか、

こちらについては、他の方が回答しているので省略します。

(1)と(2)に関してはなぜ直接p1、p2の要素数を調べるのではなくポインタの変数を使うのか、

こちらについては
int i,j;
i=j=0;
と言う宣言があれば、
(1)p1[i]
(2)p2[j]=p1[i]
(3)j++
(4)i++
のように配列の要素を操作しても同じような動きをします。

ポインタについての理解を問いたいからという出題の意図が主かもしれませんが、
動作速度向上のために行う理由もあります。

p1[i]と*(p1+i)、p2[j]と*(p2+j)が同じ動きなので、p1[i]やp2[j]といった書き方をすると
p1[i]やp2[j]で要素を参照するときと、i++やj++で添字を操作するときとでそれぞれ2回加算をすることになります。

それよりは、p1++やp2++として加算回数を減らして動作速度を(ちょっと)早くするといったことをします。

投稿2015/07/21 17:16

kutsulog

総合スコア985

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

0

ベストアンサー

(1)と(2)に関してはなぜ直接p1、p2の要素数を調べるのではなくポインタの変数を使うのか、

p1にはp1 = x1; この記述によってx1[0]のメモリ上の位置が代入されています。
p1これでx1[0]の値を参照します。
ループ1回目の
p2 = *p1; は(x[0]が奇数ならですが)x2[0]=x1[0];と同じです。

(3)と(4)に関してはなぜアドレスを1移動させてはいけないのか、

アドレスを1つ移動させるという考え方で問題ないです。
正答の記述こそが正にそれを行っていたということですね。

ただ*p1++;でも結果としては同じだったような……
確かp1で指すデータを取り出し、その後p1を1つ進めるって処理になったはずです。
前半分の処理が全く無駄ですが、結果は同じになるのだったかと
(2)と(3)をまとめて *p2 = *p1++; みたいな記述がシンタックス的には有効(p2にp1を代入した後、p1をインクリメントするような振る舞いになるはず)だったと思いますが、ソースの可読性を落とすだけなので忘れてしまうのが懸命です。
※最後のはちょっと自信ないです。すみません。


ところで、質問には直接関係なくて申し訳ないです。
(1)が*p1とのことですが、という事は「while ( *p1 > 0)」?。
x1[]に0以下の値が存在するケースを想定しており、あったら(作業の途中なのに)そこでループ抜けちゃうんですかね?
試験でこの問題に当たったら(1)に何入れるか悩みそうです。

またc言語に触れなくなって久しいので記憶が曖昧ですが、この記述って配列末尾でエラー出ませんでしたっけ?
x1[]内の値は最後のみ0で他は0以上の整数です。って設定があったり
「while(*p1)」か「while(*p1 != NULL)」の書き間違いでしょうか?

投稿2015/07/21 16:44

hirohiro

総合スコア2068

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ababab

2015/07/23 12:17

回答ありがとうございます。 *p1でp1の中身を指し、p1は配列自体を参照するということですが、 while文の2週目以降になったとき、コピーする場所を移動するという意味ではp1++は理解できました。 しかしwhile文で配列の要素数を2で割るときは、なぜp1ではいけないのか...p1は配列自体を指すのに、なぜp1ではいけないのかという疑問が残っています... 理解に乏しくて申し訳ないです。
hirohiro

2015/07/23 12:51 編集

p1には配列自体というより、配列のために確保されているメモリ上の位置情報が入っています。「データは駅前の右から2番目ロッカーに入れている紙に書いておいた」のようなものです。(データがint型なので、p1には2が入ったかもしれません。しかしとにかく実際のデータはロッカーを開けて紙を見るまで分からないです) プログラムは配列を作成する際に、メモリ上の空いている場所に配列で利用するスペースを確保します。p1 = x1; これでp1に入るのはx1[]を作成する際に確保したメモリの位置情報です。そのためプログラムを起動するたびに変わります。 ですのでp1には実際プログラムを起動してx1[]が作成されるまで、何が入るか予測できません。そうなると、p1を2で割った余りという数値は、x1[]に入っているデータとは無関係の予測できない値ということになります。(0か1ではありますが、どちらになるかはx1[]の中身とは無関係)
guest

0

なんだか自信がないですが間違ってたらごめんなさい。

int *p1;

というポインタの定義ですが、

この場合、p1は値のアドレス
*p1は値そのものをさします。

ですから、例えば
.......
int *p1,nx=5;
px=&nx;
printf("*px=%d\n",*px);
.......
という場合
*px=5
と出力されます。
キモは
px ならnxのアドレスを表わす
*pxならnxのアドレスの中身を表わす
ということかな…

でで、質問者様の問題を見てみればあとは自ずと...
頑張ってください!
あと間違ってたらご指摘お願いします。

投稿2015/07/21 13:46

RyanFoley

総合スコア10

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

ababab

2015/07/21 13:54

回答ありがとうございます。 「*」が付いていたら番地、「p1」のように&も*も付いていなければ値そのものを指していると勘違いしていました... 例えば s = 4,k = &sなら、*kは4ということでよろしいでしょうか?
RyanFoley

2015/07/21 14:35

ムム( ゚ε゚;) これは....汗 恐らくいいような。(自信無) sとkがどのように定義されているのかわかりませんが、kがポインタとして宣言されている必要があるような気がします。    int *k; というやつですね。 こう宣言されていれば、いいと思います‼ 間違えていたらご指摘お願いします。
kutsulog

2015/07/21 16:27

int s;int *k; と宣言している前提であれば、あっていますよ。
RyanFoley

2015/07/21 23:33

ご指摘ありがとうございます‼
guest

0

(*p1)++ なら p1が指し示す位置の内容の中身を1加算
p1++ ならp1が指し示す位置を1加算
*p1++ なら p1が指し示す位置の内容を参照した後p1が指し示す位置を1加算
(演算子の優先順位の関係です)

この違いが理解できないと、この先ポインタではかなり苦労すると思います。

投稿2015/07/21 13:30

lazy_tsan

総合スコア51

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問