重要なお知らせ

「教えて! goo」は2025年9月17日(水)をもちまして、サービスを終了いたします。詳細はこちら>

電子書籍の厳選無料作品が豊富!

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つ以上の任意数)

という形だと、引数の個数を取得できないためうまくできません。
実現不可能でしょうか?

A 回答 (7件)

実引数の個数を指定せず、かつ終端のNULLも渡さずに、(C++ではなく)C言語で実現ということであれば、次のような方法が考えられます。



char* test(const char*, ...);
#define test(...) test(__VA_ARGS__, NULL);

これで、

test("abc", "def", "ghi");

のように指定するだけで、(見かけ上)終端のNULLを省略してもNULLが勝手に渡されます。
ただし、C99対応のコンパイラでなければ、使えないのが難点です。
    • good
    • 0
この回答へのお礼

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

お礼日時:2005/03/05 17:54

>NULLが入るのかなって思っていたのですが、NULLが入らないようなのです。


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

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

お礼日時:2005/03/05 17:56

サンプルを作ってみました。


一番初めの文字列は、連結後のサイズがはいるだけのサイズが必要です。
#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));
}

この回答への補足

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

補足日時:2005/03/04 20:02
    • good
    • 0
この回答へのお礼

ありがとうございます。

while(wk=va_arg(ap, char *)){

というループをはじめ書いたときにも考えたのですが、どうも引数がもう無いところで、

va_arg(ap, char *)

をすると、NULLが入るのかなって思っていたのですが、NULLが入らないようなのです。
それで得られたポインタ wk を参照するとエラーが起きます。

実際、参考にいただいたソースを実行してみましたが、異常終了します(VC++ 6.0で試しました。 gccではまだ試していません)

お礼日時:2005/03/04 19:55

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;
}
    • good
    • 0
この回答へのお礼

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

お礼日時:2005/03/04 19:48

ポインタを使えばいいのではないでしょうか。


渡したいのが「任意個数のchar *」だとしたら、
char * test(char **str)
もしくは
char * test(char *str[])
として、
関数内で
char *s;
s = str[n];
のようにすればn番目の文字列を取得できます。

呼び出し側が責任を持って、
配列に正しいデータをセットしておかなくてはなりません。
渡すデータを終わりにしたいときは、
NULLポインタ、もしくは予め決めた特定の値を
終わりの印にすればいいでしょう。

でもva_argの使い方をわかってる人に
「ポインタを使え」というのも釈迦に説法という気がします。
これでだめな場合、
どういう要求があるのかをもう少し詳しく説明してください。

この回答への補足

第一引数の・・・

str1の・・・


でした。それとあらかじめ配列にいれる方法も考えましたが、perlの「.」演算子のような感じで使いたいので、
どうしても可変引数にこだわっています^^;

(perlでは
$a = "aaa" . "bbb" . "ccc"
で、連結した文字列が、変数$aに代入される)

補足日時:2005/03/04 18:26
    • good
    • 0
この回答へのお礼

すみません、補足いたします。

自前で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を使わないで関数を実装したいなって思っています。

お礼日時:2005/03/04 18:15

以下のページの、下のほうにあるサンプルを参照。



va_arg, va_end, va_start
http://msdn.microsoft.com/library/en-us/vccore98 …

この回答への補足

読ませていただきました。
最後に目印となるものを置くことで実現するということですね。 思いつきませんでした。 

ただ、やっぱりこれでも無駄な引数が1つ増えちゃいますよね。しかし、引数の数を書くよりはずっと楽なので、もし他に方法がみつからなければこちらで実装してみます。

間違っていたらごめんなさい。

補足日時:2005/03/04 18:19
    • good
    • 0
この回答へのお礼

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

お礼日時:2005/03/04 17:59

以下のページの、下のほうにあるサンプルを参照。



va_arg, va_end, va_start
http://msdn.microsoft.com/library/en-us/vccore98 …
    • good
    • 0

お探しのQ&Aが見つからない時は、教えて!gooで質問しましょう!