C 有没有一种方法来定义“一个”;普通的;多参数编号和类型的结构

C 有没有一种方法来定义“一个”;普通的;多参数编号和类型的结构,c,C,我想创建一个公共结构,我可以使用它将多个长度和类型的参数传递到不同的函数中 作为一个例子,考虑以下结构: typedef struct _list_t { int ID; char *fmt; int nparams; } list_t; list_t infoList[100]; //this will be pre-populated with the operations my app offers typedef struct _common { i

我想创建一个公共结构,我可以使用它将多个长度和类型的参数传递到不同的函数中

作为一个例子,考虑以下结构:

typedef struct _list_t {
    int ID;
    char *fmt;
    int nparams;
} list_t;
list_t infoList[100]; //this will be pre-populated with the operations my app offers 


typedef struct _common {
    int ID;
    char *params;
} common;
如果已填充格式,则使用可变大小函数传递参数:

int Vfunc(common * c, ...) {
    va_list args;
    va_start(args, c);
    
    //code to search for ID in infoList and fetch its fmt
    char params_buff[100]; //max params is 100
    vsprintf(str_params, fmt, args);

    va_end(args);

    c->params = (char *)malloc(sizeof(char)*(strlen(params_buff)+1));
    strncpy(c->params, params_buff, strlen(params_buff)+1);
}
int execute(common * c) { 
    if (c->ID == 1) { //add 2 numbers
        int x, y; // i expect 2 numbers 
        //code to find ID in infoList and fetch its fmt
        sscanf(c->params, fmt, &x, &y);
    
        return (x + y);
    }
    else if (c->ID == 2) {
    //do another operation, i expect an unsigned char array?
    }
    
}
主程序看起来有点像这样:

int main()
{
    common c;
    c.ID = 1;

    Vfunc(&c, 12, 2);
    
    execute(&c);
    
    return 0;
}
现在我可以将结构传递给任何函数,这些函数将适当地处理参数。但是,我没有看到将
unsigned char[]
作为参数之一的方法,因为unsigned char数组没有“格式”。
char[]
的格式应为
%s
。基本上我想通过这个结构传入一些原始数据

有没有办法做到这一点或更好地实现目标

编辑:

问题的目标似乎不明确。假设我的应用程序可以提供算术运算(如计算器)。假设我的应用程序的一个用户想要添加2个数字。我想让他们做的就是填写这个公共结构,然后将它传递到一个函数中,让它执行。所有操作的ID都将从手册中得知,因此用户将知道他们可以传递多少个参数,以及ID的作用。作为应用程序所有者,我将用我提供的ID填写
infoList


所以这只是给你们一个我所说的“公共结构”的概念。它也可以用其他方式实现,也许你有更好的方法。但我的目标是实现能够传入一个无符号字符数组。我可以这样做吗?

我理解您的问题,您希望将所有参数值保存在文本字符串中,以便以后可以使用
sscanf
重新构建这些值。此外,您希望能够处理数字数组,例如无符号字符数组

你会问:

有办法做到这一点吗

为了让您的想法发挥作用,需要
sscanf
能够解析(aka match)您希望在程序中使用的数据类型。并且-正如你在问题中所写-
scanf
无法解析数字数组。所以答案是:

,无法使用标准功能完成

