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

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

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

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

Q&A

解決済

3回答

587閲覧

c言語 連続メモリ割り当てコードでの疑問

akiyama3284pga

総合スコア186

C

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

0グッド

1クリップ

投稿2022/06/18 09:42

編集2022/06/19 19:04

教科書中以下のような例題があり、1点疑問があります。

#include <malloc.h> #include <stdio.h> int main(){ int rows = 2; int columns = 5; int **matrix = (int**) malloc(rows * sizeof(int*)); matrix[0] = (int*) malloc(rows * columns * sizeof(int)); matrix[0][0] = 111; // matrix[1][0] = 222; // ★ERROR => Segmentation fault (core dumped) printf("%d\n", matrix[0][0]); // 111 // printf("%d\n", matrix[0][1]); // 今回の場合、このforは"1"ループしかしない。 for (int i = 1; i < rows; i++){ matrix[i] = matrix[0] + i * columns; } matrix[1][0] = 222; printf("%d\n", matrix[1][0]); // ★SUCCESS => 222 }

一つ目の★の点において、自分はセグフォになる理由がわかりません。
matrix[0]におけるカラム0から4には当然代入できるとして、
matrix[1]の5から9のカラムにも、
matrix[0] = (int*) malloc(rows * columns * sizeof(int));で
全ての必要なメモリを確保(今回なら40byte分一度に)して代入した時点で、
利用可能なアドレスが確保(malloc)されていて使用できるのではないかと考えます。
直前にint matrix = (int) malloc(rows * sizeof(int*));
をしているため、matrix[0]とmatrix[1]のメモリの連続性は100%保障されるのでこのような動作になれないのは少し不便に思います。
これは仕様として受け止めるべきことでしょうか?

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

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

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

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

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

guest

回答3

0

ベストアンサー

これは仕様として受け止めるべきことです。
連続して確保される状態で利用したいなら、
int (* matrix)[5] = (int(*)[5]) malloc(rows * columns * sizeof(int));
にすればいいだけです。

#include <malloc.h> #include <stdio.h> int main(){ int rows = 2; int columns = 5; int (* matrix)[5] = (int(*)[5]) malloc(rows * columns * sizeof(int)); matrix[0][0] = 111; matrix[1][0] = 222; printf("%d\n", matrix[0][0]); printf("%d\n", matrix[1][0]); }

投稿2022/06/18 10:01

PingHermit

総合スコア478

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

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

PingHermit

2022/06/18 10:07

int (* matrix)[columns] = (int(*)[columns]) malloc(rows * columns * sizeof(int)); が出来るコンパイラであればこれでもいいです。
akiyama3284pga

2022/06/18 11:45

ありがとうございます。 とても興味深い代替案のご提示感謝いたします。 ただ、こちらの理解が上手くできません... int (* matrix)[5] は、 int matrix[5]へのポインタかと思います。 この[5]はcolumnですので、 5以上は使用できないと思うのですが、 matrix[0][0] = 111; matrix[1][0] = 222; matrix[2][5] = 255; matrix[3][5] = 255; matrix[4][5] = 255; matrix[5][5] = 255; matrix[6][6] = 255; printf("%d\n", matrix[0][0]); printf("%d\n", matrix[1][0]); printf("%d\n", matrix[2][5]); printf("%d\n", matrix[3][5]); printf("%d\n", matrix[4][5]); printf("%d\n", matrix[5][5]); printf("%d\n", matrix[6][6]); のように、範囲を超えて使用できてしまいます。 このあたりの事情をお聞きできれば助かります。
tmp

2022/06/18 13:54

よこからすいません。5以上使用できているわけではなく、 はみ出たら次に重なりますが、そんな使い方をしているコードを私は、あまり見たことがありません。 matrix[1][0]=123;matrix[0][5]=199; printf("%d\n", matrix[0][5]); printf("%d\n", matrix[1][0]); PingHermitさん、配列のポインタとして確保するという方法が参考になりました。多くはポインタのポインタばかり見かけます。
akiyama3284pga

2022/06/18 14:48 編集

ありがとうございます。 次に重なるという表現は、matrix[1][5]以上([1][6]や[2][0]...)の、(int(*)[5]) malloc(rows * columns * sizeof(int)); により確保された以上の領域を使用することはできてしまうが、それは関係ないアドレスを食いつぶして いる(上書きしている)ということでいいでしょうか? でありましたら、コンパイル時にエラーが出ない以上プログラマの責任で管理するということですかね... 追) はみ出るというものを再現するために、 matrix[0][6] = 222; matrix[1][0] = 333; printf("%d\n", matrix[0][6]); // 333 ★ printf("%d\n", matrix[1][0]); // 333 これでだいぶ理解できました。 しかし、何と言いますか、はみ出たら次に重なるというのが、ないはずの[0][6]が[1][0]にアドレス的に等しくつながっているというその仕組みが今の自分には何とも理解しがたく...
PingHermit

