【问题标题】:C test if variable is in read-only sectionC测试变量是否在只读部分
【发布时间】:2016-05-07 16:49:13
【问题描述】:

我想编写一个低级的日志记录函数,如下所示:

DO_DBG("some string", val1, val2)

出于性能原因,我想要它做的是存储指向字符串的指针而不是字符串的副本。这假定字符串是只读文字。为了防止人们不得不调试调试器,如果DO_DBG 的第一个参数位于代码与文本的可写部分等情况下,如果编译器可以抱怨,那就太好了。我想知道是否存在这样做的机制。 (我使用的是 gcc 4.9.1,ld 2.24)。

【问题讨论】:

  • 如果任意变量字符串文字是只读的? C 标准保证字符串文字是只读的。但是,如果相等的字符串存储在同一地址,则由实现定义。
  • 是的,但是如果有人尝试将字符串文字以外的任何内容作为函数的第一个参数传递,我希望编译器编译失败。该函数将采用const char * 参数,我想知道它是否是文字...
  • 这似乎是在规避未定义的行为并将其替换为“更安全”的东西,例如编译时错误。我认为不修改编译器/打破 C 标准是不可能的。
  • 无论参数是字符串文字还是数组,您的函数都将获得一个指针。除非您担心并发修改,否则我不确定我是否看到了好处。
  • @cad 标准不保证字符串文字是只读的,它说写入它们具有未定义的效果

标签: c gcc macros


【解决方案1】:

您可以使用自动文字字符串连接来发挥您的优势:

#define DO_DBG(s, a, b) _DO_DBG("" s, a, b)

并将您的真实宏实现为_DO_DBG

【讨论】:

  • @KerrekSB - 然后将引发所需的编译时错误。
  • 太棒了——这正是我想要的。谢谢
【解决方案2】:

您也可以使用 stringify 宏构造:

#define DO_DBG(s, a, b) DO_DBG_INTERNAL(# s, a, b)

并调用你的函数DO_DGB_INTERNAL 所以人们会这样做:

DO_DBG(some string, val1, val2)

PS,很可能在这里使用可变参数函数和宏是个好主意。

【讨论】:

  • 字符串化的结果还是一个常量字符串,就像程序员用手写的字符串一样处理。这应该没什么区别。
  • 如果你这样做:DO_DBG(some_charstar_var, ...)你肯定会避免some_charstar_var的值改变影响DO_DBG存储的问题......
  • 一点也不。可变参数处理完全在运行时进行。
  • 你完全没有抓住重点。 OP 关注char * c;(c 指向某个字符串)DO_DBG(c, etc)DO_DEBUG saves the value of c(c 指向其他某个字符串)(访问了保存的值 DO_DBG,但在调用 DO_DBG 后可能无效或更改)。
  • 听起来他正在开发一种其他人将使用的调试工具,并试图使其尽可能万无一失,我对此表示赞赏——尽管这句格言有一些道理,它只会产生更大的效果傻瓜。
【解决方案3】:

你的函数得到一个指向字符串开头的指针。如果您的操作系统允许,并且编译器以这种方式设置它,则可以在只读内存区域中分配一个常量字符串(如您的示例中所示)。如果你的编译器很聪明,它会存储一个多次出现的字符串常量的副本。它甚至可能会发现某些字符串从未使用过,只是根本不存储它们。

除上述情况外,除非您的程序依赖于未定义的行为(避免写入常量字符串),否则您应该无法确定它是否在只读内存中。您可以比较字符串的地址以查看它们是否只有一份,但仅此而已。

在任何合理的 C 实现中(即,一个不会为了让你搞砸的方式),你将看不到(或至多是非常小的)性能差异,如果该字符串是只读的、可读写的、一份或多份。

如果您使用char * 参数编写函数,并将所述指针存储起来(不是指向字符串的副本),您应该总是得到相同的字符串(除非您的环境真是奇怪)。

【讨论】:

    【解决方案4】:

    你可以设置一个SIGSEGV处理程序,然后尝试做:

    s[0] = s[0];
    

    如果触发了handler,说明变量在只读段,可以保存指针。如果没有,您可以投诉(或只是复制字符串)。

    您需要声明字符串volatile,这样编译器就不会优化分配。

    【讨论】:

    • 这适用于运行时,但 OP 正在寻找编译时检查。
    • 啊,没有意识到这一点。他谈到了只读和可写内存区域,这似乎是一个运行时问题。
    • 这是一种有趣的只读测试方法。它可能在以后的其他事情中有用。
    【解决方案5】:

    这是一个基于 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
    }
    

    【讨论】:

      猜你喜欢
      • 2011-08-05
      • 1970-01-01
      • 1970-01-01
      • 2012-05-26
      • 1970-01-01
      • 1970-01-01
      • 2015-08-29
      • 2011-04-17
      • 1970-01-01
      相关资源
      最近更新 更多