【问题标题】:How to correctly malloc a struct in C如何在 C 中正确 malloc 结构
【发布时间】:2018-04-27 15:23:37
【问题描述】:

这是我的完整代码,它看起来可以工作,但效果不是很好。 我会接受任何像这样工作的代码。

首先,代码可以工作,但是当我想将第三个名称添加到结构时,它会崩溃。

还有其他方法吗?

我需要结构,因为将来我想添加一些其他参数,比如年龄、平均、性别等。

请帮帮我。

//The student table
typedef struct students {
    char name[50];
} students;

//Global params
int scount = 0;
students *s;

//Basic functions
void addNewStudent();

int main()
{
    int loop = 1;
    char in;
    int ch;
    printf("Willkommen.\n Wahlen Sie bitte von die folgenden Optionen:\n");
    while (loop)
    {
        printf("\t[1] Neue Student eingeben\n");
        printf("\t[9] Programm beenden\n");

        scanf(" %c", &in);
        while ((ch = getchar()) != '\n');
        switch (in)
        {
        case '1':
            addNewStudent();
            break;
        case '9':
            loop = 0;
            break;
        default: printf("------\nOption nicht gefunden.\n------\n");
            break;
        }
    }
    free(s);
    return 0;
}

void addNewStudent()
{
    int index = 0;
    if (scount == 0)
    {
        s = (students*)malloc(sizeof(students));
    }
    else
    {
        realloc(s, sizeof(students) * scount);
    }

    printf("Geben Sie Bitte die Name:\n");
    fgets(s[scount].name, sizeof(s[scount].name), stdin);

    while (s[scount].name[index] != '\n')
    {
        index++;
    }
    s[scount].name[index] = '\0';
    scount++;
}

我正在使用 Visual Studio。

感谢您的帮助!

【问题讨论】:

  • 建议将结构名称更改为单数,即学生或学生
  • Don't cast malloc: s = malloc(sizeof(students));
  • @lurker: 更好的是s = malloc(sizeof *s); - 防止更改指向的类型。
  • 没有 stdlib.h 包含,因此如果您通过恐龙 C90 编译器运行此代码,确实可能会崩溃和烧毁。
  • realloc(s, sizeof(students) * scount); --> s = realloc(s, sizeof *s * (scount + 1)); if (s== NULL) OutOfMemory();

标签: c pointers struct malloc realloc


【解决方案1】:
students *mynew= realloc(s, sizeof(students)* (scount+1));
if( mynew != NULL )
    s=mynew;

另外,你有内存泄漏。你没有使用realloc的返回值。

不要转换malloc的返回类型。

按照标准§7.22.2.35

void *realloc(void *ptr, size_t size)

realloc 函数释放ptr 指向的旧对象,并且 返回一个指向具有size 指定大小的新对象的指针。

最好不要使用与您调用malloc 相同的指针变量,因为如果它失败,您也会失去对旧变量的引用(除非它以其他方式存储)。

另外你没有检查malloc的返回值。

s = malloc(sizeof(students));
if( s == NULL ){
   frpntf(stderr,"%s","Memory allocation failed");
   exit(1);
}

您还应该检查fgets() 的返回值。

if( fgets(s[scount].name, sizeof(s[scount].name), stdin) == NULL){
     fprintf(stderr,"%s","Error in input");
     exit(1);
}

还试图编译它显示的代码

warning: ignoring return value of ‘realloc’, declared with attribute warn_unused_result [-Wunused-result]
         realloc(s, sizeof(students) * scount);
         ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

编译时尽量不要忽略任何警告消息。它显示了您遇到的问题。

重点:(为什么scount+1realloc中?)

重新分配的总体思路是增加学生人数。为此,您需要为学生分配额外的内存。这就是代码中scount+1 的原因。(realloc)。


其他几点:

while (s[scount].name[index] != '\n')
{
    index++;
}
s[scount].name[index] = '\0';

你也可以这样做

size_t len = strlen(s[scount].name);
if(len){ 
   s[scount].name[len-1]='\0'; 
}

从标准 §7.21.7.2

了解原因
char *fgets(char * restrict s, int n,FILE * restrict stream)

