代码之家  ›  专栏  ›  技术社区  ›  Rizo

C/C++中的变量函数和变量赋值

  •  5
  • Rizo  · 技术社区  · 16 年前

    我想知道在C/C++语言中是否可以将参数传递给键值形式的函数。 例如,在Python中,可以执行以下操作:

    def some_function(arg0 = "default_value", arg1):
        # (...)
    
    value1 = "passed_value"
    some_function(arg1 = value1)
    

    所以C中的替代代码可以是这样的:

    void some_function(char *arg0 = "default_value", char *arg1)
    {
        ;
    }
    
    int main()
    {
        char *value1 = "passed_value";
        some_function(arg1 = value1);
        return(0);
    }
    

    所以在某些函数中使用的参数是:

    arg0=“默认值”

    arg1=“传递的值”

    有什么想法吗?

    7 回复  |  直到 16 年前
        1
  •  8
  •   Christoph    16 年前

    下面是一个使用复合文字和可变宏的C99解决方案:

    #include <stdio.h>
    
    #define some_func(...) some_func_((struct some_func_args_){ __VA_ARGS__ })
    
    struct some_func_args_
    {
        const char *arg1;
        const char *arg2;
    };
    
    static void some_func_(struct some_func_args_ args)
    {
        if(!args.arg1) args.arg1 = "default";
        printf("---\narg1 = %s\narg2 = %s\n", args.arg1, args.arg2);
    }
    
    int main(void)
    {
        some_func("foo", "bar");
        some_func(.arg1 = "spam");
        some_func(.arg2 = "eggs");
        return 0;
    }
    
        2
  •  6
  •   sellibitze    16 年前

    你可以用C++仿真这个:

    struct params {
       string foo_;
       double bar_;
       short  xxx_;
       params() : foo_("123"), bar_(3.1415), xxx_(42) {} // default parameters
       params& foo(string s) {foo_=s;return *this;}
       params& bar(double x) {bar_=x;return *this;}
       params& xxx(short x) {xxx_=x;return *this;}
    };
    
    void some_function(params const & p);
    
    int main() {
       some_function(params().bar(99.9).xxx(23));
    }
    

    但我不值得这么做。锅炉板太多。

    如果我记得正确的话,Stroustrup的书“C++的设计和演化”包含了一个讨论“命名参数”的特征请求的章节。结论大致是:不是个好主意。如果你想知道细节,就去看看。

        3
  •  6
  •   el.pescado - нет войне    16 年前

    C中不支持命名参数,但可以使用variadic函数模拟命名参数(尽管您释放了类型安全性):

    #include <stdarg.h>
    
    void do_sth (int foo, ...)
    {
        int baz = 7;             /* "baz" argument */
        const char *xyz = "xyz"; /* "xyz" argument */
    
        /* Parse named parameters */
        va_list ap;
        va_start (ap, foo);
        for (;;) {
            const char *key = va_arg (ap, char *);
            if (key == NULL) {
                /* Terminator */
                break;
            } else if (strcmp (key, "baz") == 0) {
                baz = va_arg (ap, int);
            } else if (strcmp (key, "xyz") == 0) {
                xyz = va_arg (ap, char *);
            } else {
                /* Handle error */
            }
        }
        va_end (ap);
    
        /* do something useful */
    }
    
    do_sth (1, NULL);                             // no named parameters
    do_sth (2, "baz", 12, NULL);                  // baz = 12
    do_sth (3, "xyz", "foobaz", NULL);            // xyz = "foobaz"
    do_sth (4, "baz", 12, "xyz", "foobaz", NULL); // baz = 12, xyz = "foobaz"
    

    只需记住以空结束可选参数列表。也可以使用一些枚举作为键,而不是字符串。

    例如,该技术在GTK+中使用:

        4
  •  3
  •   pdusen    16 年前

    这在香草C或C++中是不可用的。但是,有一个C++升压库,可以让您为编写的函数这样做: Boost.Parameter . 借用他们的一个例子,它的用法是这样的:

    myclass x("bob", 3);                     // positional
    myclass y(_index = 12, _name = "sally"); // named
    myclass z("june");                       // positional/defaulted
    

    然而,为您的函数实现这一点看起来确实有点复杂。你可能会认为这不值得付出努力。

        5
  •  1
  •   SoapBox    16 年前

    不,在C或C++中不能按名称传递参数。

    两种语言都支持 拖尾 函数中的参数。也就是说,函数的任何参数都可以有默认值,但在找到第一个默认值之后的所有后续参数也必须有默认值。

    例如,以下各项均有效:

    void f(int a, int b = 0);
    void g(double a = 1, double b = 2);
    void h(int a = 3, int b = 2, int c = 1, int d = 0);
    void i(float a, float b, float c = 1, float d = 2);
    

    但以下各项均无效:

    void j(int a = 1, int b);
    void k(int a, int b = 1, int c);
    void l(int a = 2, int b = 1, int c);
    

    原因很简单:因为在C和C++中不能按名称传递参数,所以知道使用默认值的哪些参数和使用传递值的唯一方法是将传递的值按顺序分配给参数,然后使用剩余的默认值(按顺序)。

    因此,以下调用使用上述函数是有效的:

    f(0); // calls f(0, 1);
    g(); // calls g(1,2);
    g(10); // calls g(10,2);
    h(); // calls h(3,2,1,0);
    h(1,2); // calls h(1,2,1,0);
    
        6
  •  0
  •   nategoose    16 年前
    #define call_foo(...) \
       do { \
          int first; \
          int second; \
          int third;  \
          (__VA_ARGS__); \
          foo(first, second, third); \
       } while (0)
    
    ....
    
    int main(void) {
       call_foo(first=9, third=5, second=3);
    }
    

    以非笨拙的方式获取返回值更困难。

    这将允许您做一些非常愚蠢的事情(我讨厌这样做),例如为参数指定默认值:

    #define call_foo(...) \
       do { \
          int first = default_first; \
          int second; \
          int third;  \
          (__VA_ARGS__); \
          foo(first, second, third); \
       } while (0)
    

    如果您这样做,将失败:

       int first = 9;
       call_foo(first=first, third=5, second=3);
    

    因为在宏扩展中 first=first 将意味着初始化本地 first 与它自己。我曾考虑过用预处理器连接来绕过这个问题,但这会变得更复杂。您必须列出与函数参数相同数量的宏参数。

    #define call_foo(x, y , z) \
       do { \
          int call_foo_first; \
          int call_foo_second; \
          int call_foo_third;  \
          call_foo_##x;   \
          call_foo_##y;   \
          call_foo_##z;   \
          foo(call_foo_first, call_foo_second, call_foo_third); \
       } while (0)
    

    这个 call_foo_##x; 线会变成 call_foo_first=first; 如果在前面的示例中调用,那么每个符号都有自己的唯一名称。 这使得默认的参数技巧更加尴尬,因为您需要指定一些东西来填充宏参数列表中的那个位置。

    如果宏是用默认参数定义的, 第一 然后:

    call_foo(first, third=7, second=8);
    

    这迫使您列出所有参数(或者至少是导致合法C的一些参数——您可能已经列出了第二个参数,但无论如何都可以这样做),以便使用这个宏。

    我认为您应该能够为列出变量参数的宏/函数扩展此函数的最后一个版本,但是可选参数仍然必须在列表的末尾传递,并且在获得可选参数之前,您必须使用所有必需的点。

        7
  •  -1
  •   José Tomás Tocino    16 年前

    C++解决方案: 您可以使用boost::any、std::map和std::string创建带有可设置位置参数的对象。

    #include <iostream>
    #include <map>
    #include <string>
    
    #include "boost/any.hpp"
    
    using namespace std;
    
    void variableArguments(map<string, boost::any> arguments){
        cout << ">>> variableArguments begins" << endl;
    
        if(arguments.find("intParameter") != arguments.end()){
        cout << "Found int parameter: " 
             << boost::any_cast<int>(arguments["intParameter"]) << endl;
        }
    
        if(arguments.find("stringParameter") != arguments.end()){
        cout << "Found string parameter: " 
             << boost::any_cast<string>(arguments["stringParameter"]) << endl;
        }
    
        cout << "<<< variableArguments ends" << endl;
    
    }
    
    int main(int argc, char *argv[])
    {
    
        map<string, boost::any> argMap;
        argMap["intParameter"] = 5;
        argMap["stringParameter"] = string("MyString");
    
        variableArguments(argMap);
    
        argMap.erase(argMap.find("intParameter"));
    
        variableArguments(argMap);
    
        return 0;
    }