【问题标题】:C - Enforcing string parameter to be in read-only memoryC - 强制字符串参数在只读内存中
【发布时间】:2018-09-13 18:37:13
【问题描述】:

我正在优化一些代码,我有一个这样的函数:

const char * gStrPtr = NULL;

void foo (const char *str) {
     gStrPtr = strdup(str);
}

截至目前,foo() 仅使用常量字符串调用。例如:

const char fooStr[]="Some really long string...";
foo(fooStr);

请注意,因为它总是用常量调用,所以我应该可以这样做:

void foo (const char *str) {
     gStrPtr=str;
}

但是,它打开了一根锋利的棍子:如果将来有人违反约定,并尝试使用稍后释放的字符串的动态副本调用foo(),则可能会导致未定义的行为。

我想知道是否有可能创建一个编译时甚至是运行时检查来检查str 是否在只读内存中,以避免以后出现昂贵的错误。

注意:如果我假设 str 是一个字符串文字,那么我可以使用宏来做到这一点:

#define foo(str)  foo_func("" str)

这将导致非字符串文字的编译错误。但它也不接受指向 const 字符的指针。


编辑

我想我会在下面的讨论之后发布这个。 @CraigEtsy 指出了__builtin_constant_p 的使用,这是解决此问题的最佳方法(但可能足以满足我的需求)。我对此进行了以下测试,并得到了这些结果:

void foo(const char *str) {
        if (__builtin_constant_p(*str))
                printf("%s is constant\n", str);
        else
                printf("%s is not constant\n", str);
}

const char globalArray[] = "globalArray";
const char *globalPtr = "globalPtr";

int main()
{
    const char localArray[]="localArray";
    const char *localPtr="localPtr";
    char localNonConst[]="localNonConst";
    foo("literal");     // constant
    foo(localArray);    // not constant
    foo(localPtr);      // constant
    foo(globalArray);   // constant
    foo(globalPtr);     // not constant
    foo(localNonConst); // not constant
}

当使用 -O3 编译时,它给出了结果:

literal is constant
localArray is not constant
localPtr is constant
globalArray is constant
globalPtr is not constant
localNonConst is not constant

因此,对于我的特殊情况,我可以将const char arr[]="str" 切换到const char * arr="str",然后,在我的foo() 中,我可以检查该值是否为常量,并分配内存并提高运行时如果不是,则发出警告(并标记一个标志,以便我知道以后是否释放指针......)。

【问题讨论】:

  • 问题不是只读内存而是静态存储(例如我可以用mmap动态分配只读内存并稍后释放)。我不认为你可以检查。
  • 不,你不能那样做。
  • 我想我想检查指针是否位于 .text 部分...
  • 如果你void myfunc() { const char mystr[] = "abc"; foo(mystr); } 然后使用gStrPtr 怎么办? mystr 是否在只读内存中?什么是只读存储器?
  • 您可以使用 [with gcc]、__builtin_constant_p 函数和一个在函数返回 false 时执行 strdup 的宏。如果你给它"abc",它返回true,否则返回false。然后,您可以对.rodata 部分进行范围检查并直接设置是否在其中,但如果不是,则执行strdup。也许太乱了,但我想我会把它传递下去。

标签: c gcc


【解决方案1】:

我认为没有任何合理的方法可以在运行时强制执行此操作,至少在没有机器的情况下不会比仅调用 strdup 贵许多数量级。

如果函数只应该将 immutable 字符串作为参数(这就是您要寻找的词 - 不可变,因为它的生命周期将是进程生命周期的剩余时间,并且它的内容在其剩余的生命周期内不会改变),这需要成为其接口契约的文档部分。

【讨论】:

  • 谢谢。我得出了同样的结论,但我想我会问,以防有人有任何我忽略的聪明技巧。不幸的是,我不相信未来的编码员会阅读 cmets。我可能会强制大家使用文字而不是 const char 数组,然后使用宏技巧...
  • 您可以将您的函数定义为宏,并让宏在将其参数传递给函数之前对其进行字符串化。那么,如果不绕过宏,就无法传递非字符串文字。 ;-)
  • 根据平台,检查指针是否在只读数据段内比动态分配便宜很多
  • @M.M:也许,但这是一个很大的假设,并且有很多警告,除非您处于简单的静态链接案例中。使用动态链接和加载,您需要在每次调用时探测新共享库的函数,这将比分配更昂贵。此外,如果库在运行时加载并且可能通过dlclose 卸载,共享库中的只读内存是否算作不可变(这是 OP 真正需要的)?我会说不,但这里的重点是定义甚至不清楚。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-09-13
  • 1970-01-01
  • 1970-01-01
  • 2021-11-06
  • 1970-01-01
  • 2017-06-20
  • 2011-07-22
相关资源
最近更新 更多