我想编写一个低级日志记录功能,如下所示:
DO_DBG("some string", val1, val2)
出于性能原因,我想要它做的是将指针存储到字符串而不是字符串的副本。这假设字符串是只读文字。为了防止人们不得不调试调试器,如果DO_DBG
的第一个参数位于代码与文本等的可写部分中,编译器可能会抱怨会很好。我想知道是否有一个机制可以做到这一点存在。 (我正在使用gcc 4.9.1,ld 2.24)。
答案 0 :(得分:8)
您可以使用自动文字字符串连接:
#define DO_DBG(s, a, b) _DO_DBG("" s, a, b)
将您的真实宏实现为_DO_DBG
。
答案 1 :(得分:2)
您可以设置SIGSEGV
处理程序,然后尝试执行以下操作:
s[0] = s[0];
如果处理程序被触发,则意味着该变量位于只读段中,因此您可以保存指针。如果没有,你可以抱怨(或只是复制一下字符串)。
您需要声明字符串volatile
,以便编译器不会优化分配。
答案 2 :(得分:0)
或者你可以使用stringify宏构造:
#define DO_DBG(s, a, b) DO_DBG_INTERNAL(# s, a, b)
并调用您的函数DO_DGB_INTERNAL
所以人们会这样做:
DO_DBG(some string, val1, val2)
PS,很可能是一个可变函数和宏在这里是一个好主意。
答案 3 :(得分:0)
您的函数获取指向字符串开头的指针。如果您的操作系统允许它,并且编译器以这种方式设置它,则可以在只读存储器区域中分配常量字符串(如示例中所示)。如果您的编译器是智能的,它将存储一个出现多次的字符串常量的副本。它甚至可能会发现某些字符串从未被使用过,而根本就没有存储它们。
除了上述内容之外,除非您的程序依赖于未定义的行为(在常量字符串上写入),否则您应该无法确定它是否在只读内存中。您可以比较字符串的地址,看它们是否只是一个副本,但就是这样。
在任何合理的 C实现中(即,一个不会过时的方式只是为了让你搞砸了),你将看不到(或者至多是如果字符串是只读,读写,一个或多个,那么性能差异非常小。
如果您使用char *
参数编写函数,并将所述指针存储起来(不是复制到指向的字符串),那么 总是会返回相同的字符串(除非你的环境真的很奇怪。)
答案 4 :(得分:0)
这是基于SIGSEGV处理程序的测试,用于检查指向的内存是否映射为只读:
#define _GNU_SOURCE
#include <stdio.h>
#include <signal.h>
#include <string.h>
#include <setjmp.h>
static sigjmp_buf label;
static void segv_hndlr(int Sig) { siglongjmp(label,1); } //safe in this ctx
_Bool is_ro(char volatile *X)
{
_Bool r=0;
struct sigaction old;
X[0]; //fault now if the target isn't writable
if(sigsetjmp(label,1))
{ r=1; goto out; }
sigaction(SIGSEGV,&(struct sigaction){.sa_handler=segv_hndlr}, &old);
X[0]=X[0];
out: sigaction(SIGSEGV,&old, NULL);
return r;
}
//example:
int main()
{
#define PR_BOOL(X) printf(#X"=%d\n", X)
char bar[]="bar";
char *baz = strdup("baz");
static int const static_ro_int = 42;
int const auto_ro_int = 42;
PR_BOOL(is_ro("foo")); //1
PR_BOOL(is_ro(bar)); //0
PR_BOOL(is_ro(baz)); //0
PR_BOOL(is_ro((void*)&static_ro_int)); //1
PR_BOOL(is_ro((void*)&auto_ro_int)); //0
}