s[-1] = 0的含义是什么?

4
我正在研究来自BSD libc的函数strtok的代码,当我在我的机器上运行时,程序接收到了信号SIGSEGV,并显示错误原因为s [-1] = 0。这里是该代码的链接s[-1] = 0是否正确?
以下是我的代码:
#include <stdio.h>
#include <stdlib.h>
#include "strtok.c"

int main(int argc, char* argv[]) {
    char* str = "xxxx xxxyy fdffd";
    const char* s = " ";

    char* token = strtok(str, s);

    while (token != NULL) {
        printf("%s\n", token);
        token = strtok(NULL, s);
    }

    return 0;
}

2
是的,没错。但是你的调用代码可能有问题。 - Karoly Horvath
2
可能s[-1] = 0是正确的。可能调用它的代码有问题。 - Dietrich Epp
1
http://www.codinghorror.com/blog/2008/03/the-first-rule-of-programming-its-always-your-fault.html - Karoly Horvath
我不知道为什么没有人直接告诉你 - 问题在于你正在向 strtok() 传递一个不可修改的字符串(即字符串字面值)。strtok() 修改了传入的字符串,因此字符串字面值是不行的。 - Michael Burr
6个回答

6
s[-1]

扩展为:

*( s - 1 )

因此,如果结果指向有效的内存,则代码是定义好的。

2
(-1)[s] 也同样有效。疯狂! - Andreas Grapentin
记住C语言并没有真正的数组,只有指针会对你有所帮助。 - Hot Licks
2
@HotLicks C语言确实拥有数组。http://eli.thegreenplace.net/2009/10/21/are-pointers-and-arrays-equivalent-in-c/在数组上执行[-1]是未定义的行为。 - this

3
这是可以的,因为s是一个指针。我们可以从C99标准草案中看到,在第6.5.2.1节"数组下标"中,E1[E2]与(*((E1)+(E2)))相同,如下所示(我强调):

后缀表达式后面跟着方括号[]中的表达式是数组对象的元素的下标指定。子脚本运算符[]的定义是 E1[E2]等同于(*((E1)+(E2)))。由于适用于二进制+运算符的转换规则,如果E1是数组对象(等效于数组对象的初始元素的指针)并且E2是整数,则E1[E2]指定E1的第E2个元素(从零开始计算)。

然而,如果s是一个数组,这段代码就不合法了,因为我们将访问不属于该数组的内存,这将导致未定义行为。

3

s[-1] 是指向 s 指针所指对象之前的对象。

根据 C 语言规定,s[-1] 等价于 *(s-1)。这将:

  • 计算 s-1。结果是一个指向 s 所指对象之前的对象的指针,同样的方式,s+1 是指向 s 之后的对象的指针。
  • 解引用它,生成所指对象的左值。

因此,s[-1] = 0 将 0 赋给 s 所指对象之前的对象。

s[-1] 是合法的代码,如果 s 指向数组中第一个元素之后的元素(从而确保在其之前有一个元素),或者 s 指向数组末尾的下一个元素。(如果 s 指向不在数组中的单个对象的下一个位置,则这也是合法的,这是一种不太常见的使用方法。)


1

由于它上面的几行代码有s++,所以应该没问题,最坏情况下我们处理的是(s+1)-1。


1
无论 s 的运行时值是什么,s[-1] = 0 是否“正确”或“错误”取决于它。 s[-1] = 0 本身并没有什么错误或异常。

0

我有一种直觉,FreeBSD的strtok(3)应该是非常稳定和经过充分测试的。

s是一个char*类型;s[-1]将指向s前面的字符设置为NUL

能否让我们看到您实际调用strtok(3)的代码?问题很可能出在您的设置上。此外,您是否阅读了手册页?

第一次调用strtok()时,应该指定str;后续调用希望从同一字符串中获取更多标记时,应传递空指针。分隔符字符串sep必须每次提供,并且可以在调用之间更改。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接