2022/06/19 00:35

配列は連続しているので matrix[0][0] と matrix[0][1] が連続しているのと同じ様に、 matrix[0] [0〜4]と、matrix[1][0〜4]も連続しています。 配列は、構造体のようにパディングも発生しません。 パディングが発生している構造体を配列にしても、配列に空間は発生しません。 以上を踏まえて、 int matrix_arr[2][5]; を代入出来るポインタに代入したら、 int (* matrix)[5] = matrix_arr; となるところを、malloc で代用しているだけです。
PingHermit

2022/06/19 00:45 編集

あ、配列は連続しているので、 matrix[0][0]〜matrix[0][9]まで領域確保済みということです。 matrix[1][-5]〜matrix[1][4] も領域が確保されている範囲です。
PingHermit

2022/06/19 00:56

最近の C# などでは、 ポインタ配列と、2次元配列は書式が違うはずなので、 あまり混乱はしないのかな? matrix[0][0] と、 matrix[0,0] の様にかき分けるようになっていた様な気がします。 どちらがどちらだったか覚えてませんが。
PingHermit

2022/06/19 01:19

>printf("%d\n", matrix[6][6]); >のように、範囲を超えて使用できてしまいます。 >このあたりの事情をお聞きできれば助かります。 範囲を超えているかどうかは、C言語ではチェックしませんから、 アクセス使用できる領域であれば使用できますし、 アクセス使用できないところも、エラーで止まる程度で、アクセスすることは可能です。 領域が確保できていないところをアクセスできないようにするのは、 自分でその様な処理をするような仕組みを作ってください。 C++だと、その様な仕組みを作ってある物もありますので、 それを使用するのも手です。
akiyama3284pga

2022/06/19 03:09

色々とありがとうございます。 上記から、自分の理解に必要な点は、 ・範囲を超えているかどうかは、C言語ではチェックしませんから、アクセス使用できる領域であれば使用できる。 ・matrix[0][0]〜matrix[0][9]、表現を変えるなら、matrix[1][-5]〜matrix[1][4]は確保されている部分であり、 matrix[100][100]などもチェックしないから空きであれば使用できてしまうが、確保外を使用しているということ。 ・2次元配列も一列のメモリ上にあるので、例えば matrix[0][6]と matrix[1][0]は表現方法の違いであり、メモリ上の指す位置は全く同じ ・mallocしたのは、フレッシュな領域を確保するためであり、 int (* matrix)[5]という配列へのポインタに対してmallocすれば、そのポインタに確保したアドレスが入り、 その後[..][..]として利用ができる。 ・範囲外を使用できないようにするには、プログラマによるひと工夫がいる。 まだ完全な理解には至りませんが、とても勉強になります。 BAにさせて頂きます。
guest

0

「 2 次元配列」と「ポインタ配列による "2 次元配列 " 的表記」をごっちゃにされているようです。
2 次元配列のつもりでメモリを確保してポインタ配列の先頭に設定したからといって、ポインタ配列の他要素に 2 次元配列の行のアドレスが勝手に入ることはありません。

投稿2022/06/18 10:52

jimbe

総合スコア12545

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

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

akiyama3284pga

2022/06/19 10:04

毎度ありがとうございます。 皆様のおかげで理解の訂正を行うことができました。 ごちゃ混ぜにならないようにしっかりと線引きして理解しておきたいと存じます。
guest

0

matrixはポインタのポインタである以上、matrix[1]の値はポインタで、matrix[0]にセットした値とは無関係です。mallocで取ったばかりの状況では、matrix[1]にはでたらめなアドレスを指すポインタが格納されています。

matrix[1]の5から9のカラムにも

このような使い方をしたいなら、自力でmatrix[1] = matrix[0] + columns;とセットしてください。

投稿2022/06/18 10:00

maisumakun

総合スコア145121

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

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

maisumakun

2022/06/18 10:01

> matrix[0]とmatrix[1]のメモリの連続性は100%保障されるので されません。matrix[0]とmatrix[1]は、全く別な値を設定できる、別個のポインタです。
akiyama3284pga

2022/06/18 11:27

毎度ありがとうございます。 おっしゃる通り、仕様を無視した理解をしようとしておりました。 でたらめな値に対して代入しようとすればセグフォして当然です...
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問