一段时间前,我不得不使用mmc-utils的开源代码,并找到了以下函数:


int do_erase(int nargs, char **argv)
{
    int dev_fd, ret;
    char *print_str;
    __u8 ext_csd[512], checkup_mask = 0;
    __u32 arg, start, end;

    ...

    if (strcmp(argv[1], "legacy") == 0) {
        arg = 0x00000000;
        print_str = "Legacy Erase";
    } else if (strcmp(argv[1], "discard") == 0) {
        arg = 0x00000003;
        print_str = "Discard";
    } else if (strcmp(argv[1], "secure-erase") == 0) {
        print_str = "Secure Erase";
        checkup_mask = EXT_CSD_SEC_ER_EN;
    }

    ...

    if ((checkup_mask & ext_csd[EXT_CSD_SEC_FEATURE_SUPPORT]) !=
                            checkup_mask) {
        fprintf(stderr, "%s is not supported in %s\n",
            print_str, argv[4]);
        ret = -ENOTSUP;
        goto out;
    }

out:
    printf(" %s %s!\n\n", print_str, ret ? "Failed" : "Succeed");
    close(dev_fd);
    return ret;
}

我不明白的是,在没有为print_str分配任何内存的情况下,这个函数是如何工作的.当然,堆栈中准备的字符串有一个指针,但C语言在运行时没有神奇的编译器堆栈分配--有吗?

编译mmc-utils运行得很好,之前我运行了几次代码,没有出现问题.有没有人能给我解释一下这里使用的神秘巫术?

推荐答案

当您编译代码时,它被转换为一个BLOB,然后存储在磁盘上(可执行文件).要执行它,您需要将其加载到内存中,并开始执行其中包含的机器指令.如果某些数据在编译时是已知的,例如"Discard"个字符序列,则也可以将其嵌入到可执行文件中的指令旁边.在编译过程中,机器代码通常存储在.text节中,而字符串等数据则存储在.data.rodata以及其他相关节中.段随后被插入到适当的段中,这些段最终存储在您的磁盘上,并在稍后加载到内存中.

当您的编译器看到一个字符串文字(如"Discard")时,它会将其理解为"此数据在编译时就已知,请为可执行文件中的字符串+空终止符分配足够的空间,并将此数据存储在此处".同时,当您将这样的字符串文本赋值为右侧值时,它的计算结果是该字符串在内存中的地址.

char* print_str = "Discard";

上面的语句完成这两项任务:在编译期间,它为可执行文件中的字符串分配内存并将其存储在那里,在执行期间,它将字符串的地址分配给变量print_str.您可以使用strings命令显示文件中嵌入的所有可打印字符串(包括程序使用的数据以及由编译器插入/以可执行文件格式呈现的其他数据和元数据).在其他内容中,它还应该显示字符串"disard").

您可以使用gdb来判断程序使用的内存区域以及它们来自哪里.如果您使用gdb运行程序并从gdbshell 中执行info proc mappings命令,它将向您显示内存范围列表.其中一些可能被标记为堆栈或堆,并且不是基于文件的,但另一些将来自可执行文件本身或它使用的任何动态库.您应该看到至少有一个来自您的可执行文件的内存区域没有X权限(EXECUTE),这意味着它不包含机器码(如果没有执行它的权限,它将毫无用处).这样的区域通常包含可读和/或可写数据.

在运行时,C没有神奇的编译器堆栈分配--有吗?

实际上,堆栈是一种非常简单的内存分配方法.除了系统强加的内存限制或堆栈大小限制外,没有什么可以阻止您在任何给定时间(至少在x86PC上)为其分配更多内存.堆栈的唯一缺点是它是当前函数的局部函数,因此值一旦返回就会丢失.这在这里当然是一个大问题,但在其他情况下,它允许例如用户定义的数组或运行时已知的任何其他形式的大小:

size_t array_size = user_input_size(); // The array_size variable is unknown until runtime
char arr[array_size]; // But this is still legal C, just grow the stack space a bit (up to a limit defined by the OS)

C++相关问答推荐

POSIX文件描述符位置

C指针算法在函数参数中的应用

由Go调用E.C.引起的内存快速增长

如何在IF语句中正确使用0.0

如何使用低级C++写出数值

在C23中使用_GENERIC实现带有右值的IS_POINTER(P)?

C中函数类型的前向声明

`#if`条件中是否允许`sizeof`?

对重叠字符串使用MemMove

解决S随机内存分配问题,实现跨进程高效数据共享

C中的FREE函数正在触发断点

为什么函数是按照定义的顺序执行的,而不是按照从avr-c中的int main()调用的顺序执行的?

如何使用libgpio(d)为Raspberry Pi编译C程序?

是否定义了此函数的行为?

For循环不会迭代所有字符串字符吗?(初学者问题)

为什么我在C代码中得到一个不完整的类型?

无法识别C编程语言的语法,如书中所示

如何对现有的双向循环链表进行排序?

';malloc():损坏的顶部大小';分配超过20万整数后

即使客户端不发送数据,也会发生UNIX套接字读取