3

編集3:コード自体については、最初の回答またはこの投稿の最後を確認してください。

タイトルで述べたように、私はオプションの引数が関数に渡されたかどうかを判断する方法を見つけようとしています。私がやろうとしているのは、ほとんどすべての動的言語が部分文字列関数を処理する方法のようなものです。以下は現在私のものですが、使用されているかどうか/いつ使用されているかを判断する方法がわからないため、機能しません。

char *substring(char *string,unsigned int start, ...){
    va_list args;
    int unsigned i=0;
    long end=-1;
    long long length=strlen(string);
    va_start(args,start);
    end=va_arg(args,int);
    va_end(args);
    if(end==-1){
        end=length;
    }
    char *to_string=malloc(end);
    strncpy(to_string,string+start,end);
    return to_string;
}

基本的には、戻したい文字列の長さを含めずに、文字列の最後まで移動できるようにしたいのです。しかし、私はこれを行う方法を見つけることができないようです。Cで渡された引数の数を知る方法もないので、それは私の最初の考えを奪いました。

編集:これを行うための新しい方法は、現在のコードです。

#define substring(...) P99_CALL_DEFARG(substring, 3, __VA_ARGS__)
#define substring_defarg_2 (0)
char *substring(char *string,unsigned int start, int end){
    int unsigned i=0;
    int num=0;
    long long length=strlen(string);
    if(end==0){
        end=length;
    }
    char *to_string=malloc(length);
    strncpy(to_string,string+start,end);
    return to_string;
}

次に、ファイルでtest.cを呼び出して、機能するかどうかを確認します。

#include "functions.c"
int main(void){
    printf("str:%s",substring("hello world",3,2));
    printf("\nstr2:%s\n",substring("hello world",3));
return 0;
}

