确保函数参数的静态存储

时间:2013-02-12 10:08:39

标签: c gcc

我们有这个函数原型:

  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%,因此该方法是不希望的)。

3 个答案:

答案 0 :(得分: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";

答案 1 :(得分:1)

一般没有;没有C提供的工具来知道指针是否指向静态存储中的某些东西。特定环境和数据结构可能会改变情况 - 例如检查指向的地址是否在read-only memory segment中。

通过反复重复来节省空间

为了消除name指向的重复值,您可以使用与the flyweight pattern没有什么不同的string interning

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

为了能够快速清理,你可以将flyweight模式与reference counting结合起来,其中计数为零,意味着没有任何参考。

要保持中央商店的性能较高,请使用快速查找的数据结构(如集合),或使用map如果使用reference counting

答案 2 :(得分:0)

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

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