• ベストアンサー

可変引数について。

va_argを使う方法は知っているのですが、受け渡す引数の数を指定しない方法で、決まった型引数を任意数渡す方法はC言語(C++ではない)で実現できますか? char* test( n, str1, str2, str3, …任意数) nはint, str○ は const char* という形ならば、第二引数以降の引数を呼び出し元で n に与えてやれば、n回だけ va_arg(args, char*) を呼び出せばよいですが、 char* test2(str1, str2, str3, str4, …1つ以上の任意数) という形だと、引数の個数を取得できないためうまくできません。 実現不可能でしょうか?

質問者が選んだベストアンサー

  • ベストアンサー
  • jacta
  • ベストアンサー率26% (845/3158)
回答No.7

実引数の個数を指定せず、かつ終端のNULLも渡さずに、(C++ではなく)C言語で実現ということであれば、次のような方法が考えられます。 char* test(const char*, ...); #define test(...) test(__VA_ARGS__, NULL); これで、 test("abc", "def", "ghi"); のように指定するだけで、(見かけ上)終端のNULLを省略してもNULLが勝手に渡されます。 ただし、C99対応のコンパイラでなければ、使えないのが難点です。

ytse
質問者

お礼

ありがとうございます。 マクロにしてしまえばいいのですね。 大変参考になりました。

すると、全ての回答が全文表示されます。

その他の回答 (6)

  • BLUEPIXY
  • ベストアンサー率50% (3003/5914)
回答No.6

>NULLが入るのかなって思っていたのですが、NULLが入らないようなのです。 そうですね。NULLになることを期待しているプログラムです。 bcc32(ボーランドCコンパイラ)では、動作したのですが、他のコンパイラでは、動作しないのですね。 va_argで、変数の要素がなくなったことを感知できないとすると、最後の変数にNULLを渡してやるなどの必要があると思います。(元の形ではできない?)

ytse
質問者

お礼

gccでも通りました。 NULLを渡すかどうかのしっかりした定義が気になるのでgoogleで調べてみます。

すると、全ての回答が全文表示されます。
  • BLUEPIXY
  • ベストアンサー率50% (3003/5914)
回答No.5

サンプルを作ってみました。 一番初めの文字列は、連結後のサイズがはいるだけのサイズが必要です。 #include <stdio.h> #include <stdarg.h> #include <string.h> char *strscat(char *s, ...){ char *wk,*ret=s; va_list ap; va_start(ap, s); while(wk=va_arg(ap, char *)){ s += strlen(s); strcpy(s, wk); } va_end(ap); return ret; } void main(void){ char buff[80]="STR1"; char *str2="str2"; char *str3="String3"; printf("%s\n",strscat(buff,str2,str3)); }

ytse
質問者

お礼