所以,如果您希望能够处理数字数组,就必须编写自己的扫描函数。这包括

  • 选择转换说明符,告知代码扫描数组(例如%b)
  • 为数组选择文本格式(例如“{1,2,3}”)
  • 存储数组数据和大小的一种方法,例如
    struct{unsiged char*p;size\u t nb\u elements;}
  • 此外,
    vsprintf
    也会有同样的问题。同样,您需要编写自己的函数

    编辑

    另一种选择(我并不喜欢自己)是存储指针值。也就是说,您可以存储指向数组的指针,而不是将数组值存储在字符串
    params

    这种方法的优点是可以使用标准函数

    缺点是调用方必须确保数组存在,直到调用了
    execute

    换言之:

    unsigned char auc[] = {1, 2, 3};
    Vfunc(&c, auc);
    execute(&c);
    
    可以,但是

    Vfunc(&c, (unsigned char[]){1, 2, 3});
    execute(&c);
    
    将编译,但在运行时失败

    和C语言中的数组一样,您可能需要一个额外的参数来表示数组元素的数量

    此“另存为指针”方法的示例代码可以是:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stdarg.h>
    
    typedef struct _common {
        int ID;
        char *params;
    } common;
    
    void Vfunc(common * c, ...) {
        va_list args;
        va_start(args, c);
    
        //code to search for ID in infoList and fetch its fmt
        // For this example just use a fixed fmt
        char fmt[] ="%p %zu";
    
    
        char params_buff[100]; //max params is 100
        vsprintf(params_buff, fmt, args);
    
        va_end(args);
    
        c->params = (char *)malloc(sizeof(char)*(strlen(params_buff)+1));
        strncpy(c->params, params_buff, strlen(params_buff)+1);
    }
    
    int execute(common * c)
    {
        if (c->ID == 1) {
          // expect pointer and number of array elements
          unsigned char* a;
          size_t nbe;
    
          //code to find ID in infoList and fetch its fmt
          // For this example just use a fixed fmt
          char fmt[] ="%p %zu";
    
          if (sscanf(c->params, fmt, &a, &nbe) != 2) exit(1);
    
          // Calculate average
          int sum = 0;
          for (size_t i = 0; i < nbe; ++i) sum += a[i];
          return sum;
        }
    
        return 0;
    }
    
    int main(void)
    {
      common c;
      c.ID = 1;
    
      unsigned char auc[] = {1, 2, 3, 4, 5, 6};
    
      Vfunc(&c, auc, sizeof auc / sizeof auc[0]);
    
      printf("The saved params is \"%s\"\n", c.params);
      printf("Sum of array elements are %d\n", execute(&c));
    
      return 0;
    }
    

    请注意,保存的不是数组数据,而是指针值。

    我理解您的问题,您希望将所有参数值保存在文本字符串中,以便以后可以使用
    sscanf
    重构这些值。此外,您希望能够处理数字数组,例如无符号字符数组

    你会问:

    有办法做到这一点吗

    为了让您的想法发挥作用,需要
    sscanf
    能够解析(aka match)您希望在程序中使用的数据类型。并且-正如你在问题中所写-
    scanf
    无法解析数字数组。所以答案是:

    ,无法使用标准功能完成

    所以,如果您希望能够处理数字数组,就必须编写自己的扫描函数。这包括

  • 选择转换说明符,告知代码扫描数组(例如%b)
  • 为数组选择文本格式(例如“{1,2,3}”)
  • 存储数组数据和大小的一种方法,例如
    struct{unsiged char*p;size\u t nb\u elements;}
  • 此外,
    vsprintf
    也会有同样的问题。同样,您需要编写自己的函数

    编辑

    另一种选择(我并不喜欢自己)是存储指针值。也就是说,您可以存储指向数组的指针,而不是将数组值存储在字符串
    params

    这种方法的优点是可以使用标准函数

    缺点是调用方必须确保数组存在,直到调用了
    execute

    换言之:

    unsigned char auc[] = {1, 2, 3};
    Vfunc(&c, auc);
    execute(&c);
    
    可以,但是

    Vfunc(&c, (unsigned char[]){1, 2, 3});
    execute(&c);
    
    将编译,但在运行时失败

    和C语言中的数组一样,您可能需要一个额外的参数来表示数组元素的数量

    此“另存为指针”方法的示例代码可以是:

    #include <stdio.h>
    #include <string.h>
    #include <stdlib.h>
    #include <stdarg.h>
    
    typedef struct _common {
        int ID;
        char *params;
    } common;
    
    void Vfunc(common * c, ...) {
        va_list args;
        va_start(args, c);
    
        //code to search for ID in infoList and fetch its fmt
        // For this example just use a fixed fmt
        char fmt[] ="%p %zu";
    
    
        char params_buff[100]; //max params is 100
        vsprintf(params_buff, fmt, args);
    
        va_end(args);
    
        c->params = (char *)malloc(sizeof(char)*(strlen(params_buff)+1));
        strncpy(c->params, params_buff, strlen(params_buff)+1);
    }
    
    int execute(common * c)
    {
        if (c->ID == 1) {
          // expect pointer and number of array elements
          unsigned char* a;
          size_t nbe;
    
          //code to find ID in infoList and fetch its fmt
          // For this example just use a fixed fmt
          char fmt[] ="%p %zu";
    
          if (sscanf(c->params, fmt, &a, &nbe) != 2) exit(1);
    
          // Calculate average
          int sum = 0;
          for (size_t i = 0; i < nbe; ++i) sum += a[i];
          return sum;
        }
    
        return 0;
    }
    
    int main(void)
    {
      common c;
      c.ID = 1;
    
      unsigned char auc[] = {1, 2, 3, 4, 5, 6};
    
      Vfunc(&c, auc, sizeof auc / sizeof auc[0]);
    
      printf("The saved params is \"%s\"\n", c.params);
      printf("Sum of array elements are %d\n", execute(&c));
    
      return 0;
    }
    

    请注意,保存的不是数组数据,而是指针值。

    我认为您希望从描述中看到的可能是结构的并集,其中第一个并集成员是定义所用结构类型的枚举数,这就是多态性通常在C中实现的方式。查看X11头中的一个Xevents的巨大示例

    一个简单的例子:

    //define our types
    typedef enum  {
    chicken,
    cow,
    no_animals_defined
    } animal;
    
    typedef struct {
    animal species;
    int foo;
    char bar[20];
    } s_chicken;
    
    typedef struct {
    animal species;
    double foo;
    double chew;
    char bar[20];
    } s_cow;
    
    typedef union {
    animal species; // we need this so the receiving function can identify the type.
    s_chicken chicken ;
    s_cow cow ;
    } s_any_species;
    
    现在,这个结构可以被传递给一个函数,并采用任意一个标识。接收函数
    #include <stdio.h>
    #include <stdlib.h>
    #include <stdarg.h>
    #include <string.h>
    
    // note: it is the most fundamental part of this solution
    //       'sarah_next' is a macro, that
    //       returns *(type *)buf means a value of type "type", and also
    //       increments 'buf' by 'sizeof(type)', so that
    //       it may target next element
    //       'sarah_next' is used to retrieve data from task cache
    
    // I've named it after you, you may choose to name it as you wish
    #define sarah_next(cache, type)                        \
            (((cache) = (cache) + sizeof(type)),           \
            *((type*) (char *) ((cache) - sizeof(type))))
    
    
    // defining pool size for task pool
    #define POOL_SIZE 1024
    
    
    // notice: getSize() has been updated to support unsigned char and unsigned char *
    // getSize() is a function that returns type size based on type hint
    size_t getSize(char type) {
        if(type == 's') {
            return sizeof(char *);
        }
        if(type == 'c') {
            return sizeof(char);
        }
        if(type == 'i') {
            return sizeof(int);
        }
        if(type == 'u') { // 'u' represents 'unsigned char'
            return sizeof(unsigned char);
        }
        if(type == 'x') { // let's, say 'x' represents 'unsigned char *'
            return sizeof(unsigned char *);
        }
        // you can add your own custom type here
        // also note: you can easily use 'unsigned char'
        //            use something like 'u' to represent 'unsigned char'
        //            and you're done
        // if type is not recognized, then
        printf("error: unknown type while trying to retrieve type size\n");
        exit(1);
    }
    
    
    typedef struct __task {
        int id;
        void *cache;
    } Task;
    
    // notice: constructTask has been updated to support unsigned char and unsigned char *
    // note: here, types contains type hint
    Task *constructTask(int id, char *types, ...) {
        // determine the size of task cache
        int cacheSize = 0;
        for(int i=0; types[i]; i++) {
            cacheSize += getSize(types[i]);
        }
        // allocate memory for task cache
        void *cache = malloc(sizeof(char) * cacheSize);
        
        va_list buf;
        va_start(buf, types);
        
        // itr is an iterator, which iterates over cache
        char *itr = (char *)cache;
        for(int i=0; types[i]; i++) {
            if(types[i] == 's') {
                *(char **)itr = va_arg(buf, char *);
    
            } else if(types[i] == 'x') { // added support for 'unsigned char *'
                *(unsigned char **)itr = va_arg(buf, unsigned char *);
    
            } else if(types[i] == 'c') {
                // notice: i used 'int' not 'char'
                // cause: compiler-warning: 'char' is promoted to 'int' when passed through '...'
                // also note: this promotion helps with 'unsigned char'
                *(char *)itr = (char)va_arg(buf, int); // so cast it to char
    
            } else if(types[i] == 'u') { // added support 'unsigned char'
                // notice: i used 'int' not 'unsigned char'
                // cause: compiler-warning: 'unsigned char' is promoted to 'int' when passed through '...'
                // also note: this promotion helps with 'unsigned char'
                *(unsigned char *)itr = (unsigned char)va_arg(buf, int); // so cast it to unsigned char
    
            } else if(types[i] == 'i') {
                *(int *)itr = va_arg(buf, int);
    
            }
            // it won't come to else, cause getSize() would
            // caught the type error first and exit the program
            itr += getSize(types[i]);
        }
    
        va_end(buf);
    
        // now, construct task
        Task *task = malloc(sizeof(Task));
        task->id = id;
        task->cache = cache;
        // and return it
        return task;
    }
    
    // destroyTask is a function that frees memory of task cache and task
    void destroyTask(Task *task) {
        free(task->cache);
        free(task);
    }
    
    // notice: that 'task->id == 4' processing part
    // it is equivalant to your 'execute()' function
    int taskProcessor(Task *task) {
        // define ret i.e. return value
        int ret = 999; // by default it is some code value, that says error
    
        // note: you already know, what type is required in a task
        if(task->id == 1) {
            // note: see usage of 'sarah_next()'
            int x = sarah_next(task->cache, int);
            int y = sarah_next(task->cache, int);
    
            ret = x + y;
    
        } else if(task->id == 2) {
            char *name = sarah_next(task->cache, char *);
            if(strcmp(name, "sarah") == 0) {
                ret = 0; // first name
            } else if (strcmp(name, "cartenz") == 0) {
                ret = 1; // last name
            } else {
                ret = -1; // name not matched
            }
        } else if(task->id == 3) {
            int x = sarah_next(task->cache, int);
            char *name = sarah_next(task->cache, char *);
            int y = sarah_next(task->cache, int);
    
            printf("%d %s %d\n", x, name, y); // notice: we've been able to retrieve
            // both string(i.e. char *) and int
            // you can also see for ch and int, but i can assure you, it works
    
            ret = x + y;
    
        } else if(task->id == 4) { // working with 'unsigned char *'
            int a = sarah_next(task->cache, int);
            unsigned char *x = sarah_next(task->cache, unsigned char *); // cast to unsigned char *
            // char *x = sarah_next(task->cache, char *); // this won't work, would give wrong result
            int b = sarah_next(task->cache, int);
    
            printf("working with 'unsigned char *':");
            for(int i=0; x[i]; i++) {
                printf(" %d", x[i]); // checking if proper value is returned, that's why using 'integer'
            }
            printf("\n");
    
            ret = a + b;
        } else {
            printf("task id not recognized\n");
        }
    
        return ret;
    }
    
    
    int main() {
        Task *taskPool[POOL_SIZE];
    
        int taskCnt = 0;
    
        taskPool[taskCnt++] = constructTask(1, "ii", 20, 30); // it would return 50
        taskPool[taskCnt++] = constructTask(1, "ii", 50, 70); // it would return 120
        taskPool[taskCnt++] = constructTask(2, "s", "sarah"); // it would return 0
        taskPool[taskCnt++] = constructTask(2, "s", "cartenz"); // it would return 1
        taskPool[taskCnt++] = constructTask(2, "s", "reyad"); // it would return -1
        taskPool[taskCnt++] = constructTask(3, "isi", 40, "sarah", 60); // it would print [40 sarah 60] and return 100
    
        // notice: I've added an exmaple to showcase the use of unsigned char *
        // also notice: i'm using value greater than 127, cause
        // in most compiler(those treat char as signed) char supports only upto 127
        unsigned char x[] = {231, 245, 120, 255, 0}; // 0 is for passing 'NULL CHAR' at the end of string
        // 'x' is used to represent 'unsigned char *'
        taskPool[taskCnt++] = constructTask(4, "ixi", 33, x, 789); // it would print ['working with unsigned char *': 231 245 120 255] and return 822
        // note: if you used 'char *' cast to retrieve from 'cache'(using a compiler which treats char as signed), then
        //       it would print [-25 -11 120 -1] instead of [231 245 120 255]
        //       i guess, that makes it clear that you can perfectly use 'unsigned char *'
    
        for(int i=0; i<taskCnt; i++) {
            printf("task(%d): %d\n", i+1, taskProcessor(taskPool[i]));
            printf("\n");
        }
    
        // at last destroy all tasks
        for(int i=0; i<taskCnt; i++) {
            destroyTask(taskPool[i]);
        }
    
        return 0;
    }
    
    // notice the updated output
    task(1): 50                                        
                                                       
    task(2): 120                                       
                                                       
    task(3): 0                                         
                                                       
    task(4): 1                                         
                                                       
    task(5): -1                                        
                                                       
    40 sarah 60                                        
    task(6): 100                                       
                                                       
    working with 'unsigned char *': 231 245 120 255    
    task(7): 822
    
    enum tag { TAG_INT, TAG_DOUBLE, TAG_CHAR_PTR };
    union tagged {
        struct {
            enum tag tag;
            // no data -- this explicitly gives generic access to the tag
        } as_any;
        struct {
            enum tag tag;
            int data;
        } as_int;
        struct {
            enum tag tag;
            double data;
        } as_double;
        struct {
            enum tag tag;
            char *data;
        } as_char_ptr;
        // etc.
    };
    
    struct arg_list {
        unsigned num;
        union tagged *args;
    };
    
    int foo(char *s, double d) {
        char[16] buffer;
        sprintf(buffer, "%15.7e", d);
        return strcmp(s, buffer);
    }
    
    union tagged foo_wrapper(struct arg_list args) {
        // ... validate argument count and types ...
    
        return (union tagged) { .as_int = {
            .tag = TAG_INT, .data = foo(args[0].as_char_ptr.data, args[1].as_double.data)
        } };
    }
    
    void demo_foo_wrapper() {
        union tagged arg_unions[2] = {
            { .as_char_ptr = { .tag = TAG_CHAR_PTR, .data = "0.0000000e+00" },
            { .as_double =   { .tag = TAG_DOUBLE,   .data = 0.0 }
        };
        union tagged result = foo_wrapper((struct arg_list) { .num = 2, .args = arg_unions});
        printf("result: %d\n", result.as_int.data);
    }
    
    union varying {
        int as_int;
        double as_double;
        char *as_char_ptr;
        // etc.
    };
    
    struct arg_list {
        unsigned num;
        union varying *args;
    };
    
    union varying foo_wrapper(struct arg_list args) {
        return (union vaying) { .as_int = foo(args[0].as_char_ptr, args[1].as_double) };
    }
    
    void demo_foo_wrapper() {
        union varying arg_unions[2] = {
            .as_char_ptr = "0.0000000e+00",
            .as_double   = 0.0
        };
        union varying result = foo_wrapper((struct arg_list) { .num = 2, .args = arg_unions});
        printf("result: %d\n", result.as_int);
    }