【问题标题】:Why doesn't strsep() work with pointers to the stack?为什么 strsep() 不适用于指向堆栈的指针?
【发布时间】:2019-08-27 20:22:45
【问题描述】:

使用函数strsep 查找字符串的第一个单词似乎存在指针兼容性问题。到目前为止,我一直认为char *schar s[] 是完全可以互换的。但似乎他们不是。我在堆栈上使用数组的程序失败并显示以下消息:

foo.c: In function ‘main’:
foo.c:9:21: warning: passing argument 1 of ‘strsep’ from incompatible pointer type [-Wincompatible-pointer-types]
  char *sub = strsep(&s2, " ");
                     ^
In file included from foo.c:2:0:
/usr/include/string.h:552:14: note: expected ‘char ** restrict’ but argument is of type ‘char (*)[200]’
 extern char *strsep (char **__restrict __stringp,

我不明白这个问题。使用malloc 的程序有效。

这行得通:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int main(void)
{
    char s1[] = "Hello world\0";
    char *s2 = malloc(strlen(s1)+1);
    strcpy(s2, s1);
    char *sub = strsep(&s2, " ");

    printf("%s\n", sub);

    return 0;
}

这不是:

#include <stdio.h>
#include <string.h>

int main(void)
{
    char s1[] = "Hello world\0";
    char s2[200];
    strcpy(s2, s1);
    char *sub = strsep(&s2, " ");

    printf("%s\n", sub);

    return 0;
}

有什么问题? (对不起strcpy)。为什么函数指针指向堆栈或堆很重要?我明白为什么你不能访问二进制/文本段中的字符串,但是堆栈有什么问题?

【问题讨论】:

  • 我很确定您第二个示例中的 &amp;s2 会返回 char **?打开编译器警告。
  • 您应该只使用s2,或者如果您愿意,可以使用&amp;s2[0]。数组已经转换为指针。
  • @YoYoYonnY 这些将导致char *strsep 的第一个参数是 char **
  • 您需要为strsep 提供一个char **,以便它可以修改它。在第二个示例中,&amp;s2 具有类型char (*)[200],这是完全不同且不兼容的。它需要修改指针,在这种情况下它显然不能这样做。
  • @YoYoYonnY 不正确。第二个示例中的&amp;s2 具有char (*)[200] 类型,它从不char ** 兼容。

标签: c string heap-memory stack-memory strsep


【解决方案1】:

@DavidRankin 关于为什么它不起作用是正确的。但是您仍然可以编写代码,以便它可以使用堆栈上的变量。

要使用数组代替 malloc(),您可以创建另一个指向该数组的指针并将其用作 strsep() 的参数,如 version1() 函数所示。

我知道这可能只是一个示例,但是您提供的带有 malloc() 和 strsep() 的示例可能会导致内存错误,因为 strsep() 将更新指针(它会修改它指向的地址)。因此,您必须保存 malloc() 返回的原始地址才能正确释放该内存。请参阅 version2() 示例。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>


void version1(void)
{
    char s1[] = "Hello world";
    char s2[200];
    char* s3 = s2;
    char *sub;

    strcpy(s3, s1); // or s2 in place of s3

    while ((sub = strsep(&s3, " ")))
    {
        printf("%s\n", sub);
    }
}

void version2(void)
{
    char s1[] = "Hello world";
    char *mymem = malloc(strlen(s1)+1);
    char *s2 = mymem;
    char *sub;

    strcpy(s2, s1);

    while ((sub = strsep(&s2, " ")))
    {
        printf("%s\n", sub);
    }

    free(mymem);
}

int main(int argc, char* argv[])
{
    printf("Version1\n");
    version1();

    printf("\nVersion2\n");
    version2();

    return 0;
}

【讨论】:

    【解决方案2】:
     note: expected ‘char ** restrict’ but argument is of type ‘char (*)[200]’
    

    您的警告会准确地告诉您问题所在。你有两种不同的类型。

    char *s2;        /* declares a character pointer */
    

    同时

    char s2[200];   /* declares an array of char[200] */
    

    当你把地址作为一个指针,结果是一个pointer-to-pointer。当您将地址设为一个数组时,结果是一个指向数组的指针。当您取消引用 pointer-to-pointer 时,结果是 一个指针。当你取消引用 pointer-to-array 时,结果是 一个数组

    strsep 并非旨在将 指向数组的指针 作为参数(这会阻止它根据需要重新分配)

    【讨论】:

    • 但是为什么它们在其他地方完全可以互换呢?您可以使用指针对数据进行索引,就像使用 pt[i] 处理数组一样
    • @TheoFreeman 它们不是完全可以互换的,但它们密切相关。
    • 了解C11 Standard - 6.3.2.1 Other Operands - Lvalues, arrays, and function designators(p3) 解释了如何在访问时将数组转换为指针。虽然这允许具有自动存储持续时间的数组和动态分配的数组在某些情况下可以互换使用,但它不会改变底层类型。问题是当函数需要 pointer-to-pointer 时,例如char *strsep(char **stringp, const char *delim);ssize_t getline(char **lineptr, size_t *n, FILE *stream);
    猜你喜欢
    • 1970-01-01
    • 2010-12-19
    • 2016-11-14
    • 2012-02-05
    • 2020-03-09
    • 1970-01-01
    • 1970-01-01
    • 2012-12-13
    • 2019-11-23
    相关资源
    最近更新 更多