C字典/地图

我想映射struct成员,这样我就可以消除循环中的分支。 在C中实现此function的最佳方式或约定是什么? 我想它可能是一个二维数组而不是……然后我可以将整数映射到char键?

char chunk[32]; int n; int i; char *ptr = config; while (*ptr != '\0') { int items_read = sscanf(ptr, "%31[^;]%n", chunk, &n); if(chunk[0] == 'S' && chunk[1] == 'P') { for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) { theMeas[i].signal_path = atoi(&chunk[2]); } } if(chunk[0] == 'T' && chunk[1] == 'L') { for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) { theMeas[i].trace_length = atoi(&chunk[2]); } } if(chunk[0] == 'S' && chunk[1] == 'R') { for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) { theMeas[i].sample_rate = atoi(&chunk[2]); } } chunk[0]='\0'; if (items_read == 1) ptr += n; if ( *ptr != ';' ) { break; } ++ptr; } 

我怀疑你(理想情况下)想要的是一本字典:

 theMeas[i]["signal_path"] = atoi(&chunk[2]); 

当然,上面的语法永远不会发生在C中,但这在这里并不重要。 问题是你必须编写实现字典数据类型的所有代码,我怀疑这是过度的。

所以我怀疑你(真正)想要的是一种可以在循环中使用的名称:

 foreach(signal_path, trace_length, sample_rate) 

我在这里告诉你,你可以这样做(有点)! 最简单的方法是使用enum

 enum fields { signal_path, trace_length, sample_rate, END_fields, UNKNOWN_fields, BEGIN_fields = 0, }; 

您使用数组而不是struct成员:

 int theMeas[size][END_fields]; 

要索引“成员”,请使用以下命令:

 theMeas[i][signal_path]; 

你可以循环遍历所有“成员”,你可以使用这个:

 for(enum fields j = BEGIN_fields; j != END_fields; j++) theMeas[i][j]; 

当你想要进行基于字符的比较时,这确实会分解,但我们可以做一点点:

 const char *to_str(enum fields f) { #define FIELD(x) case x: return #x switch(f) { FIELD(signal_path); FIELD(trace_length); FIELD(sample_rate); default: return ""; } #undef FIELD } enum fields from_str(const char *c) { #define FIELD(x) if(!strcmp(c, #x)) return x FIELD(signal_path); FIELD(trace_length); FIELD(sample_rate); default: return UNKNOWN_fields; #undef FIELD } enum fields from_abv(char *c) { for(enum fields i = BEGIN_fields; i < END_fields; i++) { char *field = field_str(i); if(tolower(c[0]) == field[0] && tolower(c[1]) == strchr(field, '_')[1]) return i; } return UNKNOWN_fields; } 

您的if语句可以替换为:

 theMeas[i][from_abv(chunk)] = atoi(&chunk[2]); 

或者,更安全:

 enum fields j = from_abv(chunk); if(j != UNKNOWN_fields) theMeas[i][j] = atoi(&chunk[2]); else /* erroneous user input */; 

这是我能得到的尽可能接近。

请注意,我故意使用命名方案来促进宏的创建,这将自动化大部分内容。 我们试试吧:

 #define member(name, ...) \ enum name { __VA_ARGS__, \ M_END_##name, \ M_UNKNOWN_##name, \ M_BEGIN_##name = 0 } #define miter(name, var) \ enum name var = M_BEGIN_##name; var != M_END_##name; var++ #define msize(name) M_END_##name 

用法:

 // define our fields member(fields, signal_path, trace_length, sample_rate); // declare object with fields int theMeas[N][msize(fields)]; for(size_t i = 0; i < N; i++) // iterate over fields for(miter(fields, j)) // match against fields if(j == from_abv(chunk)) theMeas[i][j] = atoi(&chunk[2]); 

最后一点似乎并不那么糟糕。 它仍然允许您通过theMeas[i][signal_path]接近类似struct的访问,但允许您遍历“成员”,并隐藏宏后面的大部分繁重工作。

to_strfrom_str函数需要更多的宏from_str来自动化。 您可能需要考虑P99。 对于一般情况, from_abv函数不是我推荐的,因为我们无法保证下次创建可迭代字段时您将使用带下划线的名称。 (当然,您可以删除from_abv函数并为您的成员提供不可思议的名称,如SPTLSR ,允许您直接将它们与字符串数据进行比较,但您需要将strcmp更改为具有大小的memcmp (sizeof(#x) - 1) 。然后你所有的地方from_abv你只需要使用from_str ,它可以自动为你生成。)

但是, from_abv并不难定义,你可以诚实地将上面的if块复制并粘贴到它中 - 它会稍微提高效率,但是如果你添加了一个“成员”,你必须更新这个function(如上所述,如果添加成员,它将自动更新。)

C支持指向函数的指针 ,因此您可以创建一个指向函数的指针数组,并根据您的输入寻址数组。 这将要求您使用相同的签名实现其他function。

另一种方法可能是将if子句封装在一个单独的函数中并使用参数调用它。

但是,我认为如果有的话,你不会获得太多的加速。

您可以使用指向整数的指针重写您的逻辑:

 while (*ptr != '\0') { int items_read = sscanf(ptr, "%31[^;]%n", chunk, &n); int *p = NULL; if(chunk[0] == 'S' && chunk[1] == 'P') { p = &theMeas[i].signal_path; } if(chunk[0] == 'T' && chunk[1] == 'L') { p = &theMeas[i].trace_length; } if(chunk[0] == 'S' && chunk[1] == 'R') { p = &theMeas[i].sample_rate; } for(i=0;i 

这种方法将关于要更改的变量( if语句)与实际执行每种情况的工作的代码( for循环)的决策分开。

当然,如果chunk[0]chunk[1]与你期望的任何东西都不匹配,你当然想检查p == NULL

不幸的是,简单的C99这是不可能的,因为数组索引只能是无符号整数。 但是strncmp strncmp()函数更适合你吗?

 #define EQUALN(a,b,n) (strncmp(a, b, n) == 0) ... if(EQUALN(chunk, "SP", 2)) { for(i=0;i 

如果(并且它是一个相当大的if)你可以依赖数据总是这三个选项中的一个,那么我们可以在这三种情况下构造一个“最小完美哈希”。 假设charset是ASCII(或与ASCII一致):

 L = 76, 0 mod 4 P = 80, 0 mod 4 R = 82, 2 mod 4 S = 83, 3 mod 4 T = 84, 0 mod 4 

所以,S + P是3 mod 4,T + L是0 mod 4,S + R是1 mod 4.不是最小,但足够接近:

 size_t lookup[3] = { offsetof(Mea, trace_length), offsetof(Mea, sample_rate), 0, offsetof(Mea, signal_path) }; size_t offset = lookup[((unsigned)chunk[0] + chunk[1]) % 4]; for(i=0;i 

你可能更喜欢用宏或内联函数在这个猪上int *fieldptr一些口红,或者代替int *fieldptr有一个char *fieldptr ,从((char*)theMeas) + offset ,并按sizeof(Mea)递增时间。

如果您不能依赖友好数据,那么您至少需要一个某种类型的分支(条件或通过函数指针调用),以避免在数据不好的情况下编写任何内容。 即使将它保持为1,你可能需要一个64k条目的查找表来处理3个案例,这种情况很稀疏,所以你可能会更好地使用条件。