C入門書を勉強中の者です。
文字列のポインタ変数の受け渡しの仕方で、もう何日も悩んでおります。
どうか間違いを教えてください。
環境:Win98 VC++6.0 ウィザートで、MFCを使わないWin32 Application
で作成される一般的なHellow Worldアプリケーション内で実験中。
質問1
文字列を返す関数を作成しようとしております。
1.関数に文字列変数のポインタを渡し、
2.関数内でそのポインタで指定されるメモリに文字列を書き込み
3.関数処理後、呼び出し元でそのポインタで示される変数を見て、文字列を取得する
というつもりでC言語の入門書を見ながら下記のコードを書きました。
/////////////////呼び出し側/////////////////////////
case IDM_FUNC:
int ret;
char szBuf;
ret=TestFunc(&szBuf);
MessageBox(hWnd,(LPCSTR)&szBuf,"ポインタ渡し",MB_OK);
break;
////////////////////////////////////////
///////テスト用Function///////
int TestFunc(char *szPath)
{
wsprintf(szPath,"%s","C:\\WINDOWS\\Application Data\\Test");
return 0;
}
実行結果、W:\WIN としか、メッセージボックスに出力されません。
なにをどう間違えているのかわかりません・・・何日も・・・・
どうか何を間違えているのか、どうやればよいのかをご教授お願いしたします。
質問2
自exeが起動されているパスをアプリ内で取得するのはどうやればいいのでしょう?
当サイトのC言語コーナー「コマンドライン引数 」で参考になりそうなものが紹介
されていますが、これをどう関数化したら良いかわからない。
(同一フォルダでINIファイルを使いたい)
入門者のアホな質問とは思いますが、基礎の大事なところと感じております。
以上、宜しくお願いしたします。
とりあえず、質問1の方だけ。
しかも、正解かはわからないけど、関数に渡してwsprintfで文字列が突っ込まれる
ポインタの指す先に文字列分の領域が確保されていません。
あなたが確保している char szBuf; は文字1文字分だけです。あらかじめ、
char szBuf[100];とか確保して、szBufをポインタとして渡す必要があります。
質問2の方は、APIのGetModuleFileName()を使えばよろしいかと
思います。
SSさん、Wandereさんご教授ありがとうございます。
さて、簡単な方からTestしてみました。
質問2.自アプリのパスの取得
GetModuleFileName()で取得できました。
で、SSさんのご指摘もありますので、本命のポインタ引数の受け渡しの実験も
かねて関数作ってみました。
/////呼び出し側///////
case IDM_GETMYPATH:
char szBuf2;
SelfPath(&szBuf2);
MessageBox(hWnd,&szBuf2,"SelfPathポインタ渡し",MB_OK);
break;
//////////////関数//////////////////
void SelfPath(char *szPath)
{
int ret;
ret=GetModuleFileName(NULL,szPath,MAX_SIZE); //MAX_SIZEは255
}
結果、正常にパスを取得する事が出来ました。
これも、呼び出し側でメモリ領域の確保はしてないですが、正常に結果が戻りました。
質問2-1
これはGetModuleFileNameの引数内で、MAX_SIZEを指定しているから、この段階で
自動的に、メモリ領域が確保されたと考えていいのでしょうか?
質問1 ポインタの受け渡し
ですが、
>char szBuf[100];とか確保して、szBufをポインタとして渡す必要があります。
は、納得です。が、具体的にどう書けばいいのでしょうか?
char szBuf[MAX_SIZE];
ret=TestFunc(&szBuf);
では当然ダメでした。「'char (*)[255]' から 'char *' に変換できません。」
ちなみに最初UPしたソースでも、関数内で
wsprintf(szPath,"%s","C:\\WINDOWS\\Application Data\\BabyData");
MessageBox(0,(LPCSTR)szPath,"TestFunc",MB_OK);
とやると、正常にMsgBoxにパスが表示されます。この段階でメモリ領域は確保
出来てはいないのでしょうか???
で、戻るポインタ変数は最初のデータのアドレスになると理解していたのですが。
甘えるなといわれそうですが、呼び出し側で、確保する方法でも、関数内で
確保する方法でも結構ですので、サンプルをお願いできませんでしょうか?
C、C+;ではこのような手法は普通使わないのですか?VBでは、参照渡しでは
ありますがたまに使いますが・・。
もし、いわゆる定石的な方法で無いならその点も合わせてご指摘ください。
(文字列を渡して、処理を加えて戻す関数作成の際に必須と思うので)
宜しくお願い致します。
> ret=TestFunc(&szBuf);
配列の場合は、変数名だけでポインタと同じ扱いですから、
ret=TestFunc(szBuf);
です。
C, C++の場合、確かにポインタ変数はデータの最初のアドレスですが、
その中身まで自動的に確保してくれる訳ではありません。
char szBuf;
はchar型の変数(1バイト)を確保したにすぎません。これに文字列を
書き込むとメモリ上は書き込まれますが、管理下におかれているのは
あくまで確保した1バイトだけであって、それ以降のデータはその後
どうなろうが全く保証されません。同じ関数内では大丈夫だったとい
うのは、たまたま文字列の入ったメモリ状態がそのまま残っていたに
すぎないということです。
ですから書き方としては、すでに出ているように
char szBuf[MAX];
MyFunc(szBuf);
で問題ありません。szBufに格納できる文字列の長さは最後に \0 が入
ることを考慮して、MAX - 1 バイトまでということになります。
GetModuleFileName()で取得する場合も、きちんとバッファを確保して
ください。この関数の第3引数でバッファのサイズを指定するように
なっているのも、第2引数で渡すバッファに確保されたメモリの大き
さを超えないようにするためのものです。
> 質問2-1
たまたま動いただけです。
> は、納得です。が、具体的にどう書けばいいのでしょうか?
「文字列の先頭のアドレスを渡す」ことになります。
つまり,
TestFunc(&szBuf[0]); /* 明示的に書いた場合 */
又は
TestFunc(szBuf); /* 暗黙の変換を利用する場合 */
とします。
Cの基本の話なので,ちゃんと勉強しておくとよいでしょう。
みなさん、こんばんは
皆さんのご教授で、もう一度よ〜く本を見ると、サンプルコードには無かったのですが、
char a[6]
文字列の先頭アドレスを指す表記 a
などという表がありました。(これだけ!)
Wandererさんが書いてくれたように
>配列の場合は、変数名だけでポインタと同じ扱いですから
のように書いていてくれるか、サンプルソースでもあると一発で判るのに。
Wandererさん>
>第2引数で渡すバッファに確保されたメモリの大きさを超えないようにするためのものです
変数についての説明も含め、大変わかりやすい説明本当に助かりました。
ありがとうございました。
YuOさん>
>Cの基本の話なので,ちゃんと勉強しておくとよいでしょう。
はい、基本が判らなければ何も出来ませんから、続けて勉強したいと思います。
(自分用のrtfエディタを作成しながら勉強してます。)
皆さんのご教授で今回の疑問については、理解する事が出来ました。
又、わからない点がありましたら、今後も質問させていただくと思いますので、
その際も宜しくお願いします。
ご教授くださった皆さんどうもありがとうございました。