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

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

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

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

Q&A

解決済

4回答

188閲覧

C言語の関数とポインターの勉強中

退会済みユーザー

退会済みユーザー

総合スコア0

C

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

0グッド

0クリップ

投稿2018/03/14 06:15

関数とポインタを勉強してます、わからないところが有るので教えてください。

関数内で作られた配列を受け取る時はどのようにすればいいのでしょうか?

数字の配列は個々に値を受け取れず
文字列の配列は個々でしか受け取れず
仕組みがよく解りません。
return p;をreturn p[1];とすると
return makes pointer from integer without a cast
と出ます。そもそもp[1]はpointerだと思ってたのですが違うのですか?

どのようにして関数内で作られた配列を受け取るのか教えて欲しいです。
よろしくお願いします。

C

1int *getNum() { 2 int a[] = {111, 222, 333}; 3 int *p = a; 4 return p; 5} 6char *getStr() { 7 char *str[] = {"One", "Two", "Three"}; 8 return str[1]; 9} 10 11int main(void) { 12 printf("%d\n", getNum()[1]); 13 printf("%s\n", getStr()); 14 return 0; 15}

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

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

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

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

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

guest

回答4

0

ベストアンサー

こんにちは。

C言語の一般的な方法は下記の3つです。

  1. パラメータで返却先の配列を指定し、そこへ関数側で値を設定する
  2. mallocで獲得して返却する
  3. 配列をグローバル変数とする

1.のサンプルは下記の通りです。

C

1#include <stdio.h> 2 3void getNum(int *a) 4{ 5 a[0]=111; 6 a[1]=222; 7 a[2]=333; 8} 9void getStr(char * str[]) 10{ 11 str[0] = "One"; 12 str[1] = "Two"; 13 str[2] = "Three"; 14} 15 16int main(void) 17{ 18 int a[3]; 19 getNum(a); 20 printf("%d\n", a[1]); 21 22 char* str[3]; 23 getStr(str); 24 printf("%s\n", str[1]); 25 return 0; 26}

wandbox


あまりC言語では一般的ではありませんし性能上の懸念がありますが、一度構造体として定義して構造体をreturnすることも可能です。

C

1#include <stdio.h> 2 3struct IntArray3 4{ 5 int integer[3]; 6}; 7 8 9struct CharPtrArray3 10{ 11 char* str[3]; 12}; 13 14struct IntArray3 getNum() 15{ 16 struct IntArray3 a = {{111, 222, 333}}; 17 return a; 18} 19 20struct CharPtrArray3 getStr() 21{ 22 struct CharPtrArray3 str = {{"One", "Two", "Three"}}; 23 return str; 24} 25 26int main(void) 27{ 28 printf("%d\n", getNum().integer[1]); 29 printf("%s\n", getStr().str[1]); 30 return 0; 31}

wandbox

構文上はコピーが発生しますの性能的には懸念されます。
C++の場合は最適化(NRVO)により、コピーが省略される処理系が多いのですが、C言語でも同様かどうかまでは分かりませんでした。

投稿2018/03/14 06:35

Chironian

総合スコア23272

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

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

yumetodo

2018/03/14 09:23

このコードは解説用なので省略されていますが、実際には必ず書込み可能な要素数を同時に引数に渡すようにしましょう。さもないとgets関数がやらかしたようにバッファーオーバーランの危険があります。
Chironian

2018/03/14 09:50

yumetodoさん。フォローありがとうです。 Ya-madaさん。実用プログラムでは前者のサンプルの場合はエラーチェックをやっておかないと信頼性を担保し辛いです。 チェックすべきはNULLと要素数ですね。違反は恐らくバグでしょう。バグの場合に必要な処理はデバッグですから、デバッグに有用な情報(ファイル名と行番号など)をstderrへ出力してabort();するのも手です。
退会済みユーザー

退会済みユーザー

2018/03/14 10:04 編集

C言語どころかプログラミング自体が初めてなので関数から配列を受け取ることが一般的ではないという事に気づきもしませんでした。 丁寧な解説を頂きありがとうございました。 構造体を試してみます。 エラーチェックとかデバックとかまだ全然解らない段階です。 その辺も合わせで勉強します。
Chironian

2018/03/14 11:13

