C 字典/地图

发布于 2024-11-07 07:29:43 字数 1031 浏览 0 评论 0原文

我想映射结构成员,这样我就可以消除循环中的 if 分支。在 C 中实现此功能的最佳方法或约定是什么?我想它可能是一个二维数组......然后我可以将整数映射到 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;
    }

I want to map struct members so I can eliminate if branches in a loop. What is the best way or convention to implement this in C? I suppose it could be a 2 dimensional array instead...then I could map integers to the char keys?

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

兔小萌 2024-11-14 07:29:43

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

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 "<unknown>";
      }
#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 函数需要更多的宏技巧才能实现自动化。您可能需要研究一下 P99。对于一般情况,我不推荐使用 from_abv 函数,因为我们无法保证下次创建可迭代字段时将使用带下划线的名称。 (当然,您可以删除 from_abv 函数并为您的成员提供难以理解的名称,例如 SPTLSR,允许您直接将它们与字符串数据进行比较,但您需要将 strcmp 更改为 memcmp,其大小参数为 (sizeof( #x) - 1)。然后,您拥有 from_abv 的所有位置都只需使用 from_str,它会自动为您生成。)

但是, from_abv 并不难定义,老实说,您可以直接将上面的 if 块复制并粘贴到其中 - 它会稍微更有效,但如果您添加一个“成员”,你必须更新该函数(如所写,如果你添加一个成员,它会自行更新。)

I suspect what you (ideally) want is a dictionary:

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

Of course, the above syntax will never happen in C, but that's not really important here. The problem is that you would have to write all the code implementing a dictionary data type, and I suspect that's overkill.

So I suspect what you (really) want is a way to have names that can be used in a loop:

foreach(signal_path, trace_length, sample_rate)

And I'm here to tell you that you can do this (kind of)! The simplest way is with an enum:

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

Instead of struct members, you use an array:

int theMeas[size][END_fields];

To index a "member", use this:

theMeas[i][signal_path];

You can loop through all the "members," you can use this:

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

This does break down a little when you want to get character-based comparisons, but we can do a little bit:

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 "<unknown>";
      }
#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;
}

Your if statements could be replaced with:

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

Or, more safely:

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

Which is about as close as I can get.

Note that I've deliberately used a naming scheme to facilitate the creation of macros that will automate much of this. Let's try:

#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

Usage:

// 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]);

That last bit doesn't seem so bad. It still allows you something close to struct-like access via theMeas[i][signal_path], but allows you to iterate over the "members," and hides most of the heavy lifting behind macros.

The to_str and from_str functions take a little more macro trickery to automate. You'll probably need to look into P99 for that. The from_abv function isn't something I'd recommend for the general case, as we have no way of guaranteeing that the next time you make iterable fields you'll use names with underscores. (Of course, you could drop the from_abv function and give your members inscrutable names like SP, TL, and SR, allowing you to directly compare them to your string data, but you'd need to change the strcmp to a memcmp with a size argument of (sizeof(#x) - 1). Then all the places you have from_abv you'd just use from_str, which can be automatically generated for you.)

However, from_abv isn't hard to define, and you could honestly just copy and paste your if blocks from above into it - it'd be slightly more efficient, though if you added a "member" you'd have to update the function (as written, it'll update itself if you add a member.)

旧人 2024-11-14 07:29:43

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

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

然而,我认为无论哪种方式,你都不会获得太多的加速(如果有的话)。

C supports pointers to functions, so you could create an array of pointers to functions and address the array according to your input. This would require you to implement additional functions with same signatures.

Another way might be to encapsulate the if-clauses in a separate function and calling it with the arguments.

However, I think neither way you will gain much speedup, if any.

酷炫老祖宗 2024-11-14 07:29:43

您可以使用指向整数的指针重写您的逻辑,如下所示:

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<GLOBAL_MEAS_CUTOFF; i++) {
        *p = atoi(&chunk[2]);
    }   

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

您当然需要检查 p == NULL 是否为 chunk[0]chunk[1] 与您的任何内容不匹配正在期待。

You might rewrite your logic something like this, using a pointer to an integer:

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<GLOBAL_MEAS_CUTOFF; i++) {
        *p = atoi(&chunk[2]);
    }   

This approach separates the decision about what variable to change (the if statements) from the code that actually does the work that is common to each case (the for loop).

You will of course want to check whether p == NULL in case chunk[0] and chunk[1] didn't match anything you were expecting.

或十年 2024-11-14 07:29:43

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

#define EQUALN(a,b,n) (strncmp(a, b, n) == 0)

...

if(EQUALN(chunk, "SP", 2)) {
    for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) {
        theMeas[i].signal_path = atoi(&chunk[2]);
    }
}    
else if(EQUALN(chunk, "TL", 2)) {
    for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) {
        theMeas[i].trace_length = atoi(&chunk[2]);
    }
}    
else if(EQUALN(chunk, "SR", 2)) {
    for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) {
        theMeas[i].sample_rate = atoi(&chunk[2]);
    }
}

Unfortunately with simple C99 this is not possible, since array indices can only be unsigned integers. But propably the strncmp() function suits you more?

#define EQUALN(a,b,n) (strncmp(a, b, n) == 0)

...

if(EQUALN(chunk, "SP", 2)) {
    for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) {
        theMeas[i].signal_path = atoi(&chunk[2]);
    }
}    
else if(EQUALN(chunk, "TL", 2)) {
    for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) {
        theMeas[i].trace_length = atoi(&chunk[2]);
    }
}    
else if(EQUALN(chunk, "SR", 2)) {
    for(i=0;i<GLOBAL_MEAS_CUTOFF; i++) {
        theMeas[i].sample_rate = atoi(&chunk[2]);
    }
}
抱猫软卧 2024-11-14 07:29:43

如果(这是一个相当大的如果)您可以依赖数据始终是这三个选项之一,那么我们可以在这三种情况下构造一个“最小完美哈希”。假设字符集是 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<GLOBAL_MEAS_CUTOFF; i++) {
    int *fieldptr = (int*)(((char*)(theMeas+i)) + offset);
    *fieldptr = atoi(&chunk[2]);
}

您可能更喜欢使用宏或内联函数给这只猪涂上口红,或者用 char *fieldptr 代替 int *fieldptr,从 ((char*)theMeas 开始) + offset,并每次将其增加 sizeof(Mea)

如果您不能依赖友好的数据,那么您至少需要某种类型的分支(条件或通过函数指针的调用),以避免在数据错误的情况下编写任何内容。即使将其保持为 1,您也可能需要 3 个案例的 64k 条目查找表,这有点稀疏,因此您可能最好使用条件语句。

If (and it's a fairly big if) you can rely on the data always being one of those three options, then we could construct a "minimal perfect hash" over the three cases. Assuming the charset is ASCII (or consistent with 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

So, S+P is 3 mod 4, T+L is 0 mod 4, and S+R is 1 mod 4. Not minimal, but close enough:

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<GLOBAL_MEAS_CUTOFF; i++) {
    int *fieldptr = (int*)(((char*)(theMeas+i)) + offset);
    *fieldptr = atoi(&chunk[2]);
}

You might prefer to put some lipstick on this pig with macros or inline functions, or instead of int *fieldptr have a char *fieldptr, start it at ((char*)theMeas) + offset, and increment it by sizeof(Mea) each time.

If you can't rely on friendly data, then you need at least one branch of some kind (a conditional or a call through a function pointer), just to avoid writing anything in the case where the data is bad. Even to keep it to 1 you probably need a 64k-entry lookup table for 3 cases, which is kind of sparse, so you're probably better off with the conditionals.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文