c - 理解 C 中的 printf

标签 c linux printf

我试图了解 printf 在 C 中如何在一个简单的情况下工作。我编写了以下程序:

#include "stdio.h"

int main(int argc, char const *argv[])
{
    printf("Test %s\n", argv[1]);
    return 0;
}

在二进制文件上运行 objdump 我注意到 Test %s\n 位于 .rodata

objdump -sj .rodata bin

bin:     file format elf64-x86-64

Contents of section .rodata:
 08e0 01000200 54657374 2025730a 00        ....Test %s..

因此格式化打印似乎执行从 rodata 到其他地方的额外模式复制。

在使用stare ./bin rr 编译并运行它之后,我注意到在实际写入之前有一个brk 系统调用。所以用

运行它
gdb catch syscall brk
gdb catch syscall write

显示在我的例子中,当前中断等于 0x555555756000,但随后设置为 0x555555777000。当 write 出现时格式化字符串

x/s $rsi
0x555555756260: "Test rr\n"

位于“旧”和"new"中断之间。写入发生后,程序退出。

问题: 为什么我们分配了这么多页,为什么在 write 系统调用发生后 break 没有返回到前一个?是否有任何理由使用 brk 而不是 mmap 来进行这种格式化?

最佳答案

brk()(及其配套的 sbrk())是某种专门用于操纵堆大小的 mmap()。由于历史原因,libc 也可以直接使用 mmap()mremap()

随着分配额外的内存,堆会扩展,例如使用 malloc(),这在 libc 内部发生,例如有足够的空间从格式字符串创建实际的字符串和参数或许多其他内部事物(即,将缓冲 io 与 f* 函数系列一起使用时的输出缓冲区)。

如果堆的某些部分不再使用,它​​通常不会自动释放,主要有两个原因:堆可能是碎片化的,和/或未使用的堆没有低于某个阈值,这证明操作是合理的,因为可能很快会再次需要它。

附带说明:格式字符串本身肯定不会从 ro-section 复制到堆中,这将完全没有用。但是结果字符串(通常)是建立在堆上的。

关于c - 理解 C 中的 printf,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54090408/

相关文章:

c++ - 如何编写可以控制设备驱动程序的程序?

linux - 在设置位置参数时使用 - 和 -- 作为要设置的选项之间的区别

c - C 型鞭尾压力表

c - 在 C 中的多列中打印 ASCII 字符

c - 尽管未对其进行任何更改,C 中的全局变量也会发生变化

c++ - CUDA SHA-计算失败

c - printf 没有按预期工作。有人可以解释输出吗?

c - 无法从 C 中的结构中打印字段

c - 在集群的单个节点上运行 OpenMP

c - C 程序如何在 Linux 环境中同时执行其他操作的同时轮询用户输入?