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
无法解析数字数组。所以答案是:
否,无法使用标准功能完成
所以,如果您希望能够处理数字数组,就必须编写自己的扫描函数。这包括
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
无法解析数字数组。所以答案是:
否,无法使用标准功能完成
所以,如果您希望能够处理数字数组,就必须编写自己的扫描函数。这包括
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);
}