C言語関係掲示板

過去ログ

No668 内部関数の配列からmain関数への配列の受け渡し

[戻る] [ホームページ]
No.7453

内部関数の配列からmain関数への配列の受け渡し
投稿者---こうじ(2003/06/17 11:17:48)


関数nameから配列classのポインタをmain関数に渡し、main関数でname関数の配列classを使いたいのですが、main関数の/*expect tanaka*/でエラーとなります。
/*expect yamada*/はyamadaと表示されます。
ご教授お願いします。

#include <stdio.h>

char *name();

void main()
{
char *list[3][10];
int i;

list[0][0] = name();

printf("name: %s\n", *list[0]); /*expect yamada*/
printf("name: %s\n", *(list[0]+10)); /*expect tanaka*/
printf("name: %s\n", *(list[0]+20)); /*expect sato*/


}

char *name()
{
char class[]={"yamada","tanaka","sato"};
char *ptr;

return(ptr=class);
}


No.7455

Re:内部関数の配列からmain関数への配列の受け渡し
投稿者---YuO(2003/06/17 12:31:02)


>関数nameから配列classのポインタをmain関数に渡し、main関数でname関数の配列classを使いたいのですが、main関数の/*expect tanaka*/でエラーとなります。
>/*expect yamada*/はyamadaと表示されます。
>ご教授お願いします。

色々問題があるのですが……。

え〜っと,
char *list[3][10];

という定義は何を意味するのかわかっていますか?
list is array[3] of array[10] of pointer to char. … (*)
#「charへのポインタを要素として10個持つ配列」を要素として3個持つ配列。
です。

printf("name: %s\n", *list[0]); /*expect yamada*/
printf("name: %s\n", *(list[0]+10)); /*expect tanaka*/
printf("name: %s\n", *(list[0]+20)); /*expect sato*/

で,まず,list[0]というのは,(*)でarray[3]となっているところを解決します。
次に,+10や+20及び*はarray[10]となっている部分を解決します。
つまり,それぞれ,
list[0][0], list[0][10], list[0][20]を参照していることになり,
list[0][0], list[1][0], list[2][0]と同じことになります。

で,戻り値が代入されているのはlist[0][0]です。
list[1][0]などには,不定値が入っています。
エラーが起きたのはそれが原因でしょう。

でもって,list[0][0]自体も問題があります。
というより,name関数ですが。


char *name()
{
char class[]={"yamada","tanaka","sato"};
char *ptr;

return(ptr=class);
}


まず,classの定義がおかしいです。
char *class[]={"yamada", "tanaka", "sato"};

にしないといけません。
#ポインタの配列なのですから。

次に,自動変数へのポインタを関数から返してはいけません。
関数が終了した時点で自動変数は死んだオブジェクトになります。
classは自動変数ですから,そのポインタを利用した場合,動作は未定義となります。

一般にはstaticにしたりmalloc使ったりすることで,
関数のスコープよりも長い寿命を持つオブジェクトを用意して,
そのオブジェクトへのポインタを返します。


No.7568

Re:内部関数の配列からmain関数への配列の受け渡し
投稿者---こうじ(2003/06/19 14:51:32)


>>関数nameから配列classのポインタをmain関数に渡し、main関数でname関数の配列classを使いたいのです.
実行すると下記のようにfaultが出ます。どうしてでしょうか?
**実行結果**
> list_test
class[0]: yamada
class[0]:
Segmentation fault

**ソース**
#include <stdio.h>

char *name();

void main()
{
char *list[3][10], *str;
int i;

list[0][0] = name();
str=name();

printf("name: %s\n", *list[0]); /*expect yamada*/
printf("name: %s\n", *list[1]); /*expect tanaka*/
printf("name: %s\n", *list[2]); /*expect sato*/


}

char *name()
{
char *class[3][10]={"yamada","tanaka","sato"};
char *ptr;

printf("class[0]: %s\n", *class[0]);

ptr=(char *)malloc(sizeof(class));
class[0][0]=ptr;

printf("class[0]: %s\n", *class[0]); 
printf("class[1]: %s\n", *class[1]);
printf("class[2]: %s\n", *class[2]);
printf("class: %s\n", *class);

printf("size of class: %d\n", sizeof(class));

printf("ptr: %d\n",ptr);
return(ptr);
}



No.7569

