您只能要求用户显式地将其输入标记为字面量或分配的字符串。
但是,正如@Mints97在他的回答中提到的那样,基本上这种方法在架构上是不正确的:您强制库的用户执行某些显式操作,如果他忘记了,最有可能导致内存泄漏(甚至应用程序崩溃)。因此,只有在以下情况下才使用它:
- 您想大大减少分配的数量。在我的情况下,这是JSON节点名称,在程序生命周期中从不更改。
- 您可以对您的库的消费者代码有很好的控制。在我的情况下,库与二进制文件一起提供,并且与其紧密绑定。
实现示例
#define AAS_DYNAMIC 'D'
#define AAS_STATIC 'S'
#define AAS_STATIC_PREFIX "S"
#define AAS_CONST_STR(str) ((AAS_STATIC_PREFIX str) + 1)
char* aas_allocate(size_t count) {
char* buffer = malloc(count + 2);
if(buffer == NULL)
return NULL;
*buffer = AAS_DYNAMIC;
return buffer + 1;
}
void aas_free(char* aas) {
if(aas != NULL) {
if(*(aas - 1) == AAS_DYNAMIC) {
free(aas - 1);
}
}
}
...
char* s1 = AAS_CONST_STR("test1");
char* s2 = aas_allocate(10);
strcpy(s2, "test2");
aas_free(s1);
aas_free(s2);
测试性能(注1)
我使用以下代码(800k次迭代)对我的libtsjson库进行基准测试:
node = json_new_node(NULL);
json_add_integer(node, NODE_NAME("i"), 10);
json_add_string(node, NODE_NAME("s1"), json_str_create("test1"));
json_add_string(node, NODE_NAME("s2"), json_str_create("test2"));
json_node_destroy(node);
我的CPU是Intel Core i7 860。
如果NODE_NAME
只是一个宏,每次迭代的时间为479ns。
如果NODE_NAME
是内存分配,则每次迭代的时间为609ns
提示用户或编译器(注意#2)
char __autostring* s1 = aas_copy("test"); /* OK */
char __autostring* s2 = strdup("test"); /* Should be fail? */
char* s3 = s1; /* Confuses sparse */
char* s4 = (char*) s1; /* Explicit conversion - OK */
(不完全确定 Sparse 的输出)
#ifdef AAS_STRICT
typedef struct { char a; } *aas_t;
#else
typedef char *aas_t;
#endif
这种方法是迈向一个充斥着肮脏C语言黑客技巧的世界的又一步,即sizeof(*aas_t)
现在大于1。
完整的更改源代码可以在此处找到。如果使用-DAAS_STRICT
编译它,将会出现大量错误:https://ideone.com/xxmSat 即使对于正确的代码,它也可能抱怨strcpy()
(在ideone上未重现)。
free
。例如char buffer[20]; b=buffer;
,buffer 不是字面量,但它也没有在堆上分配。 - Seanchar*
,无法确定它指向哪里。它只是一个 char 指针,不管你如何设置它,它都没有任何附加信息。如果您进行动态分配,则必须自行跟踪它。 - P.P