
main関数内で
char strarray[3][21]
という文字列配列を宣言し、
サブルーチンvoid input(・・・)内でscanfを使って
strarray[0]~strarray[2]の各行に入力を行うとします。
ここでポインタを引数としてstrarrayをmainとinputで共有する場合、
strarrayに関して以下の項目はどう記述すればよいのでしょうか?
1)inputを宣言する際の仮引数の書式
2)main内でinputを呼び出す際の引数の書式
3)input内でscanfする際の引数の書式
いろいろ組み合わせを試してはみたのですが、
どうにもSegmentationFaultを回避できません。
いい加減混乱してきたので、そろそろすっきりと整理したいと思っています。
よろしくお願いします。
…まあ「構造体使えばいいじゃないか」と言われてしまえばそれまでなのですが…
No.5ベストアンサー
- 回答日時:
まず、配列とポインタの関係から説明します。
C 言語の場合、配列はコンパイラによって、その「先頭要素を指すポインタ」に読み替えられます。
つまり、int hoge[10]; と定義した配列に対して、式の中で hoge と書くと、これは int*型の変数として扱われて、それは &hoge[0] と同じ意味になります。
# 一部例外はあります。
# たとえば、sizeof(hoge) とした場合、ポインタサイズではなくて、配列のサイズが
# 取得できます。
で、これをそのまま配列の配列に適用してみれば良いわけです。
どうなるかというと、char piyo[3][10]; と定義した変数に対して、式の中で piyo と書けば char(*)[10]型の変数として扱われて、それは &piyo[0] と同じ意味になります。
char(*)[10] 型と書きましたが、これは「char型配列(要素数10)を指すポインタ型」のことです。
配列を指すポインタ型の変数を定義する場合には、次のように書きます。
char (*p_piyo)[10];
つまり、piyo はもともと「char型配列(要素数10)の配列(要素数5)」だったわけですよね。
これが、コンパイラによって「char型配列(要素数10)を指すポインタ」として扱われるようになるわけです。
注意点としては、この配列→ポインタの読替え規則は再帰的には行われないことですね。
どういうことかというと、配列の配列は、ポインタのポインタにはならず、配列のポインタにしかならない、ということです。
さて、前置きが長くなりましたが、話を etendard さんのご質問に戻します。
1) これまでの話で、char strarray[3][21] に対してコンパイラが読み替える等価な型が char (*str_ptr)[21] となることはおわかりいただけると思います。
関数の引数に配列を渡す場合は、必ずポインタに読みかえられますので、仮引数の宣言もポインタに読み替えられた方の型で書くことになります。
# というよりも、配列を渡すことができないので、配列の先頭要素を指すポインタを
# 渡している、という言い方のほうが正しいのですけどね。
つまり、プロトタイプは
void input( char (*str_ptr)[21] );
のようになります。
できれば、配列の要素数も一緒に渡せるようにするのが良いでしょう。
その場合は、こんな感じです。
void input( char (*str_ptr)[21], int num );
2) 関数コールする場合は次のようにします。
input( strarray );
要素数付きの場合は、次のようにします。
input( strarray, 3 );
要素数付きにしたほうが良い理由は、ポインタに読み替えられた方の配列の要素数が、関数に渡らないためです。
標準関数では、gets() なんかはその典型ですよね。
# バッファオーバーランの原因となるということで、WARNING を吐くコンパイラも
# あるとか。。。
それに対して fgets() は文字列の数も渡せるので、より安全だといえます。
要するに、それと同じことです。
3) input() の中で scanf() する場合には、次のようにすれば良いです。
for ( i=0; i<3; i++ )
{
scanf( "%s", str_ptr[i] );
}
# なぜこれで良いのかは、よく考えてみてください。
同様に、要素数付きの場合には次のようにします。
for ( i=0; i<num; i++ )
{
scanf( "%s", str_ptr[i] );
}
ちなみに、関数の仮引数の場合に限り、ポインタ宣言と配列宣言は同じ物になります。
# というよりも、いずれもポインタ宣言になります、といった方が正しいです。
つまり、以下の表記はいずれも同じ物になります。
void input( char (*array_ptr)[21] );
void input( char array_ptr[][21] );
void input( char array_ptr[3][21] );
ただし、3番目の表記をした場合であっても、[3] の部分の要素数は無視されます。
# コンパイラが勝手にポインタに読み替えてしまいますからね。
宣言や定義について、このような読替えが起こるのは関数の仮引数宣言の場合だけです。
通常の、宣言や定義ではこのような読替えは起こりませんのでご注意ください。
かなり長くなってしまいましたが、こんな感じでいかがでしょうか。
御回答ありがとうございます。
ソースと照らしながら順を追って見直していくと、
今まで理解の甘かった部分を整理することができました。
発生していた問題も、見事解決です。
丁寧な御解説、どうもありがとうございました。
No.4
- 回答日時:
以下のプログラムはVisualC++6.0でコンパイル/テスト済みです。
面倒なのでscanfによる入力は行なわず、strarray[0],[1],[2]へ直接文字列"12345"を代入し、呼び出し側でそれらを確認表示しています。参考にして下さい。
#include <stdio.h>
#include <string.h>
void input( chary[3][21] )
{
int i;
for(i=0; i<3; i++){
strcpy( y[i], "12345" );
}
}
void main( void )
{
char strarray[3][21];
input( strarray );
printf("%s\n", strarray[0]);
printf("%s\n", strarray[1]);
printf("%s\n", strarray[2]);
}
No.3
- 回答日時:
基本的にNo1のanmochiさんのでいいと思います。
が、そのままだと
>main関数内で
>char strarray[3][21]
>という文字列配列を宣言し、
のstrarrayが、inputのパラメータに乗らない=シンタックスエラーを起こすと
思います。
char** strarrayとchar strarray[3][21]では、
前者は、アドレスの配列(の先頭アドレス)で、アドレスのみからなり
領域の実体を一切持ちません。
後者は、charの配列でメモリ実体を持ちます。
また見かけは2次元ですがcharの配列の配列で結局は1次元配列で、
1次元配列として配列先頭のアドレスを持っています。
したがって、char** strarrayとchar strarray[3][21]の整合性をとってやるため
char strarray[3][21];
char* lines[3];
int index,max;
for(index=0,max=sizeof(lines)/sizeof(char*);index<max;index++) {
lines[index] = strarray[index];
}
input(lines);
な事をやって、char** strarrayがchar strarray[3][21]の各行の領域のアドレスを
持つ配列(ここではlines)をつくってやる必要があると思います。
(いちおう試してみました(笑))
御回答ありがとうございます。
やはり、行単位で文字列を管理するポインタ配列が別に必要になりますか…
どの解説書やサイトをあたっても、二次元配列にポインタを使って
ダイレクトに入力・管理する方法が書かれていなかったので、
うすうすそんな気はしていたのですが。
どうもありがとうございました。