features.cには、これまでに必要なすべてのものを含むfunctions.hのインクルードがあります。これがclangの出力です(clangは通常もう少し詳細を提供するように見えるためです。

In file included from ./p99/p99.h:1307:
./p99/p99_generic.h:68:16: warning: '__error__' attribute ignored
__attribute__((__error__("Invalid choice in type generic expression")))
               ^
test.c:4:26: error: called object type 'int' is not a function or function
      pointer
    printf("\nstr2:%s\n",substring("hello world",3));
                         ^~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from test.c:1:
In file included from ./functions.c:34:
In file included from ./functions.h:50:
./string.c:77:24: note: instantiated from:
#define substring(...) P99_CALL_DEFARG(substring, 3, __VA_ARGS__)

GCCは、オブジェクトは関数ではないと言っているだけです

編集2:-1に設定しても変更されないことに注意してください。それでも、同じものがスローされます。私が使用しているコンパイルオプションは次のとおりです。

gcc -std = c99 -c test.c -o test -lm -Wall

Clangは同じものです(それが機能するかどうかは別の質問です。

ここに答える

#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include "p99/p99.h"
#define substring(...) P99_CALL_DEFARG(substring, 3, __VA_ARGS__)
#define substring_defarg_2() (-1)
char *substring(char *string, size_t start, size_t len) {
  size_t length = strlen(string);
  if(len == SIZE_MAX){
    len = length - start;
  }
  char *to_string = malloc(len + 1);
  memcpy(to_string, string+start, len);
  to_string[len] = '\0';
  return to_string;
}

そこからp99が必要になります。選ばれた答えによるものです。ソースディレクトリにドロップするだけで、大丈夫です。また、ライセンスに関する彼の答えを要約します。好きなように使うことはできますが、基本的にはフォークすることはできません。したがって、この目的のために、プロプライエタリまたはオープンソースを問わず、あらゆるプロジェクトでそれと文字列関数を自由に使用できます。

私が尋ねる唯一のことは、少なくともこのスレッドへのリンクを返して、そのスレッドで発生した他の人がスタックオーバーフローについて知ることができるようにすることです。

4

4 に答える 4

6

Cでは、オプションの引数などはありません。このような状況の一般的なイディオムは、2つの機能を持つことです。substr(char *, size_t start, size_t end)およびsubstr_f(char *, size_t start)または、特別な値が与えられた場合に特別な意味を持つ単一の関数を持つことend(この場合、startよりも小さい数、または単に0など)。

varargsを使用する場合は、引数リストの最後に番兵値(NULLなど)を使用するか、前の引数としてargc(引数カウント)を渡す必要があります。

Cの実行時イントロスペクションの量は非常に少なく、これは機能であり、バグではありません。

編集:関連する注記で、Cの文字列の長さとオフセットに使用する正しいタイプはですsize_t。これは、任意の文字列の任意の文字をアドレス指定するのに十分な大きさであり、格納された場合にスペースを無駄にしないように十分に小さいことが保証されている唯一の整数型です。

署名されていないことにも注意してください。

于 2012-04-22T10:51:54.077 に答える
4

オプションの引数を持つ一般的な信念関数以外はCで実装できますが、va_arg関数はそのようなものに適したツールではありません。関数が受け取る引数の数を取得する方法があるため、va_argマクロを介して実装できます。全体を説明して実装するのは少し面倒ですが、P99を使用してすぐに使用できます。

関数のシグネチャを次のようなものに変更する必要があります

char *substring(char *string, unsigned int start, int end);

endそして、それが呼び出し側で省略されている場合のための特別なコードを発明します-1。次に、P99を使用して行うことができます

#include "p99.h"

#define substring(...) P99_CALL_DEFARG(substring, 3, __VA_ARGS__)
#define substring_defarg_2() (-1)

関数を「オーバーロード」するマクロを宣言し(これは可能です。一般的なCライブラリの実装では常にこれを使用します)、関数が受け取る引数の数(この場合は3)に関する知識を置き換えます。 )。デフォルト値を設定する引数ごとに、_defarg_N接尾辞が.でN始まる2番目のタイプのマクロを宣言し0ます。

このようなマクロの宣言はあまりきれいではありませんが、少なくともva_arg関数のインターフェースと同じくらい多くのことが起こっていることを示しています。ゲインは発信者(「ユーザー」)側にあります。そこであなたは今のようなことをすることができます

substring("Hello", 2); 
substring("Holla", 2, 2);

あなたの好みに。

(これらすべてにC99を実装するコンパイラが必要です。)


編集:endその規則を実装したくないが、代わりに2つの異なる関数が必要な場合は、それよりもさらに先に進むことができます。次の2つの関数を実装します。

char *substring2(char *string, unsigned int start);
char *substring3(char *string, unsigned int start, unsigned int end);

次に、マクロを次のように定義します

#define substring(...)                \
 P99_IF_LT(P99_NARG(__VA_ARGS__, 3))  \
 (substring2(__VA_ARGS__))            \
 (substring3(__VA_ARGS__))

これにより、プリプロセッサが受け取る引数の数を調べて、適切な関数呼び出しを選択できるようになります。


Edit2:substring関数のより適切なバージョンは次のとおりです。

  • 長さなどの意味的に正しい型を使用する
  • 3番目のパラメーターは、文字列の終わりではなく長さのようです。それに応じて名前を付けてください
  • strncpy選択する正しい関数になることはほとんどありません。終了'\0'文字を書き込まない場合があります。文字列のサイズがわかっている場合は、を使用しますmemcpy

char *substring(char *string, size_t start, size_t len) {
  size_t length = strlen(string);
  if(len == SIZE_MAX){
    len = length - start;
  }
  char *to_string = malloc(len + 1);
  memcpy(to_string, string+start, len);
  to_string[len] = '\0';
  return to_string;
}
于 2012-04-22T12:16:00.493 に答える
2

残念ながら、次のようにva_argを使用することはできません

また、va_argは、取得された引数が関数に渡された最後の引数であるかどうか(または、そのリストの終わりを超えた要素である場合でも)を決定しないことにも注意してください。関数は、指定されたパラメーターまたは既に読み取られた追加の引数のいずれかの値によって、パラメーターの量が何らかの方法で推測できるように設計する必要があります。

一般的な「回避策」は、他の「オーバーロード」に、などのニーモニック名を付けることですright_substr。派手に見えることはありませんが、確かに高速に実行されます。

実装の複製が懸念される場合はleft_substr、、、substringおよびを、符号付き整数を受け取り、負の数を欠落しているパラメーターとして解釈するright_substr隠し関数のラッパーとして実装できます。パブリックインターフェイスでこの「規則」を使用することはおそらく良い考えではありませんが、プライベート実装ではおそらくうまく機能します。startlength

于 2012-04-22T10:51:53.980 に答える
1

標準Cでは、可変引数のプロトタイプ()を使用する場合、渡される引数の数を直接...知る方法はありません。

舞台裏では、printf()etcのような関数は、フォーマット文字列に基づいて引数の数を想定しています。

たとえば、可変数のポインターを受け取る他の関数は、リストがNULLで終了することを期待します。

これらの手法のいずれかを使用することを検討してください。

于 2012-04-22T10:51:46.057 に答える