Ensure static storage for a function parameter

2020-07-18 11:25发布

We have this function prototype:

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

Most of the code using this(and similar) are autogenerated code, and looks like:

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

The function allocates a new BNode and copies the val string into it, but it just assigns the name member to a pointer, e.g.

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

This wrecks havoc if the first argument in b_new_node("foo.bar.id.1", 0, 10, some_data); is not a string literal, or otherwise something with static storage duration, but e.g. a buffer on the stack.

Is there anyway, with gcc (other compilers are of interest too), we can have a compile time check that this argument is passed in is of static storage ?

(ofcourse the easy way to avoid these possible problems is to copy that argument too into the node - the measurements we did with that approach rises the memory need by 50% and slows the program down by 10%, so that approach is undesirable).

标签: c gcc
3条回答
霸刀☆藐视天下
2楼-- · 2020-07-18 11:55

You can make BNode conditionally copy the name. That will require and extra bit of storage in 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);
}
查看更多
Bombasti
3楼-- · 2020-07-18 12:05

This will detect string literals:

#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

So you can wrap your b_new_node() function in a macro, perhaps just for a debug build, and harness the division by zero warnings.

Note that it only detects string literals as "arguments", not static storage, e.g.

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";
查看更多
虎瘦雄心在
4楼-- · 2020-07-18 12:08

Generally no; there is no C-provided facility to know whether or not a pointer points to something in static storage. Specific environments and data structures may change the circumstances - such as checking if the pointed-to address is in a read-only memory segment.

Saving space by countering duplication

To eliminate duplicated values, as pointed to by name, you could use the flyweight pattern which is not very different from string interning.

Basically you build a central set of encountered tokens and store only the reference to each token. The references could be array indices or pointers.

To be able to clean-up quickly, you could combine the flyweight pattern with reference counting where a count of zero, means no references left.

To keep performance of the central store high, use a data structure where look-up is fast such as a set, or with a map if reference counting is used.

查看更多
登录 后发表回答