No.2
- 回答日時:
以下のようにして下さい。
--------------------------
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void input(char strarray[3][21])
{
scanf("%s",strarray[0]);
scanf("%s",strarray[1]);
scanf("%s",strarray[2]);
}
main()
{
char strarray[3][21];
input(strarray);
printf("%s\n",strarray[0]);
printf("%s\n",strarray[1]);
printf("%s\n",strarray[2]);
}
御回答ありがとうございます。
inputの引数を見たときは面食らいましたが、動くものなんですね。
配列は引数にできないものだと思い込んでいたので、目から鱗が落ちました。
ありがとうございました。
No.1
- 回答日時:
1)
void input(char **strarray);
2)
input(strarray);
3)
void input(char **strarray) {
sscanf(*strarray, "FORMAT", ...);
sscanf(*(strarray + 1), "FORMAT", ...); ・・・(1)
sscanf(*(strarray + 2), "FORMAT", ...);
}
これでどぎゃんやろ。
(1):「*strarray」の型は「char *」である。よってsscanfの第一引数として利用できる。*strarrayは*(strarray + 0)と同義なので、二個目と三個目は*(strarray + 1)と*(strarray + 2)になる。
記憶を頼りに書いているので間違ってるかも。とりあえずこれでコーディング試してみてくらはい。
早速の御回答ありがとうございます。
…で、結果なのですが、
2)の部分でポインタの変換がおかしいとコンパイラ(bcc32)に言われました。
一応コンパイルは通ったのですが、
結果を出力しようとするとSegmentationFaultが出ているので、
やはり正しく入力できていないようです。
なお、3)の部分は
int i;
for (i=0; i<3; i++) {
scanf("%s", *(strarray+i));
}
で処理しています。
お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!
似たような質問が見つかりました
- C言語・C++・C# 宣言する関数の形が決まっている状態で、 str1とstr2の文字列をこの順に引っ付けてstrに保存し 2 2022/05/30 18:21
- C言語・C++・C# c言語の問題です 3 2023/01/10 16:15
- C言語・C++・C# C言語プログラム変更 2 2022/12/21 15:03
- C言語・C++・C# c言語配列の結合についてです。 なぜうまくいかないのでしょうか。 #include <stdio.h 4 2022/05/30 22:42
- C言語・C++・C# C言語の課題が出たのですが自力でやっても分かりませんでした。 要素数がnであるint型の配列v2の並 3 2022/11/19 17:41
- C言語・C++・C# C言語初心者 ポインタについて、お助けください、、 2 2023/03/15 23:50
- システム CSVファイルのマッピング処理の省力化 1 2022/11/24 00:01
- その他(プログラミング・Web制作) python質問 1 2023/08/14 11:54
- C言語・C++・C# 至急教えてください!プログラミングの問題です。 割られる整数と割る整数を受け取って、商と余りを出力す 3 2022/07/05 10:23
- C言語・C++・C# 至急教えてください。プログラミングの問題です。 malloc関数を使ってください!お願いします! 最 1 2022/07/21 09:28
関連するカテゴリからQ&Aを探す
おすすめ情報
デイリーランキングこのカテゴリの人気デイリーQ&Aランキング
-
C言語 配列の長さの上限
-
C# Listを使わずに2次元配列の...
-
キーボード配列をもとに戻す方法
-
テキストファイルから文字列を...
-
C# 配列の変数宣言について。
-
構造体配列を引数とするDLL作成...
-
2次元配列を戻り値とする関数?
-
複数の選択範囲の行番号を個別...
-
配列で格納したものをmsgboxで...
-
簡易暗号化プログラム(文字入れ...
-
配列を使わずに、変数名を動的...
-
配列をEraseしてもメモリが開放...
-
C言語プログラミングで数あてゲ...
-
パスカルの三角形
-
mallocの確保要素数の限界は?
-
構造体から平均点を求める方法
-
【速いブラインドタッチ】手を...
-
プログラムが書けません。
-
C言語でcharの足し算
-
unsigned char の配列で途中で0...
マンスリーランキングこのカテゴリの人気マンスリーQ&Aランキング
-
C言語 配列の長さの上限
-
配列を使わずに、変数名を動的...
-
C# Listを使わずに2次元配列の...
-
【速いブラインドタッチ】手を...
-
配列をEraseしてもメモリが開放...
-
テキストファイルから文字列を...
-
先頭アドレスとは何ですか?
-
配列で格納したものをmsgboxで...
-
複数の選択範囲の行番号を個別...
-
C# 配列の変数宣言について。
-
C++ vectorに配列をプッシュしたい
-
配列を含む構造体の初期値について
-
VBで構造体の配列を関数に渡す...
-
C言語で特定列だけを抽出して配...
-
キーボードのキー配列について
-
ExcelVBAで質問です。離れた二...
-
2次元配列を戻り値とする関数?
-
unsigned char配列への入力の仕方
-
【C言語】配列の中に配列を入れ...
-
Redimした動的配列はEraseする...
おすすめ情報