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

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

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

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

配列

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

Q&A

2回答

944閲覧

二次元配列の格納についてです

mkm

総合スコア15

C

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

配列

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

0グッド

0クリップ

投稿2020/05/02 15:25

二次元配列のデータ格納についてです。

下記コードで文字列を入力すると
&n[0][0]→一度目入れた文字列
&n[1][0]→二度目入れた文字列
&n[2][0]→三度目入れた文字列



のように格納されていくと考えているのですが
結果を見るとhikaku関数に渡した際、&s1[0]の時に入力した全ての文字が表示されていました。
&s1[1]にすると、先頭文字だけ表示されていません。
これは二次元配列だった文字列が、関数に渡った際に普通の配列に変わったと言うことでしょうか?

#include <stdio.h>

void hikaku(char *s1,int k);
void hikaku(char *s1, int k){

printf("関数に移した後%s\n",&s1[0]);

}
int main(void){

char str[1000]; int i; int k; int j; char n[20][20]={}; fgets(str, sizeof(str), stdin); sscanf(str,"%d",&i); printf(" 入力する回数は %d\n",i); for(k=0;k<i;k++){ fgets(str, sizeof(str), stdin); sscanf(str,"%s",&n[k][0]); printf("直後%s\n",&n[k][0]); } for(j=0;j<i;j++){ hikaku(n[j],i); } return 0;

}

入力値--------------------------------
4
Jimmy
Bob
James
Jimmy

実行結果-------------------------------
入力する回数は 4
直後Jimmy
直後Bob
直後James
直後Jimmy
関数に移した後Jimmy
関数に移した後Bob
関数に移した後James
関数に移した後Jimmy

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

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

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

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

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

guest

回答2

0

「普通の配列」ってなんじゃ? と思いますけれど、それはともかく。

C言語では、多次元配列は下位の配列の配列、ということになっています。配列は、配列の各要素が密に並んだもの。
説明の都合上char n[3][5];だったとすれば、メモリ上はcharの変数が
n[0][0],n[0][1],n[0][2],n[0][3],n[0][4],//個々はchar型
n[1][0],n[1][1],n[1][2],n[1][3],n[1][4],
n[2][0],n[2][1],n[2][2],n[2][3],n[2][4]
と並んでいることになります。

これは、char[5]が3個ならんだ
n[0],//個々はchar[5]型
n[1],
n[2]
でもあります。

もう一つ規則として、Cでは、配列が単独で記述された場合にはそれは配列の先頭要素へのポインタとして解釈される(一部例外あり)、というのがあります。例外の1つは&(アドレス演算子)の対象になったとき。&演算子に対しては、「配列そのもの」として扱われます。
つまり、上記の場合であれば
n は &n[0] (n[0]はchar[5]の配列です。&がついて、その配列のアドレス)
n[M]は &n[M][0] (n[M][0]はchar型のデータです。&がついて、そのデータのアドレス)
と等価と解釈されるわけです。

さて、以上の知識をもとに質問のプログラムを見てみましょう。

sscanf(str,"%s",&n[k][0]);は、sscanf(str,"%s",n[k])と等価です。以下は、n[k]と&n[k][0]は随時置き換えてもOK。
sscanfはstrから文字列を取り出して、n[k]に格納します。strに"bob"が入っていて、kが0だったら
n[0][0]='b', n[0][1]='o', n[0][2]='b', n[0][3]='\0', n[0][4]は変更せず
という処理が行われることになります。
これはkが1ずつ増加するなら

&n[0][0]→一度目入れた文字列

&n[1][0]→二度目入れた文字列

ということで合っていますね。

では、void hikaku(char *s1,int k);'関数ですが。 呼び出し側はhikaku(n[j],i);`としています。n[j]ですから、&n[j][0]と等価。charを指すアドレス(ポインタ)です。
これをchar *s1で受け取る。特に疑問はないですね。s1は、呼び出し元での&n[j][0]を指しています。
であれば、
s1[0]は&n[j][0]+0
s1[1]は&n[j][0]+1 つまり &n[j][1]
です。

これは二次元配列だった文字列が、関数に渡った際に普通の配列に変わったと言うことでしょうか?

「関数に渡った際」ではないです。元の二次元配列から一次元の配列を取り出しているのは、関数を呼び出すときのhikaku(n[j],i);の部分です。前述のように、n[j]が一次元の配列で、これ即ち&n[j][0]という一次元配列の先頭要素へのポインタと解釈されて、関数に渡るのです。

(そもそもがなんだか似たような言葉が並んでややこしいですし、私の説明が上手いとも思っていないので一読してわかるかというとちょっと。頑張って解読してみて下さい)


あと、今回の問題には影響しませんが、
char n[20][20]={};
という記述がエラーにならないとすれば、C言語ではなくC++言語として扱っている可能性があります。
Cだとchar n[20][20]={0}; じゃないとエラーになったりしたような。もし、C++として扱っているとすると、CとC++は違う言語なのでたまにハマるときがあります。C++がCから枝分かれした後、それぞれの都合で変化しているので...
どちらを扱っているのかは確認の必要があるでしょう。

投稿2020/05/02 23:10

thkana

総合スコア7703

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

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

mkm

2020/05/03 02:47

回答ありがとうございます! 関数の&s1が&n[j][0]をさしているため、 &s1[1]が&n[j][1]になってしまうことは理解できました。 ただ、まだ分からないのが、下記の箇所です。 元の二次元配列から一次元の配列を取り出しているのは、関数を呼び出すときのhikaku(n[j],i);の部分です。前述のように、n[j]が一次元の配列で、これ即ち&n[j][0]という一次元配列の先頭要素へのポインタと解釈されて、関数に渡るのです。 hikaku(n[j],i)でjが1の時のn[1]だとすると、 *s1はn[1][0]を指していることは分かりますが、 一次元配列とみなしていると言う所がまだ分かりません。 *s1の指す先が各文字の先頭だから、一次元配列とみなしていると言うことでしょうか?
thkana

2020/05/16 13:41

> *s1の指す先が各文字の先頭だから、一次元配列とみなしていると言うことでしょうか? むしろ、s1が一次元配列だから&s1[0]と解釈されて、*s1は*(&s1[0])になり、*と&が打ち消し合ってs1[0]ということになる、ということでしょう。因果関係が逆です。
guest

0

n[0][0]→'J' n[0][1]→'i' n[0][2]→'m' n[0][3]→'m' n[0][4]→'y' n[0][5]→'\0'

と入ります。(二行目以降も同様)

printf("%s", X) すると、Xが指す文字を先頭に、'\0'が出てくるまでを順にプリントします。
なので printf("%s", &n[0][1]) だと "immy"とプリントされます。

投稿2020/05/02 15:38

episteme

総合スコア16612

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

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

mkm

2020/05/02 16:14

回答ありがとうございます! sscanfのさいに&n[k][0]として変数kを増やしているので、二回目の文字列は   n[1][0]→'B' n[1][1]→'o' n[1][2]→'b' となり、printf("%s",& n[1])でBobが表示されるのではないでしょうか?
episteme

2020/05/02 16:37

printf("%s",& n[1][0]) ならBob printf("%s",& n[1][1]) ならob
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.35%

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

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

質問する

関連した質問