大学の講義でプログラミングの勉強を行っているのですが配列とポインタでつまづいてます。
N個の要素を持つ整数型配列aに適当なデータを初期化で代入し、その後,そのデータを、配列bには順方向、配列cには逆方向に、ポインタx,y,zを用いて代入し、最後に配列a,b,cの要素を出力せよ。また、代入にはインクリメントとデクリメントを繰り返す方法を用いることと、代入と出力は別々のfor文を用いるという条件があります。
自分が作ったプログラムだとエラーはでないのですが数値がおかしくなります。
どこが間違っているのかご指摘をよろしくお願いします。
配列とポインタが分からない
Re: 配列とポインタが分からない
- 14行目で、ポインタの演算で許される範囲の外にポインタを移動させている
- 17行目で、最後の要素の1個次を指しているポインタxおよびyをデリファレンスしている
(デリファレンスしなければ、配列の最後の要素の1個次を指すポインタを作ることは問題ありません)
通常はこのz--;による範囲外のポインタの作成が問題になることはないでしょうが、
17行目で配列の要素を読み取らず適当な値をprintfに渡すのはよくないですね。
きちんとポインタの初期化とインクリメントを行うようにするか、
という条件なので、出力には素直に添字演算子を使うといいでしょう。かも さんが書きました:また、代入にはインクリメントとデクリメントを繰り返す方法を用いることと、代入と出力は別々のfor文を用いるという条件があります。
代入において、最後のループではデクリメントをしないことで未定義動作を回避するとさらにいいでしょう。
さらに、インデントを整えるとより良くなるでしょう。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: 配列とポインタが分からない
出力に添字演算子を用いることで正常な数値をだせることができました。
本当にありがとうございます。
しかし、自分はまだプログラミングが全然得意ではなく、みけCATさんが最初に指摘してくれた14行目と17行目の2つの問題点が、どういうことなのか言ってくれていることがなんとなく理解できるんですが、いざそれを修正しようと思うとどの部分をどのように修正するべきなのか全くわかりません。
もしよろしければこの2つの点についての説明をお願いします。
本当にありがとうございます。
しかし、自分はまだプログラミングが全然得意ではなく、みけCATさんが最初に指摘してくれた14行目と17行目の2つの問題点が、どういうことなのか言ってくれていることがなんとなく理解できるんですが、いざそれを修正しようと思うとどの部分をどのように修正するべきなのか全くわかりません。
もしよろしければこの2つの点についての説明をお願いします。
Re: 配列とポインタが分からない
正確には14行目ではなく、12~14行目ですね。
例えばxについては、最初に配列の先頭を指していたポインタは、
10回目のインクリメントをすることで、4行目で確保された配列aの範囲から飛び出します。
飛び出すだけなら良いのですが、17行目でそのポインタを使っているのがまずいのです。
今回の問題は、
1) 配列を初期化する
2) ポインタを使って配列をコピーする
3) コピーされた配列を確認する
という意味でしょうから、17行目の確認でポインタを使う必要はなく、
コピーの最後でポインタが範囲外を指すことになっても構わないでしょう。
> 代入において、最後のループではデクリメントをしないことで未定義動作を回避するとさらにいいでしょう。
と、みけCATさんは書かれていて、もし対応するならば、インクリメント等をifで囲みます。
私ならば気にしないことなので、No.3のプログラムで及第点です。
ただしインデントは気にしますので、私のコードとの違いを考えてみてください。
例えばxについては、最初に配列の先頭を指していたポインタは、
10回目のインクリメントをすることで、4行目で確保された配列aの範囲から飛び出します。
飛び出すだけなら良いのですが、17行目でそのポインタを使っているのがまずいのです。
今回の問題は、
1) 配列を初期化する
2) ポインタを使って配列をコピーする
3) コピーされた配列を確認する
という意味でしょうから、17行目の確認でポインタを使う必要はなく、
コピーの最後でポインタが範囲外を指すことになっても構わないでしょう。
> 代入において、最後のループではデクリメントをしないことで未定義動作を回避するとさらにいいでしょう。
と、みけCATさんは書かれていて、もし対応するならば、インクリメント等をifで囲みます。
私ならば気にしないことなので、No.3のプログラムで及第点です。
ただしインデントは気にしますので、私のコードとの違いを考えてみてください。
Re: 配列とポインタが分からない
繰り返しになりますが、C言語では「配列の最後の要素の1個次」を指すポインタを作ることは明示的に認められています。たいちう さんが書きました:正確には14行目ではなく、12~14行目ですね。
したがって、12行目と13行目のインクリメントは、それだけでは問題になりません。
しかし、デクリメントにより「配列の最初の要素の1個前」に行くのは、許されるという記述がありません。
よって、14行目は引き算の結果がもとのポインタと同じ配列の要素でもその1個後でもなくなるので、未定義動作になります。
(No: 3のコードではデクリメントの位置が変わっていますが、ここの行数はNo: 1のコードのものです)
N1570 6.5.6 Additive operatorsの8より引用
Moreover, if the expression P points to the last
element of an array object, the expression (P)+1 points one past the last element of the
array object, and if the expression Q points one past the last element of an array object,
the expression (Q)-1 points to the last element of the array object. If both the pointer
operand and the result point to elements of the same array object, or one past the last
element of the array object, the evaluation shall not produce an overflow; otherwise, the
behavior is undefined. If the result points one past the last element of the array object, it
shall not be used as the operand of a unary * operator that is evaluated.
「(1個だけ後に)飛び出すだけならいいが、それを使う(デリファレンスする)のがまずい」ということで正しいです。たいちう さんが書きました:例えばxについては、最初に配列の先頭を指していたポインタは、
10回目のインクリメントをすることで、4行目で確保された配列aの範囲から飛び出します。
飛び出すだけなら良いのですが、17行目でそのポインタを使っているのがまずいのです。
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)
Re: 配列とポインタが分からない
みけCATさん、解説ありがとうございます。
ようやく趣旨が理解できたと思います。
No.1のプログラムの12-13行目は問題ないので、
私のNo.4のif分の中もデクリメントのみですね。
ようやく趣旨が理解できたと思います。
No.1のプログラムの12-13行目は問題ないので、
私のNo.4のif分の中もデクリメントのみですね。
Re: 配列とポインタが分からない
こう書けば良いのでは?
#include <stdio.h>
#define N 10
int main(void)
{
int a[N] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }, b[N], c[N];
int *x, *y, *z, i;
x = a;
y = b;
z = c + N; // ポインタは配列の最後の要素の次を指せる
for (i = 0; i < N; i++) {
z--; // z が配列 c の範囲以外の要素を指すことはない
*y = *x;
*z = *x;
x++;
y++;
}
x = a; // 出力の前に再初期化
y = b;
z = c;
for (i = 0; i < N; i++) {
printf("a[%d]=%d b[%d]=%d c[%d]=%d \n", i, *x, i, *y, i, *z);
x++; // 次の要素を指す
y++;
z++;
}
return (0);
}
Re: 配列とポインタが分からない
みけCATさん、たいちうさん、かずまさん解説ありがとうございます。
14行目のデクリメントと17行目がどうしていけなにのかをようやく理解することができました。
14行目のデクリメントと17行目がどうしていけなにのかをようやく理解することができました。
Re: 配列とポインタが分からない
本の紹介になってしまいますが、余計なお節介だったらすみません。
C言語 ポインタ 書籍と検索してみたら、良い本がありました。自分はアマゾンのお気に入りに入れましたが
前橋 和弥さんの「C言語ポインタ完全制覇」か、 柴田 望洋さんの「詳解C言語 ポインタ完全攻略」
がおススメです。
あとは、有名どころではK&Rくらいでしょうか。(自分は持っていないのでモグリですし、にわかですが)
C言語 ポインタ 書籍と検索してみたら、良い本がありました。自分はアマゾンのお気に入りに入れましたが
前橋 和弥さんの「C言語ポインタ完全制覇」か、 柴田 望洋さんの「詳解C言語 ポインタ完全攻略」
がおススメです。
あとは、有名どころではK&Rくらいでしょうか。(自分は持っていないのでモグリですし、にわかですが)