缓冲区上的basename进入分段错误

时间:2016-12-28 17:38:21

标签: c pointers strcmp

我正在调整class MyDomain { String test static constraints = { test blank: false, nullable: false id blank: false, validator: {value, command -> if (Environment.current == Environment.TEST) { //do validation for not allowing more than 100 records if (MyDomain.count() > 100) return ['too.many.records'] } } } ,我遇到一个非常奇怪的案例(至少对我而言)。这是代码:

basename

第一个char buffer[300]; char* p; strcpy(buffer, "../src/test/resources/constraints_0020_000"); printf("%d\n", strcmp(basename("../src/test/resources/constraints_0020_000"), "constraints_0020_000")); //works as expected printf("assert testBasename02"); printf("%d\n", strcmp(basename(buffer), "constraints_0020_000") == 0); printf("done 1\n"); //goes in segmentation fault printf("%d\n", strcmp(basename(&buffer), "constraints_0020_000") == 0); printf("done 2\n"); //goes in segmentation fault printf("%d\n", strcmp(basename(&buffer[0]), "constraints_0020_000") == 0); printf("done 3\n"); //goes in segmentation fault p = malloc(strlen("../src/test/resources/constraints_0020_000") +1); strcpy(p, "../src/test/resources/constraints_0020_000"); printf("%d\n", strcmp(basename(p), "constraints_0020_000") == 0); //works as expected free(p); printf("all done\n"); 完全是例外;这是第二个令我困惑的问题:为什么缓冲区会出现分段错误?我尝试以不同的方式对缓冲区进行编码,但结果是一样的。

我当然可以忍受这种行为,但是......我真的不明白strcmp如果我给他一个basename或一个缓冲区(最后是也是const char*)。

是否有解释此行为的文件?只有我吗?我试图寻找解释,但我找不到任何解释。

这是我的电脑规格(如果你需要):

  • OS系统:Ubuntu 16.4(在Windows 10 64位上虚拟化64位);
  • CPU(我认为不是很有用):英特尔®酷睿™i5-3230M CPU @ 2.60GHz×2;

2 个答案:

答案 0 :(得分:4)

根据man page

  

错误

     

在这些函数的POSIX版本的glibc实现中,它们修改了它们的参数,并在使用类似"/usr/"的静态字符串调用时发生了段错误。 [...]

基本上,

 basename("../src/test/resources/constraints_0020_000")

调用调用undefined behavior,因为这是尝试修改字符串文字。

注意:如手册页中所述,需要更改单词。读它就好了,

  

在这些函数的POSIX版本的glibc实现中,它们修改了它们的参数,并且当使用类似"/usr/"的静态字符串调用时,调用未定义的行为。 [...]

分段错误是UB的副作用之一,但不是唯一的副作用。

FWIW,尝试修改字符串文字本身会调用UB。引用C11,章节§6.4.5,字符串文字

  

[...]如果程序试图修改这样的数组,行为是   未定义。

编辑:

正如后续评论中所讨论的,另一个问题是缺少头文件。你需要

  #include <libgen.h>

添加,以便获得函数basename()的前向声明。

答案 1 :(得分:0)

https://docs.mongodb.com/manual/reference/operator/update/setOnInsert/#op._S_setOnInsert

  

basename()函数可以修改path指向的字符串,   并且可以返回指向内部存储的指针。返回的指针   可能无效或存储可能被a覆盖   随后致电basename()。返回的指针也可能是   如果调用线程被终止,则无效。

the POSIX standard

  

dirname() basename()都可以修改内容   path,因此可能需要在调用其中一个时传递副本   这些功能。

您使用静态字符串调用basename(),这可能是只读的,因此当basename()尝试修改字符串时会导致SEGV。