4

我们有这个函数原型:

  BNode *b_new_node(const char *name, int pos, int len, const char *val);

大多数使用这个(和类似的)代码是自动生成的代码,看起来像:

 b = b_new_node("foo.bar.id.1", 0, 10, some_data);

该函数分配一个新的 BNode 并将 val字符串复制到其中,但它只是将name成员分配给一个指针,例如

 b_strlcpy(new_node->val, val, sizeof new_node->val);
 new_node->name = name;

如果 b_new_node("foo.bar.id.1", 0, 10, some_data); 中的第一个参数,这会造成严重破坏 不是字符串文字,也不是具有静态存储持续时间的其他内容,而是例如堆栈上的缓冲区。

无论如何,使用 gcc(其他编译器也很感兴趣),我们可以在编译时检查传入的这个参数是静态存储的吗?

(当然,避免这些可能出现的问题的简单方法是将该参数也复制到节点中 - 我们使用该方法所做的测量将内存需求增加了 50% 并将程序减慢了 10%,因此这种方法是不可取的)。

4

3 回答 3

1

一般没有;没有 C 提供的工具可以知道指针是否指向静态存储中的某些内容。特定的环境和数据结构可能会改变情况——例如检查指向的地址是否在只读内存段中。

通过防止重复来节省空间

如 所指出的,要消除重复值,name您可以使用与字符串实习没有太大区别的享元模式

基本上,您构建了一组遇到的令牌并仅存储对每个令牌的引用。引用可以是数组索引或指针。

为了能够快速清理,您可以将享元模式与引用计数结合起来,其中计数为零表示没有剩余引用。

要保持中央存储的高性能,请使用查找速度快的数据结构,例如集合,或者如果使用引用计数,则使用映射

于 2013-02-13T13:55:09.910 回答
1

这将检测字符串文字:

#include <stdio.h>

#define PRINT_IT(c) do {\
if (__builtin_constant_p(c))\
    print_it(c, 1);\
else \
    1/__builtin_constant_p(c);\
} while (0)


void print_it(const char *c, int is_static)
{
    printf("%s is a constant %d\n", c, is_static);
}

int main(int argc, char *argv[])
{
    char bar[] = "bar";
    PRINT_IT("Foo"); //line 19
    PRINT_IT(bar);   //line 20

return 0;

}


$ gcc  foo.c 
foo.c: In function ‘main’:
foo.c:20: warning: division by zero

因此,您可以将 b_new_node() 函数包装在一个宏中,也许只是为了调试构建,并利用零警告除法。

请注意,它仅将字符串文字检测为“参数”,而不是静态存储,例如

const char *foo = "foo";
PRINT_IT(foo); //will generate a warning
PRINT_IT("foo"); //will not generate a warning
PRINT_IT(global_foo); //will generate a warning, even when. 
                      //global_foo is const char *foo = "foo"; or
                      //global_foo is const char foo[] = "foo";
于 2013-02-12T15:09:44.743 回答
0

您可以BNode有条件地复制名称。这将需要额外的存储空间BNode。例如:

typedef struct BNode {
    char const* name;
    unsigned char : own_name;
} BNode;

void b_copy_name(BNode* n) {
    if(!n->own_name) {
        char* p = strdup(n->name);
        if(p) {
            n->own_name = 1;
            n->name = p;
        }
        else {
            abort();
        }
    }
}

void b_destroy(BNode* n) {
    // ...
    if(n->own_name) 
         free(n->name);
}
于 2013-02-12T13:28:15.397 回答