简体   繁体   English

如何在C中正确分配结构

[英]How to correctly malloc a struct in C

Here is my full code, it looks like to work, but it's not working very well. 这是我的完整代码,看起来不错,但是效果不是很好。 I would accept any code, that is working like this. 我会接受任何这样的代码。

Firstly, the code works, but when I want to add the third name to the struct, it crashes. 首先,代码可以工作,但是当我想在结构中添加第三个名称时,它就会崩溃。

Is there any other way to do this? 还有其他方法吗?

I need struct, because in the future, I want to add some other params, like age, average, gender, etc. 我需要结构,因为将来我想添加其他一些参数,例如年龄,平均水平,性别等。

Please, help me out. 请帮帮我。

//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++;
}

I'm using Visual Studio. 我正在使用Visual Studio。

Thanks for help! 感谢帮助!

students *mynew= realloc(s, sizeof(students)* (scount+1));
if( mynew != NULL )
    s=mynew;

Otehrwise you are having a memory leak. 否则,您将发生内存泄漏。 You didn't use the return value of realloc . 您没有使用realloc的返回值。

Don't cast the return type of malloc . 不要转换malloc的返回类型。

As per standard §7.22.2.35 根据标准§7.22.2.35

void *realloc(void *ptr, size_t size)

The realloc function deallocates the old object pointed to by ptr and returns a pointer to a new object that has the size specified by size . realloc函数取消分配ptr指向的旧对象,并返回一个指向具有size指定size的新对象的指针。

It is good not to use the same pointer variable on which you are calling malloc because in case it fails you will lose reference to the old one too (unless it is stored by other means). 最好不要使用与调用malloc相同的指针变量,因为万一失败,您也将丢失对旧变量的引用(除非它通过其他方式存储)。

Also you didn't check the return value of malloc . 您也没有检查malloc的返回值。

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

Also you should check the return value of fgets() . 另外,您应该检查fgets()的返回值。

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

Also trying to compile your code it showed this 还尝试编译您的代码,这表明

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

When compiling try not to ignore any warning messages. 编译时,请不要忽略任何警告消息。 It showed the problem you had. 它显示了您遇到的问题。

Important point: (why scount+1 in realloc ?) 要点:(为什么scount+1realloc ?)

When reallocating the general idea is increase the number of students. 重新分配总体思路时,要增加学生人数。 And for that you need to have extra memory allocated for an student. 为此,您需要为学生分配额外的内存。 That's why the scount+1 in the code.( realloc ). 这就是代码中scount+1的原因。( realloc )。


Some other points: 其他一些要点:

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

You can do it like this also 你也可以这样

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

To understand why from standard §7.21.7.2 从标准§7.21.7.2理解原因

char *fgets(char * restrict s, int n,FILE * restrict stream)

The fgets function reads at most one less than the number of characters specified by n from the stream pointed to by stream into the array pointed to by s . fgets函数从stream指向的stream s指向的数组中读取的字符数最多少于n指定的字符数。 No additional characters are read after a new-line character (which is retained) or after end-of-file. 在换行符(保留)或文件结束之后,不会读取其他字符。 A null character is written immediately after the last character read into the array. 在将最后一个字符读入数组后,立即写入空字符。

\\0 character was there already in the inputted string . 输入的string已经有\\0字符。 You can get the length of it but you know that the one before the \\0 is the \\n character 1 that you entered by pressing the Enter key. 您可以得到它的长度,但是您知道\\0之前的那个是您通过按Enter键输入的\\n字符1 We are overwriting it with the \\0 . 我们用\\0覆盖它。

1. This is the usual case but not the only one . 1.这是通常的情况,但不是唯一的情况。 There are two cases where this might not be the right way to look at the thing. 在两种情况下,这可能不是看待事物的正确方法。

  • The input line has n-1 or more characters before the '\\n' . 输入行在'\\n'之前有n-1或更多字符。 The the one before \\0 will not be the \\n rather it will be some character inputted by the user. \\0之前的那个不是\\n而是用户输入的某些字符。

  • The last line is a stream which may not have a '\\n' . 最后一行是可能没有'\\n' (stdin closed). (stdin已关闭)。 In that case also the input doesn't contain the \\n . 在这种情况下,输入也不包含\\n

So in these cases the idea of removing \\n would fail. 因此,在这些情况下,删除\\n的想法将失败。 Discussed in comment. 在评论中讨论。 (chux) (chux)


A better and safe solution than overwriting this way: 比覆盖这种方式更好,更安全的解决方案:

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

The explanation from the link is that if a \\0 is given as input then we will basically write to s[scount].name[SIZE_MAX] which is not desired. 链接中的解释是,如果输入\\0则基本上将写入s[scount].name[SIZE_MAX]


From the standard §7.24.5.3 根据标准§7.24.5.3

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

The strcspn function computes the length of the maximum initial segment of the string pointed to by s1 which consists entirely of characters not from the string pointed to by s2 . strcspn函数计算s1指向的字符串的最大初始段的长度,该段的最大长度完全由字符而不是 s2指向的字符串组成。

How to correctly malloc a struct in C ? 如何正确地在C中分配结构?

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

How to correctly re-allocate a struct in C ? 如何在C中正确地重新分配结构?

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

p points to some struct . p指向某个struct Notice no coding of that type in above. 注意上面没有该类型的编码


OP' primary problem is not using the return value of realloc() and allocating 1-too-small OP的主要问题是不使用realloc()的返回值并分配1-to-small

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

realloc returns a new pointer that you need to keep: 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;
  }

You are not allocating it back! 您没有分配回来! Take a look at how realloc works. 看一下realloc工作原理。 You need to assign the pointer back after making the re-allocation like this. 像这样进行重新分配后,需要将指针分配回去。

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;
    }
}

By Definition, realloc returns a void pointer but you aren't collecting it. 根据定义,realloc返回一个空指针,但您没有收集它。

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

realloc returns a NULL if there's not enough space. 如果没有足够的空间,则realloc返回NULL So you can re-assign it when you are sure that it is not NULL 因此,当您确定它不是NULL时,可以重新分配它

Just make a small change above and your code works like a charm! 只需在上面做些小改动,您的代码就可以发挥出魅力!

Cheers! 干杯!

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM