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

C 缓冲区上的basename进入分段错误,c,pointers,strcmp,C,Pointers,Strcmp,我现在正在调整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 a

我现在正在调整
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
作为例外完全起作用;第二个问题让我困惑:为什么缓冲区会出现分段错误?我试图用不同的方式编写缓冲区,但结果是一样的

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

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

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

  • 操作系统:Ubuntu 16.4(在Windows 10上虚拟化64位)
  • CPU(我不认为有用):Intel®Core™ i5-3230M CPU@2.60GHz×2
根据

漏洞 在这些函数的POSIX版本的glibc实现中,它们修改参数,并在使用静态字符串(如
“/usr/”
)调用时执行segfault。[……]

基本上

 basename("../src/test/resources/constraints_0020_000")
调用,因为这是修改字符串文字的尝试


注意:如手册页所述,需要更改单词。读起来像

在这些函数的POSIX版本的glibc实现中,它们修改参数,当使用静态字符串调用时,调用未定义的行为,如
“/usr/”
。[……]

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

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

[…]如果程序试图修改这样的数组,则该行为为 未定义


编辑:

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

  #include <libgen.h>
#包括
添加以获得可用的函数
basename()
的正向声明。

根据

漏洞 在这些函数的POSIX版本的glibc实现中,它们修改参数,并在使用静态字符串(如
“/usr/”
)调用时执行segfault。[……]

基本上

 basename("../src/test/resources/constraints_0020_000")
调用,因为这是修改字符串文字的尝试


注意:如手册页所述,需要更改单词。读起来像

在这些函数的POSIX版本的glibc实现中,它们修改参数,当使用静态字符串调用时,调用未定义的行为,如
“/usr/”
。[……]

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

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

[…]如果程序试图修改这样的数组,则该行为为 未定义


编辑:

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

  #include <libgen.h>
#包括
添加以获得函数的前向声明
basename()
可用。

根据:

basename()
函数可以修改路径所指向的字符串, 并可能返回指向内部存储器的指针。返回的指针 可能会无效,或者存储可能会被 随后调用
basename()
。返回的指针也可能是 如果调用线程终止,则无效

Per:

dirname()basename()都可以修改
路径
,因此在调用 这些功能

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

Per:

basename()
函数可以修改路径所指向的字符串, 并可能返回指向内部存储器的指针。返回的指针 可能会无效,或者存储可能会被 随后调用
basename()
。返回的指针也可能是 如果调用线程终止,则无效

Per:

dirname()basename()都可以修改
路径
,因此在调用 这些功能


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

因此,您可以将代码修剪为仅失败的情况吗?是!缓冲区和basename所在的所有行都处于分段错误中。我把所有测试的代码都放进去,只是为了得到所有的场景。如果您用“buffer”和“basename”注释这些行,一切正常。只要你去掉其中一个(不管是哪一个),分割错误就会发生。我对
man basename
非常困惑:“dirname()和basename()都可能修改path的内容,因此在调用其中一个函数时可能需要传递一个副本。”。因此,您首先不应该向它传递字符串文字。@EugeneSh。好的,但测试结果正好相反:如果我使用常量字符串,它就可以工作;如果我使用堆指针,它也可以工作;但是如果我使用堆缓冲区,它就不再工作了。为什么?我读了那份文件。。。调试后,basename返回表示basename本身的第一个字符的地址。如下所述-您不能对未定义的行为进行推理。一旦你有了一个,任何事情都可能发生。所以你可以把你的代码裁剪到失败的情况