如何判断是否将可选参数传递给函数C.

时间:2012-04-22 10:43:59

标签: c substring variadic-functions p99

编辑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;
}

functions.c有一个包含for 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 个答案:

答案 0 :(得分:6)

在C中,没有可选参数这样的东西。这种情况的常见习语是要么具有两个功能; substr(char *, size_t start, size_t end)substr_f(char *, size_t start)或者只有一个函数end,如果给定一个特殊值,将具有特殊含义(例如在这种情况下,可能是任何小于start的数字,或者只是0)。

使用varargs时,您需要在参数列表的末尾使用sentinel值(例如NULL),或者作为argc(参数计数)的早期参数传入。

C具有非常少量的运行时内省,这是一个功能,而不是错误。

编辑:在相关说明中,用于字符串长度和C中偏移的正确类型为size_t。它是唯一保证足够大以解决任何字符串中任何字符的整数类型,并且保证足够小以至于在存储时不会浪费空间。

另请注意,它是未签名的。

答案 1 :(得分: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后缀N0开始。

这些宏的声明不是很漂亮,但至少告诉va_arg函数的接口会发生什么。增益在呼叫者(“用户”)侧。在那里你现在可以做像

这样的事情
substring("Hello", 2); 
substring("Holla", 2, 2);

根据自己的喜好。

(你需要一个为所有这些实现C99的编译器。)


编辑:如果您不想为end实施该约定,但希望拥有两个不同的功能,您甚至可以更进一步。您将实现这两​​个功能:

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功能更合适的版本:

  • 使用语义上正确的类型长度和类似的东西 该
  • 第三个参数似乎是一个长度,而不是字符串的结尾,相应地命名
  • 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;
}

答案 2 :(得分:2)

不幸的是,you cannot use va_arg like that

  

另请注意,va_arg不会确定检索到的参数是否是传递给函数的最后一个参数(或者即使它是超过该列表末尾的元素)。该函数的设计应使得参数的数量可以通过命名参数的值或已经读取的其他参数以某种方式推断出来。

一个常见的“解决方法”是给另一个“重载”一个很好的助记符名称,例如right_substr。它看起来不那么华丽,但它肯定会跑得更快。

如果您担心重复实施,则可以将left_substrsubstringright_substr实现为隐藏函数的包装器,该函数需要startlength作为有符号整数,并将负数解释为缺失参数。在您的公共接口中使用此“约定”可能不是一个好主意,但它可能在私有实现中正常工作。

答案 3 :(得分:1)

在标准C中,当使用变量参数原型(...)时,无法直接告诉 传递了多少个参数。

在幕后,像printf()等函数假设基于格式字符串的参数数量。

其他函数,例如,可变数量的指针,期望列表以NULL结束。

考虑使用其中一种技术。