【问题标题】:Can't run printf() on a null-terminated string无法在以 null 结尾的字符串上运行 printf()
【发布时间】:2017-09-11 06:53:54
【问题描述】:

当我出于某种原因传递一个以 null 结尾的字符串时,printf() 会引发分段错误。

这是我在 GDB 中的问题的演示

λ sudo gdb -q notesearch 从notesearch读取符号...完成。 (gdb) 休息 53 0x400b32 处的断点 1:文件 notesearch.c,第 53 行。 (gdb) 运行 启动程序:/home/frosty/hack/chapter_2/code/notesearch [调试] 用户 ID:0 [调试] 文件描述符:3 断点 1,print_notes (fd=3, uid=0, searchstring=0x7fff3daf7fc0 "") 在 notesearch.c:53 53 printf("%s\n", note_buffer); (gdb) x/8xb note_buffer 0x7feb5a997168:0x68 0x65 0x6c 0x6c 0x6f 0x0a 0x00 0x00 (gdb) x/s note_buffer 0x7feb5a997168:“你好\n” (gdb) 下一个 程序收到信号 SIGSEGV,分段错误。 _dl_fixup (l=, reloc_arg=) 在 ../elf/dl-runtime.c:148 148 ../elf/dl-runtime.c:没有这样的文件或目录。 (gdb)

这是解决问题的源代码

int print_notes(int fd, int uid, char *searchstring){ int note_length = find_user_note(fd, uid); 如果 (note_length == -1) 返回0; // 文件结束 char* note_buffer; 读取(fd,note_buffer,note_length); note_buffer[note_length] = 0; //空终止字节 如果(搜索笔记(笔记缓冲区,搜索字符串)){ printf("%s\n", note_buffer); } 返回 1; }

【问题讨论】:

  • 你有char* note_buffer;,但从不分配内存。因此,当您使用它时,您将信息放入内存中,但您不知道在哪里。要么先分配内存,要么使用 char 数组。
  • @AntonH 虽然,我对它为什么起作用感到有点困惑。如果您查看我的 gdb 输出,read() 工作正常并读取了预期的字符串。是不是因为它在内存的另一个区域(堆除外)并且 printf 无法读取内存的特定区域?
  • "我对它为什么起作用感到有点困惑" --> 代码违反了规则,使用未初始化的note_buffer 编写。 C 不需要合理的结果 - 欢迎来到 Undefined Behavior (UB)。
  • @TimothySamson 没用。看起来它有效,但实际上并没有。仅仅因为您看到了预期的结果,并不意味着它有效。正如“chux”所说,未定义的行为。
  • 好的。非常感谢你们! :-)

标签: c gdb


【解决方案1】:

空终止符由\0 而非0 表示

note_buffer[note_length] = 0;

应该是

note_buffer[note_length] = '\0';

【讨论】:

  • link证明
  • 可能与 read() 系统调用有关。将此行替换为:read(fd, note_buffer, note_length); 与此 read(fd, &note_buffer, note_length);
  • 另外,我不知道这将如何工作。 &note_buffer 将是一个指向 char 指针的指针,而 read() 只要求一个 char 指针
  • @TimothySamson "read() 只要求一个字符指针" --> read() 需要一个指向有效内存的指针。
【解决方案2】:

请记住,在 C 中,数组的索引从 0 到 (array-1 的长度)

这一行:

char* note_buffer;

声明一个指向字符的初始化指针。 IE。它的值取决于该位置当前内存中的垃圾。诸如访问垃圾“指向”的位置之类的操作将是未定义的行为,并可能导致段错误事件。

强烈建议,在这行之后:

char* note_buffer;

插入以下内容:(包括检查 malloc() 失败:

note_buffer = malloc( note_length+1 );
if( !note_buffer )
{
    perror( "malloc failed" ); 
    exit( EXIT_FAILURE );
}

// implied else, malloc successful

注意:在调用 malloc() 时没有 +1 以下行:

note_buffer[note_length] = 0;

将设置一个字节,该字节超过分配区域的末尾。那将是未定义的行为,并可能导致段错误事件

另外,0 的位模式为 0x000000000000000000000000,而“\0”的位模式为 0x00000000。在这种情况下,隐式转换功能将为您节省开支,但不要依赖该功能,而是正确编码文字。所以该行应该是:

note_buffer[note_length] = '\0';

注意:perror() 输出包含的文本字符串和操作系统认为错误发生的原因到stderr,这是所有错误消息应该输出的地方。

【讨论】:

  • 是的,解决了它。关于 0 和 \0 的位模式差异的注释确实很有帮助。谢谢!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多