当前位置: 首页 > 知识库问答 >
问题:

编译器是否可以在常量数据中存储函数作用域的非静态常量数组,并避免每次调用初始化?

傅毅然
2023-03-14

在阅读二进制文件(C/C)中如何存储字符数组/字符串时?,我在考虑原始字符串,“Nancy”,以各种方式在生成的二进制文件中完整地显示出来。该帖子的案例是:

int main()
{
    char temp[6] = "Nancy";
    printf("%s", temp);

    return 0;
}

显然,在一般情况下(编译器无法确认temp是否未发生突变),它必须实际初始化堆栈本地数组,以允许将来发生突变;数组本身必须分配空间(在堆栈上,或者可能使用寄存器来实现真正奇怪的体系结构),并且必须在每次调用函数时填充它(假设这不是main,它在C中只调用一次,通常在C中只调用一次),以避免重入问题等。它是将初始化硬编码到程序集中,还是从程序的常量数据部分执行memcpy,都无关紧要;每次调用都必须初始化某些内容。

相比之下,如果char temp[6]=“Nancy” 已替换为以下任一项:

  1. const char*temp=“Nancy”
  2. char*temp=“Nancy”(仅限C;在C中,文本是const char[],但实际上它们在C中也不可变)
  3. static const char temp[6]=“Nancy”
  4. 静态字符温度[6]=“Nancy”

然后,程序不需要为每个调用分配任何基于数组长度的资源(在#1的情况下仅分配一个指针变量)

我的问题是:标准是否为const char temp[6]="Nancy";提供了与静态const char temp[6]="Nancy";行为等价的回旋余地?两者都是不可变的,修改它们是违反规则的。我所知道的唯一区别是:

  1. 如果没有静态,您会期望数组的地址与其他本地地址进行主机定位,而不是在程序内存的其他部分(可能会影响缓存性能)
  2. 如果没有静态,从技术上讲,变量在每次调用中都被创建和销毁

根据标准,我没有看到任何明显违反可观察行为的行为:

  • 除了未定义的行为,例如返回指向temp的指针,在没有保证的情况下,你不能观察数组的存在和停止
  • 您不能合法地计算不相关变量的ptrdiff_t(仅在给定数组内,加上超过该数组末端虚拟元素的变量)

因此,我认为编译器可以在这种情况下安全地“将视为静态的”,就像使用规则一样;没有办法观察差异,所以它可以做任何感觉最好的事情。

在C或C标准要求对const进行某种每次调用初始化,但不要求对静态函数作用域数组进行初始化的情况下,我缺少什么吗?如果C和C标准不一致,我也想知道。

编辑:正如Barmar在常量中指出的,有标准合法的方法来检测特定编译器中的这种行为,例如:

int myfunc() {
    const char temp[6] = "Nancy";
    const char temp2[6] = "Nancy";
    return temp == temp2;  // true if compiler implicitly made them static or combined them, false if not
}

或者:

int otherfunc(const char *s) {
    const char temp[6] = "Nancy";
    return s == temp;
}

int myfunc() {
    const char temp[6] = "Nancy";
    return otherfunc(temp); // true if compiler implicitly made them shared statics, false if not
}

共有1个答案

祁嘉瑞
2023-03-14

试试costexpr关键字:

constexpr auto& compile="Nancy";

甚至可以在C 20中使用compile作为非类型模板参数。

 类似资料:
  • 我有以下一段代码: 我可以想象编译器具有消除函数中的子句所需的所有信息。调用该函数的唯一代码路径来自main,它接受,因此它应该能够确定该代码路径未被使用。但是: 在GCC 12.2中,我们看到第二部分链接到。 如果我函数,这将消失: 我在这里错过了什么?有没有办法让编译器做一些更智能的工作?这发生在带有 C 和 。

  • 是代码片段 如果是,和是编译时常量,还是指针本身就是(无论编译时意味着什么)? 如您所见,我通常对 数组及其在类/结构中的初始化感到有些困惑。请随意回答我的具体问题,还可以提及有关此主题的常见陷阱等。

  • 问题内容: 行给出了编译错误。 为何不允许这样做的任何特定原因?如何使用数组常量初始化String数组? 编辑:谢谢大家的回答。现在,我很清楚什么是允许的,什么是不允许的。但是我能问你 为什么 不允许这样做吗? 仔细搜索一下之后,我发现了这个链接,在其中,被告知像这样的编码使编译器不明确- 宠物应该是String数组还是Objects数组。但是,从声明中可以很好地看出它是一个String数组,对吗

  • 问题:如何从静态类函数返回非常量ref,下面的代码为什么不这样做? 我试图用一个自定义分配器初始化向量,这个分配器本身是通过引用一个自定义内存管理类来初始化的。下面是它的样子: 自定义std::向量分配器已初始化,参考自定义内存管理类 但是,我希望每个模板类型只有一个内存管理器,因此我尝试使用singleton的方法初始化分配器,以便每个模板类型只创建一个内存管理器: 单例类,以确保每组模板参数只

  • 初始化的区别是什么:1)int i=47;或2)int i;{i=47;在哪些情况下我们需要第一个或第二个?

  • 我刚开始和拉威尔一起工作。我需要重写我几年前制作的整个系统,使用Laravel4作为基本框架。在我的旧系统中,我曾经有一个文件,其中声明了一些常量和一个文件,其中包含大量数组集(例如,类别、状态、事件类型、语言等)。通过这样做,我可以使用 在我的应用程序的任何地方。 我的问题是,我如何以所谓的“laravel方式”存储这些信息。我尝试使用某种对象来存储这些信息,将其设置为服务并为其创建门面: Ap