fgets 函数最多读取比 由n 指定的字符从stream 指向的流到 s 指向的数组。之后没有额外的字符被读取 换行符(保留)或文件结尾之后。 一个空值 在读入最后一个字符后立即写入字符 数组。

\0 字符已经存在于输入的string 中。你可以得到它的长度,但是你知道\0之前的那个是\n字符1,你通过按Enter 键。我们用\0 覆盖它。

1.这是常见的情况,但不是唯一的。在两种情况下,这可能不是看待事物的正确方式。

  • 输入行在'\n' 之前有n-1 或更多字符。 \0 之前的不是\n,而是用户输入的某个字符。

  • 最后一行是一个流,它可能没有'\n'。 (标准输入关闭)。在这种情况下,输入也不包含\n

所以在这些情况下,删除\n 的想法会失败。在评论中讨论。 (chux)


比这种方式覆盖更好、更安全的解决方案:

s[scount].name[strcspn(s[scount].name, "\n")] = '\0';

link 的解释是,如果将\0 作为输入,那么我们基本上会写信给s[scount].name[SIZE_MAX],这是不希望的。


从标准 §7.24.5.3

size_t strcspn(const char *s1, const char *s2)

strcspn 函数计算最大初始值的长度 s1 指向的字符串的片段,它完全由 字符不是来自s2指向的字符串。

【讨论】:

  • "但是你知道 \0 之前的那个是 \n 字符" - 这是通常的情况。当 1)(病态情况)输入行在 '\n' 之前有 n-1 个或更多字符时,情况并非如此。 2)最后一行是一个流,可能没有 '\n'。 (stdin 关闭)。
  • 非常感谢!太棒了,它正在工作。现在我真正更好地理解了指针的工作原理。谢谢。
  • @chux, ... 或者当用户在输入正确的字符后按下 Ctrl-D 时。您将收到一个非 nl 终止的字符串(无论大小如何),这不是 EOF 正常情况。
【解决方案2】:

您没有将其分配回来!看看realloc 是如何工作的。像这样进行重新分配后,您需要重新分配指针。

if (scount == 0)
{
    s = (students*)malloc(sizeof(students));
}
else
{
    students *temp = realloc(s, sizeof(students) * (scount+1));
    if(temp == NULL){
        free(s);
    }
    else{
        s = temp;
    }
}

根据定义,realloc 返回一个 void 指针,但您没有收集它。

void *realloc(void *ptr, size_t size);

如果没有足够的空间,realloc 会返回一个NULL。所以当你确定不是NULL时可以重新赋值

只需在上面做一个小改动,您的代码就会像魅力一样工作!

干杯!

【讨论】:

  • 但是不要那样做——当你测试s以确定realloc()是否失败时,你已经泄露了之前的值!
  • @TobySpeight,我的错。只专注于给出答案而不是检查极端情况。无论如何,谢谢。
【解决方案3】:

如何在 C 中正确地 malloc 结构?

p = malloc(sizeof *p);
if (p == NULL) Handle_OutOfMemory();

如何在 C 中正确地重新分配结构?

void *t = realloc(p, sizeof *p * number_of_elements);
if (t == NULL && number_of_elements > 0) {
  Handle_OutOfMemory();
} else {
  p = t;
}

p 指向一些struct。请注意上面的没有该类型的编码


OP' 的主要问题是没有使用realloc() 的返回值并分配 1-too-small

// realloc(s, sizeof(students) * scount);
s = realloc(s, sizeof *s * (scount+1));  // or use above code with check for out-of-memory.

【讨论】:

  • "Handle_OutOfMemory",你的 Camel_CaSe 组合很有趣^^
  • @Stargateur 这是一个“MajorIdentifier_MinorIndentier”模型。
【解决方案4】:

realloc 返回一个您需要保留的新指针:

  students* snew = realloc(s, sizeof(students) * (scount + 1));
  if (!snew) {
     free(s); // If there is not enough memory, the old memory block is not freed
     // handle out of memory
  } else { 
     s = snew;
  }

【讨论】:

    猜你喜欢
    • 2013-11-05
    • 1970-01-01
    • 2021-06-28
    • 1970-01-01
    • 2011-01-11
    • 2020-08-26
    • 1970-01-01
    相关资源
    最近更新 更多