当字符串文字以换行符\ n结尾时,strdup读取大小为4

当src字符串以\n结尾时,我收到无效的读取错误,当我删除\n时错误消失:

 #include  #include  #include  int main (void) { char *txt = strdup ("this is a not socket terminated message\n"); printf ("%d: %s\n", strlen (txt), txt); free (txt); return 0; } 

valgrind输出:

 ==18929== HEAP SUMMARY: ==18929== in use at exit: 0 bytes in 0 blocks ==18929== total heap usage: 2 allocs, 2 frees, 84 bytes allocated ==18929== ==18929== All heap blocks were freed -- no leaks are possible ==18929== ==18929== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) ==18929== ==18929== 1 errors in context 1 of 1: ==18929== Invalid read of size 4 ==18929== at 0x804847E: main (in /tmp/test) ==18929== Address 0x4204050 is 40 bytes inside a block of size 41 alloc'd ==18929== at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==18929== by 0x8048415: main (in /tmp/test) ==18929== ==18929== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0) 

如何在不牺牲新行字符的情况下解决这个问题?

它不是换行符,也不是printf格式说明符。 你已经发现了strlen()可以说是一个bug,我可以告诉你必须使用gcc。

你的程序代码非常好。 printf格式说明符可能会更好一些,但它不会导致您看到的valgrind错误。 让我们看看那个valgrind错误:

 ==18929== Invalid read of size 4 ==18929== at 0x804847E: main (in /tmp/test) ==18929== Address 0x4204050 is 40 bytes inside a block of size 41 alloc'd ==18929== at 0x402A17C: malloc (in /usr/lib/valgrind/vgpreload_memcheck-x86-linux.so) ==18929== by 0x8048415: main (in /tmp/test) 

“无效读取大小4”是我们必须理解的第一条消息。 这意味着处理器运行一条指令,该指令将从内存中加载4个连续的字节。 下一行表示尝试读取的地址是“地址0x4204050是一个大小为41的块中的40个字节。”

有了这些信息,我们可以搞清楚。 首先,如果用'$'或任何其他字符替换'\n' ,将产生相同的错误。 试试吧。

其次,我们可以看到你的字符串中有40个字符。 添加\0终止字符会将用于表示字符串的总字节数增加到41。

因为我们有消息“地址0x4204050是一个大小为41的块中的40个字节,所以我们现在知道出了什么问题。”

  1. strdup()分配了正确的内存量,41个字节。
  2. strlen()尝试从40开始读取4个字节,这将延伸到不存在的第43个字节。
  3. valgrind发现了这个问题

这是一个glib()错误。 曾几何时,一个名为Tiny C Compiler(TCC)的项目开始起飞。 巧合的是,glib已完全改变,因此正常的字符串函数(如strlen()不再存在)。 它们被优化版本取代,这些版本使用各种方法读取内存,例如一次读取四个字节。 gcc同时被更改以生成对相应实现的调用,具体取决于输入指针的对齐方式,编译的硬件等等。当对GNU环境的这种更改使得生成如此困难时,TCC项目被放弃了。新的C编译器,通过剥夺使用glib作为标准库的能力。

如果您报告错误,glib维护者可能无法修复它。 原因是在实际使用中,这可能永远不会导致实际崩溃。 strlen函数一次读取4个字节,因为它看到地址是4字节对齐的。 在没有segfaulting的情况下,总是可以从4字节对齐的地址读取4个字节,因为从该地址读取1个字节会成功。 因此,来自valgrind的警告并未揭示潜在的崩溃,只是对如何编程的假设不匹配。 我认为valgrind在技术上是正确的,但我认为glib维护者没有机会做任何事情来压制警告。

错误消息似乎表明它是通过strdup分配的malloc ed缓冲区读取的strlen 。 在32位平台上,最佳的strlen实现可以一次读取4个字节到32位寄存器中,并进行一些bit-twiddling以查看其中是否存在空字节。 如果接近字符串的末尾,剩下少于4个字节,但仍然读取4个字节来执行空字节检查,然后我可以看到此错误被打印。 在这种情况下,大概是strlen实现者会知道在特定平台上执行此操作是否“安全”,在这种情况下,valgrind错误是误报。