ありがとうございます。 while(wk=va_arg(ap, char *)){ というループをはじめ書いたときにも考えたのですが、どうも引数がもう無いところで、 va_arg(ap, char *) をすると、NULLが入るのかなって思っていたのですが、NULLが入らないようなのです。 それで得られたポインタ wk を参照するとエラーが起きます。 実際、参考にいただいたソースを実行してみましたが、異常終了します(VC++ 6.0で試しました。 gccではまだ試していません)

ytse
質問者

補足

エラーの補足です。 ループ内のstrcpy部分をコメントアウトした結果、 私の環境ではループを2回で終わらなければいけないところを、24回くりかえしていました。

すると、全ての回答が全文表示されます。
  • Tacosan
  • ベストアンサー率23% (3656/15482)
回答No.4

exec みたいに最後の引数を (const char *)0 にするという約束にすれば, こんな感じかなぁ? #include <stdarg.h> #include <string.h> char *cloneCat(const char *str1, ...) { char *p; const char *str; size_t len; va_list ap; va_start(ap, str1); len = strlen(str1)+1; p = malloc(len); strcpy(p, str1); while ((str = va_arg(const char *)) != 0) { char *q; len += strlen(str); q = malloc(len); sprintf(q, "%s%s", p, str); free(p); p = q; } va_end(ap); return p; }

ytse
質問者

お礼

ありがとうございます。 やはり最後の引数を目印にする方法がよさそうですね。 ソース参考にいたします。

すると、全ての回答が全文表示されます。
  • liar_adan
  • ベストアンサー率48% (730/1515)
回答No.3

ポインタを使えばいいのではないでしょうか。 渡したいのが「任意個数のchar *」だとしたら、 char * test(char **str) もしくは char * test(char *str[]) として、 関数内で char *s; s = str[n]; のようにすればn番目の文字列を取得できます。 呼び出し側が責任を持って、 配列に正しいデータをセットしておかなくてはなりません。 渡すデータを終わりにしたいときは、 NULLポインタ、もしくは予め決めた特定の値を 終わりの印にすればいいでしょう。 でもva_argの使い方をわかってる人に 「ポインタを使え」というのも釈迦に説法という気がします。 これでだめな場合、 どういう要求があるのかをもう少し詳しく説明してください。

ytse
質問者

お礼

すみません、補足いたします。 自前でstrcatに似た文字連結関数を作っています。引数に与えられた文字列を全て連結し、その文字列へのポインタを返すというものです。 とりあえず動作している、「引数の数を渡して処理する方の関数」をのせておきます char *cloneCat(int n, const char* str1, ...){   const char* *ps;   char *pstr;   ps = &str1; // 第一引数のポインタ取得   //str1の文字列文のメモリ領域取得   if((pstr = (char *)malloc(strlen(str1) + 1)) == NULL)    return NULL;   strcpy(pstr, str1); //第一引数をコピー   for(i=1;i<n;i++){    //メモリサイズ変更。 ++ps 部分で次の引数    //へポインタを進めて、参照し、そのサイズ    //をstrlenで取得    if((pstr = (char *)realloc(pstr, strlen(pstr) + strlen(*++ps) + 1)) == NULL) return NULL;    //文字連結    strcat(pstr, *ps);   }   //parrayはmallocで得たアドレスを格納している   //大域変数。pcntはその数を入れている大域変数   return parray[pcnt++] = pstr; } 例えば、pstr = cloneCat(3, "aa", "bb", "cc") とすればpstrには "aabbcc"が得られます。この引数の数3を使わないで関数を実装したいなって思っています。

ytse
質問者

補足

第一引数の・・・ ↓ str1の・・・ でした。それとあらかじめ配列にいれる方法も考えましたが、perlの「.」演算子のような感じで使いたいので、 どうしても可変引数にこだわっています^^; (perlでは $a = "aaa" . "bbb" . "ccc" で、連結した文字列が、変数$aに代入される)

すると、全ての回答が全文表示されます。
  • phoenix343
  • ベストアンサー率15% (296/1946)
回答No.2

以下のページの、下のほうにあるサンプルを参照。 va_arg, va_end, va_start http://msdn.microsoft.com/library/en-us/vccore98/HTML/_crt_va_arg.2c_.va_end.2c_.va_start.asp

ytse
質問者

お礼

ありがとうございます。 英語はちょっと苦手なのですが、これからがんばってサンプルを解読してみます。

ytse
質問者

補足

読ませていただきました。 最後に目印となるものを置くことで実現するということですね。 思いつきませんでした。  ただ、やっぱりこれでも無駄な引数が1つ増えちゃいますよね。しかし、引数の数を書くよりはずっと楽なので、もし他に方法がみつからなければこちらで実装してみます。 間違っていたらごめんなさい。

すると、全ての回答が全文表示されます。
  • phoenix343
  • ベストアンサー率15% (296/1946)
回答No.1

以下のページの、下のほうにあるサンプルを参照。 va_arg, va_end, va_start http://msdn.microsoft.com/library/en-us/vccore98/HTML/_crt_va_arg.2c_.va_end.2c_.va_start.asp

すると、全ての回答が全文表示されます。