> エラーチェックとかデバックとかまだ全然解らない段階です。 まだ、理解するのは厳しいかも知れませんが、雰囲気をつかめるようサンプルをおいておきます。 https://wandbox.org/permlink/l3V2JO8htH9pYBtV このASSERTマクロでエラーチェックし、エラーがあったらデバッグ用の情報を出力してプログラムを強制終了しています。 main()関数の下の方のコメントアウトしている行のような呼び出しをすると異常動作するので、それをチェックしています。 このエラーチェックがないと、運がよい時はプログラムが異常終了します。運が悪いと想定外のメモリが書き換えられ一見正常に動作しつつ、こっそりおかしな結果を返します。このこっそりおかしな結果になることを回避するためのエラーチェックです。 プログラムが小さいうちはどうにでもなるのですが、プログラムが大きく(数千行数万行に)なってくるとこのような不具合を潰すのが本当に難しくなるので、このようなチェックを仕込むことは重要なのです。
退会済みユーザー

退会済みユーザー

2018/03/17 08:38

Chironianさん、お礼遅れました。 サンプルまで作って頂きありがとうございました。
guest

0

関数の中で作られた配列を返す、というのは無理あるので、

配列のアドレスとサイズを(呼び出し側から)引数でもらって、そこにデータを格納して返す、ということがよく行われていますねー

#んで、返り値は、実際にデータを格納した数、とか。

投稿2018/03/14 06:27

編集2018/03/14 06:29
y_waiwai

総合スコア87749

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

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

退会済みユーザー

退会済みユーザー

2018/03/14 06:34

現実的。
y_waiwai

2018/03/14 06:36

Win32APIなんかで多用されてる手法ですねー @ヒネた回答担当
退会済みユーザー

退会済みユーザー

2018/03/14 06:51

いーや、yさんのが一番的確だと思いましたよ。 ロボじゃないんだから、聞かれた通りの事を答えるのはやめろよ、と思いますね。
退会済みユーザー

退会済みユーザー

2018/03/14 10:05

配列は無理なんですね。。。 勉強になりました。 ありがとうございました。
guest

0

return p;をreturn p[1];とすると
return makes pointer from integer without a cast
と出ます。そもそもp[1]はpointerだと思ってたのですが違うのですか?

これはgetNum関数のところのことでよいでしょうか。
順を追って考えましょう。

int *p = a;

ここでpにはa配列の先頭アドレスが入ります。
ということは、
p[0] = 111
p[1] = 222
p[2] = 333
というように参照できるわけです。
これでわかるようにreturn p[1]と書くと、return 222と書いているのと同じ意味になります。
つまりgetNum関数はポインタを返してねと定義されているのに、222という値を返しているのでエラーになっています。
return &p[1]と書けばエラーは消えるでしょう。

但し、関数内のローカル変数は、関数を抜けると生存しなくなります。
正確には関数を抜けた直後なら参照できるかもしれませんが、いつ別の値に置き換わってもおかしくはありません。
配列内の値を返却するのでよければ、戻り値をint*からintに変更し、return p[1]としましょう。
配列のアドレスを返却したいのであれば、配列の定義をstatic int a[]として、静的変数にしましょう。

投稿2018/03/14 06:26

ttyp03

総合スコア16998

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

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

退会済みユーザー

退会済みユーザー

2018/03/14 10:07

丁寧な解説ありがとうございました。 勉強になりました。
guest

0

どのようにして関数内で作られた配列を受け取るのか教えて欲しいです。

残念ながら、C言語では一筋縄でいかない方法ばかりとなります。なお、普通に宣言したローカルの配列を返すことはできません(関数を抜けた瞬間に使用不能となるので、何が起きても責任は負わない状態です)。

  • structの中に配列を定義して、それを返す

C言語でもstructはコピーできますので、structの中に作った配列は返り値として使えます。デメリットとしては、「structのメンバ参照が必要になって、そのまま配列として使えない」「配列のサイズは固定になる」ということがあります。

  • staticな配列を返す

staticに宣言された配列であれば、関数を抜けても保持されるので、ポインタが無効になることはありません、ただし、何度呼び出しても同じメモリ領域を参照していますので、書き換えたりするとカオス化します。

  • mallocで確保した領域を返す

自分でメモリを確保するという手段もありますが、正しくfreeしないとメモリリークします。

  • C++のvectorなどを使う

C++の世界であれば、コードで管理された動的配列も利用可能です。

投稿2018/03/14 06:23

maisumakun

総合スコア145183

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

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

退会済みユーザー

退会済みユーザー

2018/03/14 10:11

たいへん有益な勉強になりました あがとうございました。 構造体を利用してみようと思います。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問