【问题标题】:why a pointer char cause a segmentation fault in a struct environment为什么指针 char 在结构环境中导致分段错误
【发布时间】:2020-09-23 15:31:43
【问题描述】:

我是 C 的初学者,通常我使用 C++。我尝试使用带有 char 数组的 struct,但是当我使用另一个 char *str 时,它会引发 segfault

我的代码:

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

typedef struct s_obj t_obj;

struct s_obj {
    char *str;
};

int main() {

    char *str; // if disable no segmentation fault
    t_obj obj; 
    printf("%lu\n",strlen(obj.str));
    return(0);
}

我试图理解你所说的“strlen 的参数必须是一个字符串”@anastaciu 的意思...所以我尝试编写代码来做到这一点,但结果是一样的:segfault when使用了另一个char *str

我找不到在结构中初始化 char *str 的方法。

typedef struct s_obj t_obj;
struct s_obj {
    char *str;
};

int main() {
    char *str; // if disable no segmentation fault fault
    t_obj obj;
    obj.str = strcpy(obj.str, "truc");
    // printf("%lu\n",strlen(obj.str));
    printf("%s\n",obj.str);
    return(0);
}

【问题讨论】:

  • "... 一个包含 char 数组的结构" - 不,它没有。数组类似于char str[100]。你所拥有的是一个 pointer,你永远不会初始化它并且它不指向任何东西。

标签: c string pointers


【解决方案1】:

线

printf("%lu\n", strlen(obj.str));

调用未定义的行为,strlen 的参数必须是一个字符串,也就是一个空终止的字符数组,obj.str 不是一个字符串它只是一个未初始化的指针,你需要为它分配内存,否则使其指向一个有效的内存位置。

例如:

t_obj obj;
obj.str = calloc(100, sizeof *obj.str); //99 character string, 0 initialized 
                                        //malloc does not "clear" the allocated memory
                                        //if you use it you can't use strlen before strcpy
printf("%zu\n",strlen(obj.str)); //will print 0, the string is empty
obj.str = strcpy(obj.str, "truc");
printf("%zu\n",strlen(obj.str)); //will print 4, the length of the string

Live demo

当您删除 char *str; 时,程序的行为并没有那么糟糕这一事实完全在 undefined behavior 的范围内:

【讨论】:

  • @Knupel 我的回答中有一个错字,在第 2 行,我更正了它并添加了一个在线示例,请注意,在您的回答中,您没有为数组分配足够的内存,您应该更正那个。
【解决方案2】:

C 不是 C++ ;) 似乎您错过了两者的重要区别。

C++ 示例:

#include <string>
struct t_obj {
    std::string str;
};
void foo(){
    t_obj obj; // <-- In C++ this is enough to get a properly initialized instance.
}

在 C++ 中,此代码将为您提供一个正确初始化的对象,其中包含一个(也已初始化的)字符串。

但在 C 中(如您的示例):

typedef  struct t_obj  t_obj;
struct t_obj {
    char *str;
};
void foo(){
    t_obj obj; // <-- Nothing gets initialized here.
}

上面的 C++ 示例中没有初始化。 obj 将只是一块(未初始化的)内存。你必须自己初始化它。

您的第二个样本也有问题:

strcpy 不能那样工作。我们需要将分配的内存块传递给strcpy,它会将数据复制到我们给它的那个地方。

但是当我们传递一个“未初始化的指针”时,strcpy 会尝试将我们的数据写入内存中的某处

我认为问题“whats the difference between C strings and C++ strings?”可能会有所帮助。它解释了一些关于 C 和 C++ 字符串差异的细节。

【讨论】:

    【解决方案3】:

    在任何一种情况下,您都在使用 obj.str 未初始化。

    它持有的地址是不确定的,它指向的内存位置的内容也是不确定的。因此,它不是以空值结尾的,并且将其与strlen() 一起使用(即,与需要字符串参数的函数一起使用)将导致越界访问,这本质上是无效的内存访问,这反过来又会调用未定义的行为。

    供参考,C11,第 7.24 章,字符串处理&lt;string.h&gt;

    [...]除非在本子条款中对特定函数的描述中另有明确说明,否则此类调用中的指针参数仍应具有有效值,[...]

    至少,将指针初始化为空值。

    【讨论】:

      【解决方案4】:

      现在代码可以正常工作了:

      #include <stdio.h>
      #include <stdlib.h>
      #include <string.h>
      
      typedef struct s_obj t_obj;
      struct s_obj {
          char *str;
      };
      
      int main() {
          char *str;
          t_obj obj;
          if (!(obj.str = (char*)malloc(sizeof(char))))
              return (0);
          obj.str = strcpy(obj.str, "truc");
          printf("%s\n",obj.str);
          free(obj.str);
          return(0);
      }
      

      【讨论】:

      • 这是错误的。您只分配一个字符,然后 happen 避免缓冲区溢出。这是一个错误。如果您无法确定要分配的大小,请使用strdup
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2018-10-19
      • 2014-04-02
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-25
      • 2018-02-20
      相关资源
      最近更新 更多