Re:内部関数の配列からmain関数への配列の受け渡し
投稿者---none(2003/06/19 15:22:52)


まずは「メモリ」から勉強し直して下さい。
その後、「配列」と「ポインタ」について勉強し直して下さい。

【参考 HP 】
  http://www.pro.or.jp/~fuji/mybooks/cpro/index.html

No.7571

Re:内部関数の配列からmain関数への配列の受け渡し
投稿者---こん!(2003/06/19 16:53:25)


>【参考 HP 】
>  http://www.pro.or.jp/~fuji/mybooks/cpro/index.html

何もよそを紹介しなくてもここにあるのに・・・

配列
2次元配列
配列とポインタ

な〜んてね。別に悪意はありません。(^^ゞ m(__)m

No.7573

Re:内部関数の配列からmain関数への配列の受け渡し
投稿者---none(2003/06/19 17:24:10)


>>【参考 HP 】
>>  http://www.pro.or.jp/~fuji/mybooks/cpro/index.html
>
>何もよそを紹介しなくてもここにあるのに・・・

メモリの概念を理解出来そうな所を選びました。
メモリの概念の理解なしに、ポインタなどの理解は出来ないでしょうからね。
個人的には「プログラムが CUP でどのように実行されるか」と言う所から勉強して頂きたいものですがね。

No.7586

Re:内部関数の配列からmain関数への配列の受け渡し
投稿者---こん!(2003/06/19 18:36:00)


>個人的には「プログラムが CUP でどのように実行されるか」と言う所から勉強して頂きたいものですがね。

それはDOSの時代にsymdeb等でデバッグしながらメモリダンプを見た経験が無け
れば無理ですよ。いくらなんでもなかなか机上の勉強だけで理解出来るものでは
ないと思います。

あなた方のような人達がそれを理解しているのはデバッグの実践上でアセンブラ
のコードを一ステップずつ追ってきた経験を積み重ねてきたからでしょう?

今の時代のデバッグで混合モードのウィンドウが開いてアセンブラが出てきた時
点でその左側にずらっと並んでいる16進がコードが置かれているアドレスだな
んて理解している新人プログラマが何人いるの?

いくら言葉で『メモリ』って言ったって実感として分かりませんって。それにこ
のような掲示板にこのような質問の数々を持ってくる人達の周りに昔のような後
ろに立って頭をぶん殴りながらコンピュータの概念をたたき込んでくれるような
先輩がいると思います?そんな環境にでもいなければ独学でそこまで理解するの
は無理な話ですって。よほど自分自身が好奇心の塊でもなければ。

昔あった映画の『トロン』の様に自分がコンピュータの中に入れるのであればと
もかく・・・

気持ちは分かりますけどね。

No.7594

Re:内部関数の配列からmain関数への配列の受け渡し
投稿者---YuO(2003/06/19 19:54:59)


>メモリの概念を理解出来そうな所を選びました。
>メモリの概念の理解なしに、ポインタなどの理解は出来ないでしょうからね。

なぜですか?

ポインタとメモリに直接の関係はないです。
メモリの概念がないと理解できないというのは,説明が下手なだけです。

ポインタは本質的にreferenceとiteratorの両方の機能を持ったものです。
実装として,アドレスにCPUが利用しているアドレスをそのまま使っているだけで。

はっきり言って,よくある++pでpの値がsizeof(*p)だけ増える,なんていう説明はナンセンスです。
本質的にここでの++はiteratorとしての利用ですから,
pが指している要素が含まれる配列内での,現在のpの次の要素を指すようにpを変更する,
というのが++pの意味です。


>個人的には「プログラムが CUP でどのように実行されるか」と言う所から勉強して頂きたいものですがね。

それがわかってどうするのですか?
インタプリタが間に挟まっている処理系を使う場合,
CPUの動作を知っても意味がないことです。


No.7614

Re:内部関数の配列からmain関数への配列の受け渡し
投稿者---none(2003/06/20 10:59:12)


>ポインタとメモリに直接の関係はないです。

大いに関係ありだろ。(w
プログラムもデータもメモリに格納されている。
そんな基本的なコンピュータの知識もなく、
正しくポインタを理解できるとは思えないが?

って言うか、煽りか釣り…?

No.7617

Re:内部関数の配列からmain関数への配列の受け渡し
投稿者---YuO(2003/06/20 13:39:57)


>>ポインタとメモリに直接の関係はないです。
>大いに関係ありだろ。(w

実装を抜きにして,関係を記述できますか?


>プログラムもデータもメモリに格納されている。

それは実装に関わる話。
そんなことをしなければいけないと,規格は一言も言っていません。
唯一,reallocでメモリの割り当てに失敗したとき,という表現があるので,
malloc/calloc/realloc/freeで扱うオブジェクトはメモリに割り当てる必要がある程度です。
そこ以外に,規格本文中でmemoryという文字列はみつからなかったです。
#Sub-Sectionの名前がMemory management functionっていうくらいですし……。

ただこの部分(C99=ISO/IEC 9899:1999/7.20.3.4の2)
If memory for the new object cannot be allocated, the old object is not deallocated and its value is unchanged.
は,malloc/callocにあわせて
If space for the new object cannot be allocated, the old object is not deallocated and its value is unchanged.
と書いてあると考えることもできます。
malloc/callocではmemoryを割り付けるなんてことは一言も書いていないので。

元にする版が違うとはいえ,JISでは(JIS X3010:1993/7.10.3.4)
領域の割付けができなかったとき,ptrが指すオブジェクトは変化しない。
と書いてありますし。


>そんな基本的なコンピュータの知識もなく、
>正しくポインタを理解できるとは思えないが?

実装と概念を混ぜてしまうとそうなるでしょうね……。

ISO/IEC 9899:1999 6.2.5 Types
A pointer type describe an object whose value provides a reference to an entity of the referenced type.
JIS X 3010:1993 6.1.2.5 型
ポインタ型は,被参照型の実体を参照するための値をもつオブジェクトを表す。
から,ポインタとは
  • 基本的にreference
  • さらに,配列iteratorの機能がくっついたもの
というのが私の考えるポインタの意味です。

後者は,
  • C++でいう「Array-to-pointer conversion」(C99/6.3.2.1の3)
  • ポインタに対する整数の加減算の定義(C99/6.5.6の8)
から導き出した物です。

間違いがあるなら指摘してください。当然,実装に依存しない話で。


No.7623

ポインタとメモリ
投稿者---物見遊山(2003/06/20 18:50:51)


横からゴメンなさい。

まず、参照しているのはISO-C99のFDISです。

ザッとだけど見てみた箇所は
3.2 alignment
3.14 object
6.2.4 Storage durations of objectsの2項
で、見てみた物見の解釈は
・objectはstorageにある
・pointerはobjectのaddressを格納する
(規格(ドラフト)内にaddressという言葉が出て来ること自体
 この規格がメモリを意識しているような・・・)
となりました。間違ってます?
−間違ってたら以下の部分は夢見のうわ言ということで−

>実装を抜きにして,関係を記述できますか?

という質問に対しては・・・
 ドラフト内には
 ポインタとメモリについての相関は記述されていないが、
 ポインタと、storage上のオブジェクト(のアドレス)についての相関は
 記述されている。
 あとはstorageの解釈しだい
・・・という回答でどうでしょうか?
で、storageってメモリのことなんでしょうかね?
(物見にはそのように受け取れますが・・・)

というかISでこれらの記述が削除されてたら
まったく意味なしのレスになるんだよな。

あ。削除用パスワードは1234です。
気分を害されたら削除してください。

No.7631

Re:ポインタとメモリ
投稿者---YuO(2003/06/20 20:23:38)


> ・objectはstorageにある

ISO/IEC 9899:1999 3.14
object
region of data storage in the execution environment, the contents of which can represent values
のところに残っています。


> ・pointerはobjectのaddressを格納する

これは
>3.2 alignment
>3.14 object
>6.2.4 Storage durations of objectsの2項
において見つからなかったのですが……。

ポインタ型は,前に記述した6.2.5 Typesの20項に
「参照するための値」を保持することが書いてあります。
#addressを保持するとは書いてありません。

6.2.4の2項目にあるポインタとobjectの記述は,
The value of a pointer becomes indeterminate when the object it points to reaches the end of its lifefime.
であり,
「ポインタが指すオブジェクトの記憶域期間を終了すると,ポインタの値は不定だ」
と言っています。


> (規格(ドラフト)内にaddressという言葉が出て来ること自体
> この規格がメモリを意識しているような・・・)

規格内にaddressという言葉の定義がないですからね……。
とりあえず,6.2.4 Storage durations of objectsの脚注25に,
25) The term “constant address” means that two pointers to the object constructed at possibly different times will compare equal. The address may be different during two different executions of the same program.
とあるのがaddressという単語を含む用語の意味の説明としては唯一の物だと思います。

この脚注自体は,
An object exist, has a constant address,25) and retains its last-stored value throughout its lifetime.
に掛かっており,addressとはオブジェクトを識別するための値程度の意味合いではないでしょうか。


実際には,"3. Terms, definitions, and symbols"に出てこないので,ISO/IEC 2382-1を参照するべきなのでしょうけれど,持っていないのでそこまでは確認していないです。


ちなみに,6.5.3.2 Address and inderection operatorsの3項では,初っ端に
The unary & operator returns the address of its operand.
とか書いておきながら,その後ろには場合分けしながら説明が書いてあり,最後には
Otherwise, the result is a pointer to the object or function designated by its operand.
と,最初の一文無くてもなんとかなりそうな感じで続きます。


> で、storageってメモリのことなんでしょうかね?
> (物見にはそのように受け取れますが・・・)

一般的にはメモリでしょうけれど,メモリだけを指しているのではないでしょう。
メモリを指しているのであれば,storageではなくmemoryと書くでしょうし。

例えば,EXAMPLEや脚注の中では大量にmemoryという単語が出てきます。
ところが,それ以外のところではreallocの所と,Sub-Sectionの名前にしか出てきていません。

これは,明らかに
  • 規格を構成する部分ではmemoryという単語を利用しない
  • 規格を構成しない部分ではmemoryという単語の利用を制限していない
ということだと思います。
つまり,storage∋memoryであって,storage=memoryではないです。


No.7634

Re:ポインタとメモリ
投稿者---物見遊山(2003/06/20 21:05:48)


解説していただきありがとうございます。
でも、ただいま帰宅準備中なもんで
まともなレスをつけれません。

以下、がんばったつもりだけどまともじゃないであろうレスです。

>> ・pointerはobjectのaddressを格納する
>
>これは
>>3.2 alignment
>>3.14 object
>>6.2.4 Storage durations of objectsの2項
>において見つからなかったのですが……。

おー。ゴメンなさい。6.2.4の脚注25です。
(物見はHTMLが全然だめなので上付けとか奇怪なことができない)
というかISにもありますか?

>addressとはオブジェクトを識別するための値程度の意味合いではないでしょうか。

もひとつ自己ツッコミしちゃうと
 storageにaddressがある
って記述はないんですよね。あくまでもobjectにaddressがあるという。

>これは,明らかに
>
> 規格を構成する部分ではmemoryという単語を利用しない
> 規格を構成しない部分ではmemoryという単語の利用を制限していない>>
>ということだと思います。
>つまり,storage∋memoryであって,storage=memoryではないです。

激しく納得しました。
まぁ、物見が納得したところで規格がどーなるというわけではないのですが。

ということで最初の質問はかなり煽ってますね(汗)。
ゴメンなさい。


No.7592

Re:内部関数の配列からmain関数への配列の受け渡し
投稿者---YuO(2003/06/19 19:34:30)


>>>関数nameから配列classのポインタをmain関数に渡し、main関数でname関数の配列classを使いたいのです.
>実行すると下記のようにfaultが出ます。どうしてでしょうか?

根本的にポインタがわかっていないまま作っているからです。


char *name();
char *list[3][10], *str;

再び言いますが,これらがまず間違いです。
これでは,nameが返すのは文字列になりますし,
listはcharへのポインタの配列の配列になってしまいます。


char *class[3][10]={"yamada","tanaka","sato"};

これは何がやりたいのですか?これが,
char * class[3][10] = {
  {
    "yamada",
    "tanaka",
    "sato",
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL
  }, {
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL
  }, {
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL,
    NULL
  }
};
に等しいことは理解していますか?


ポインタまわりがわかりにくいなら,typedefを多用していくのがいいかもしれません。
今回の場合,classは,
・最終的な要素は文字列→const char * (or char *)
・格納には配列を使う
ですから,
typedef char * pchar;
pchar class[3] = { "yamada", "tanaka", "sato"};
のように書けば,若干はわかりやすいと思います。
まぁ,この方法の弱点として,
・cv修飾時に混乱する
というものもありますが……。
#const pcharはconst char *ではなく,char * constだとか……。