C言語初心者です。よろしくお願いします。

戻り値が文字列のポインタである関数の場合は、char *strcat2(char *s, ...); 
のように *関数名 としてプロトタイプ宣言をするという理解で良いのでしょうか?
この場合の「*関数名」の「*」はどのように解釈すればよいでしょうか?
 
また、getsの場合は、char *gets{ char *buffer};ですが、
この場合も文字列のポインタが戻り値であると考えて良いのでしょうか?

// strcat2.c

#include <stdio.h>
#include <stdarg.h>
#include <string.h>

char *strcat2(char *s, ...);
//可変個の引数(char型のポインタ)を持つ関数で、文字列を戻り値としている

int main()
{
char str1[256] = "今日は、";
char *str2 = "良い天気ですが、";
char *str3 = "明日はどうなるか分かりません¥n";
strcat2(str1, str2, str3, "");
printf(str1);

return 0;
}

char *strcat2(char *s, ...)
{
va_list ptr;
char *st;

if(s[0] == '¥0')
{
return s;
}

va_start(ptr, s);
while (1)
{
st = va_arg(ptr, char *);
if(st == '¥0')
{
break;
}
strcat(s, st);
}
va_end(ptr);

return s;
}

回答の条件
  • 1人5回まで
  • 登録:
  • 終了:2009/03/14 21:34:09
※ 有料アンケート・ポイント付き質問機能は2023年2月28日に終了しました。

回答3件)

id:hijk05 No.1

回答回数1307ベストアンサー獲得回数23

仕様どおりです。

http://ja.wikipedia.org/wiki/Gets

id:dev_zer0 No.2

回答回数332ベストアンサー獲得回数25

ポイント40pt

> *関数名 としてプロトタイプ宣言をするという理解で良いのでしょうか?

はい、演算子の優先順位でchar *func();は(char *)func()と同じ意味になります

なお、Cではchar (*func)()という構文も可能であり、

これは「char型を返す関数へのポインタ」という別の意味になってしまいます。

また、char func()という構文も可能であり、

これは「char型を返す関数」です

今回の作り的には「char型へのポインタを返す関数」であるのは妥当でしょう。


> また、getsの場合は、char *gets{ char *buffer};ですが、

多分、char *gets(char *buffer);だと思いますが、

C標準関数のgetsはC89,C99共に「非推奨」です。

# バッファオーバフローをどうすることも出来ない

「絶対に」char char *fgets(char *s, int size, FILE *stream);

を使うようにしてください。

# ま、どちらの関数もchar型へのポインタを返します。


あと、気になった点として

> if(s[0] == '¥0')

これの使い方は良いですが、

> if(st == '¥0')

これの使い方は明らかに間違っています

変数stはchar型へのポインタとして宣言されているはずなのに

比較対照がchar型というのは変です

# ま、0もNULLも'\0'も多くの実装では区別してないですけど


あと、可変個の引数を取る文字列を連結する関数はC-FAQ 15-4にあります

http://www.kouno.jp/home/c_faq/c15.html#4

# 内部でmallocを呼んでいるので呼び出し側で解放する

# 必要があるのが難点ですが...

今回のchar *strcat2(char *s, ...);関数はsの領域の領域あふれを

strcat2側ではどうにも出来ないのは将来禍根を残すと思います


まぁ、strcat()自体も呼び出し側が注意深く使わないといけないので

設計思想上は整合性が取れているような気がしますが、

gets(), fgets()はNULLポインタを返す可能性があるのに対し、

strcat2()は常に第一引数のポインタを返すのはstrcat()の仕様ですね


長くなりましたが、strcat2()をあなたがどのような設計にしたいのかによって、

正解であるとも間違っているともいえます。

strcat()の拡張いう設計方針ならばその実装はおおむね合っています。

strcat()はその関数を使う側が第一引数のエリアが溢れないように注意するような「仕様」です。


しかし、gets(), fgets()の拡張という設計方針ならば

状況によってはNULLポインタを返す可能性や、バッファ溢れを

関数側で注意深く制御すべきと思われます


少し、質問が漠然としているので回答も漠然としてるのはご容赦下さい

この回答によってもう少し疑問点が明確になってくれたならば

コメントで「できるだけ」サポートはしたいと思います

# もちろん、私の気まぐれにより回答しないこともありえます

id:gbs01

ありがとうございます。

もっと勉強してみます。

2009/03/14 21:29:41
id:rsc96074 No.3

回答回数4506ベストアンサー獲得回数438

ポイント40pt

●戻り値が文字列のポインタである関数の場合は、char *strcat2(char *s, ...); のように

*関数名 としてプロトタイプ宣言をするという理解で良いのでしょうか?

 それで良いと思います。

●この場合の「*関数名」の「*」はどのように解釈すればよいでしょうか?

 関数の戻り値がアドレスなんだなぁと解釈すればいいです。

●また、getsの場合は、char *gets{ char *buffer};ですが、 →char *gets( char *buffer);

この場合も文字列のポインタが戻り値であると考えて良いのでしょうか?

 成功した場合、それで良いようです。

http://www.bohyoh.com/CandCPP/C/Library/gets.html

 それから、

>//可変個の引数(char型のポインタ)を持つ関数で、文字列を戻り値としている

というコメントは、少しおかしい表現かも。関数は、1つの値しか返せないので、文字列は返せません。文字列の先頭アドレスを返しているだけです。

 関数で文字列を返す場合は、次の2つの方法があります。②の使い方のほうが普通なんじゃないかなぁと思います。

①return文で、文字列の先頭アドレスを返してやる

②共有データを変更する

 return文では、複数の戻り値を返すことが出来ないので、引数をポインタにして、呼び出し側と関数側でデータを共有して、見掛け上複数のデータを返したようにしています。

 要するに、Cでは、ポインタで、call by reference を間接的に、実現しています。

http://d.hatena.ne.jp/Isoparametric/20080714/1215992126

id:gbs01

ありがとうございます。

もっと勉強してみます。

2009/03/14 21:32:09
  • id:Vacuum
    // strcat2.c

    #include <stdio.h>
    #include <stdarg.h>
    #include <string.h>

    char *StrCat(char *s, ...);
    //可変個の引数(char型のポインタ)を持つ関数で、文字列を戻り値としている

    int main() {

      char str0[256];
      char str1[256] = "今日は、";
      char *str2   = "良い天気ですが、";
      char *str3   = "明日はどうなるか分かりません\n";

      strcpy(str0,StrCat(str1, str2, str3));
      printf(str0);

      return 0;
    }
    char *StrCat(char *str, ...) {
     va_list va_ptr;
     char  *ptr;

      va_start(va_ptr,str);
      while ((ptr=va_arg(va_ptr,char *))!=0x00) {
        strcat(str,ptr);
      }
      va_end(va_ptr);
      return (str);
    }

  • id:gbs01
    Vacuumさんへ

    なるほど。上記のようなコードのほうがより良いですね。
    ありがとうございます。
    勉強になりました。

この質問への反応(ブックマークコメント)

「あの人に答えてほしい」「この質問はあの人が答えられそう」というときに、回答リクエストを送ってみてましょう。

これ以上回答リクエストを送信することはできません。制限について

回答リクエストを送信したユーザーはいません