为什么 const char *arr 的大小比 const char arr[] 大?

3

在此输入图像描述我正在使用Microchip PIC32微控制器的xc32编译器。

程序存储器大小(用于启动、向量等)为1656字节。

#include <plib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#include <string.h>

//if I use const char array[] for string literal then the size of the program memory increased 
//equal to the string length(only 12 bytes)

//const char array[] = "Hello World"; //program mem size(1668 0x684) bytes

//but in this case size increased 28 bytes
//const char *array = "Hello World";  //program mem size(1684 0x694) bytes



int main {

    while(1);
}

为了优化目的:

哪一个更好(速度和大小都考虑)?

  1. #define STRLIT "HELLO WORLD"
  2. const char *strlit = "HELLO WORLD";
  3. const char arr[] = "HELLO WORLD"

4
尽管你可能并不熟悉如何进行手动优化,但建议你尝试使用编译器的“-Osize”选项(除非你确切知道自己在做什么),以进行代码的优化。 - TSG
6
回答你的第一个问题,const char *array 需要分别存储一个指针(8字节,为了对齐分配了16字节的空间)和字符串。第一个版本(数组)只需要存储字符串。这是由于它们声明的方式不同。const char array[] = "..." 意味着字符串需要被存储。const char *array 意味着字符串需要被存储,需要有一个全局指针,并且该指针需要指向字符串。 - TSG
1
回答你的第二个问题,这要看情况。试着理解宏的作用(提示:预处理器会将所有宏的出现替换为字符串)。要正确理解每个选项对程序大小和速度的影响,你需要更好地了解它们的实现方式。目前,我鼓励你忽略这些优化,而是学习编写良好的代码。一旦你更熟悉这门语言,你就会更好地理解大小/性能方面的影响。 - TSG
这三个代码即使进行基本优化,也会编译成相同的东西。 - Shambhav Gautam
2
PIC32是冯·诺依曼还是哈佛结构?这似乎非常相关。根据 https://blog.flyingpic24.com/2008/10/26/pic32-harvard-or-von-neumann/ 的说法,我们应该能够像冯·诺依曼那样编程(也就是,在ROM中存储或访问数据时不需要特殊技巧)。 - Lundin
显示剩余6条评论
2个回答

2
通常, MCU 上的存储器是这样存储的:微控制器不同存储类型中存储的内容是什么? 这意味着:
  • 字符串字面量 "HELLO WORLD" 本身很可能存储在 .rodata 闪存 ROM 部分中。

  • 当您在文件范围内执行 const char array[] = "Hello World"; 时,array 变量应该被分配在 .rodata 中。因此,不需要分配字符串字面量,否则会导致冗余分配。

  • 当您执行 const char *array 时,您声明了一个指针,它没有分配在闪存中,而是分配在 RAM 的 .data 部分中。因此,这将意味着 RAM 和闪存的消耗。请注意,此指针是可读/写的 - 您可以将其分配到其他位置,但不能更改指向的内容。

    要将指针分配到闪存 ROM 中,需要执行 const char* const array

至于为什么会得到“28个字节”的某些东西,我不知道。我没有使用过这个部分,但这里对它进行了简要研究,声称它可能具有 Von Neumann 特性,从 C 程序员的角度来看,尽管核心是 Harvard。不确定这意味着什么,但这很重要,因为 Harvard 架构可能需要执行一些支持开销代码来访问闪存 ROM 数据,这反过来会导致稍微更大的可执行文件大小。如果您想了解详细信息,则需要反汇编。

1
你有三个选项,它们的区别如下:
  • #define STRLIT "HELLO WORLD"定义了一个预处理器标记:在可执行文件中不会生成任何代码或数据,但是代码中每个STRLIT实例在编译时将被替换为字符串字面值,这可能会在生成更多数据或不生成数据。
  • const char arr[] = "HELLO WORLD"定义了一个大小为12个字节的char类型的已命名数组, 并初始化并以空字符结尾。它占用12个字节的静态只读数据段,这可能会根据可执行文件格式中的对齐约束增加12个字节或更多。
  • const char *strlit = "HELLO WORLD";同时定义了一个字符串字面值(一个大小为12个字节的常量数组)和一个可修改的指针strlit,指向该字符串字面值。这至少应在可执行文件中使用4个字节(32位目标系统上指针的大小),但根据可执行文件格式中的对齐约束,它的大小可能会再次增加超过4个字节。
  • 请注意,根据编译器选项,可执行文件中可能存在调试数据。

    1
    宏不会存储在可执行数据中,如果您在源代码中多次使用STRLIT,则取决于使用它们的位置以及编译器设置和/或优化功能,该字符串可能会在可执行文件中重复或不重复。大多数编译器共享相同的字符串字面值,甚至是其他字符串字面值的后缀,无论是来自同一源文件还是链接时。这真的取决于您的系统,C标准允许多种解决方案。 - chqrlie

    网页内容由stack overflow 提供, 点击上面的
    可以查看英文原文,
    原文链接