【问题标题】:Initialise struct pointers in a function in C在 C 中的函数中初始化结构指针
【发布时间】:2017-11-08 20:41:13
【问题描述】:

我对 C 语言还很陌生,对于在函数中初始化作为指针的结构变量的正确方法有点困惑。这种风格是否足够,还是我需要在分配 s->str 之前分配内存? 感谢您的回复,如果问题不清楚,我们深表歉意,因为我对这种语言非常陌生。

typedef struct Mystruct{
    const char* str1;
    const char* str2;
}mystruct;

mystruct* mystruct_new(const char* str1, const char* str2){
    mystruct *s = (mystruct*)(malloc(sizeof(mystruct)));
    s->str1 = str1;
    s->str2 = str2;
    return s;
}

【问题讨论】:

  • 如果您想要分配给strdup(str1) 的字符串副本。我们需要看看如何调用这个函数来发出建议。使用字符串文字,您的代码就可以了。
  • 查看this question了解更多关于在c中转换malloc结果的信息
  • 这取决于您的意图。您想要这些字符串的副本还是只想指向已经存在的字符串?
  • @Unlikely1879 s->str1 不会指向mystruct_new(...) 末尾的垃圾。相反,它将指向与您传入的完全相同的字符串。因此,如果您调用mystruct_new(inputStr1, inputStr2);,然后更改 inputStr1,例如inputStr1[0] = 'b',它也会改变你的 mystruct 的 str1。
  • str1 不是函数本地的,所以它不会在函数结束时被销毁,但它的作用域是未知的。例如,如果它的作用域在main 中,它将持续到程序结束。但取决于它到底是什么,也许它会被改变,并且这种改变会影响你的结构,因为s->str1 将指向它而不是拥有它自己的副本。

标签: c pointers struct


【解决方案1】:

你的函数是合法的并且没有做任何坏事。不过,您应该记录它以提及不复制字符串,仅复制指针。

因此,如果传递的数据的寿命比结构本身短,您可能会遇到未定义的行为。示例:

mystruct*func()
{
   char a[]="foo";
   char b[]="bar";

   return mystruct_new(a,b);
}
mystruct*func2()
{
   char *a="foo";
   char *b="bar";

   return mystruct_new(a,b);
}

int main()
{
    mystruct *s = func();
    printf(s->a); // wrong, memory could be trashed
    mystruct *s2 = func2();
    printf(s2->a); // correct
    mystruct *s3 = mystruct_new("foo","bar");
    printf(s3->a); // also correct, string literals have global scope
}

上面的代码对于第一次打印来说是未定义的行为,因为s->a 指向一些不再分配的内存(本地到func)。 第二个打印是可以的,因为s2->a 指向一个具有无限寿命的字符串文字。

所以也许你的函数像这样更有用:

mystruct* mystruct_new(const char* str1, const char* str2){
    mystruct *s = malloc(sizeof(mystruct));
    s->str1 = strdup(str1);
    s->str2 = strdup(str2);
    return s;
}

现在为字符串分配内存。丢弃结构时不要忘记free它,最好在另一个实用程序函数中完成。

【讨论】:

    【解决方案2】:

    如果传递给strstr2 的字符串将始终 是字符串常量,而不是是的,您可以通过这种方式侥幸逃脱。然而,我的猜测是事实并非如此。所以你最好用strdup复制每个字符串并将它们分配给结构成员:

    mystruct* mystruct_new(const char* str1, const char* str2){
        mystruct *s = malloc(sizeof(mystruct));
        s->str1 = strdup(str1);
        s->str2 = strdup(str2);
        return s;
    }
    

    只需确保在释放结构之前释放每个字段。

    【讨论】:

    • 这正是我想做的,很好的解释,谢谢:)
    • 请注意strdup 不是标准库函数,可能并非所有地方都支持。您可能必须改用mallocstrcpy 的组合。
    【解决方案3】:

    这样想:当你为结构分配内存时,你会免费获得指针成员变量。所以本质上,当你这样做时:

    mystruct *s = malloc(sizeof(mystruct)); //don't cast result of malloc.
    

    然后,您可以像对待任何常规 char* 变量一样对待 s->str1,比如说

    char *str1 = NULL;
    

    如果你想让它指向某个东西,那么你必须为指针分配内存。考虑一下:

    mystruct* mystruct_new(const char* str1, const char* str2){
        mystruct *s = malloc(sizeof(mystruct);
    
        char* someString = getMyString(); //gets some arbitrary string
        char* str1 = NULL;//just for demonstration
        int length = strlen(someString) + 1;
    
       //for struct members
        s->str1 = malloc(sizeof(char) * length);
        strcpy(s->str1, someString);
    
       //For regular pointers
        str1 = malloc(sizeof(char) * length);
        strcpy(str1, someString);
    
        return s;
    }
    

    还请注意,如果您只是使用= 运算符而不是分配内存来分配指针,那么它只会将地址复制到原始值。根据上下文,这可能是也可能不是您想要的。通常,如果您知道内存位置将保持在范围内并且您不需要(或不介意)更改原始字符串,那么最好简单地分配它。否则,建议制作副本。

    //Makes a copy of the string
    s->str1 = malloc(sizeof(char) * length);
    strcpy(s->str1, someString);
    
    //copies the address of the original value only!
    s->str1 = someString;
    

    【讨论】:

      【解决方案4】:

      使用 strncpy() 代替 strcpy()。后者会受到缓冲区溢出的影响。

      例如在另一个用户给出的这段代码 sn-p 中,使用 strncpy() 代替 strcpy()

      mystruct* mystruct_new(const char* str1, const char* str2){
          mystruct *s = malloc(sizeof(mystruct);
      
          char* someString = getMyString(); //gets some arbitrary string
          char* str1 = NULL;//just for demonstration
          int length = strlen(someString) + 1;
      
         //for struct members
         s->str1 = malloc(sizeof(char) * length);
         strcpy(s->str1, someString);
      
        //For regular pointers
        str1 = malloc(sizeof(char) * length);
        strcpy(str1, someString);   // replace with strncpy(str1, someString, bufsize);  where bufsize is the maximum number of characters in your string + 1 for the terminator '\0'.  
      
        return s;
      

      }

      【讨论】:

      • 我不会投反对票,因为我看到您的声誉不允许您发表评论,但这种答案最好留给 cmets。如果你想让它成为一个答案,你应该更加充实它。 OP 从未提及strcpy,因此建议使用strncpy 并不能真正让他有所收获。
      • 我同意它应该放在评论部分;但是,由于我还不能发表评论,而且他是 C 的新手,我觉得至少提醒他注意我们在生产代码中遇到的一个常见问题会更好。因此,我在代码片段中添加了关于他可以在何处使用 strncpy() 的注释。我希望这已经足够了。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2018-02-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-13
      相关资源
      最近更新 更多