输入数组直到0的函数

时间:2017-11-01 05:23:36

标签: c arrays pointers dynamic-allocation

我想创建一个程序,在从stdin输入并存储到数组中时,为数组的每个元素动态分配内存。输入0时应停止读数。如果我尝试直接在main()中创建它,看起来像这样:

int *a;
int i = 0;

a = malloc(sizeof(int));

do
{
    scanf("%d", &a[i]);
    a = realloc(a, (i + 2) * sizeof(int)); // enough space for storing another number
    i++;

} while (a[i-1] != 0);

但我不知道如何制作能够做到这一点的功能。这就是我尝试过的,但它每次都会崩溃。

void read(int **a, int *cnt)
{
a = malloc(sizeof(int));
*a = malloc(sizeof(int));
*cnt = 0;

do
{
    scanf("%d", a[*cnt]);
    *a = realloc(*a, (*cnt + 2) * sizeof(int)); // enough space for storing another number
    (*cnt)++;

} while (a[*cnt-1] != 0);

}

3 个答案:

答案 0 :(得分:1)

如何将所有内容放入函数并返回一个;

int *read()
{
    int *a;
    int i = 0;

    a = malloc(sizeof(int));
    if( !a ) return NULL;

    do
    {
        scanf("%d", &a[i]);
        a = realloc(a, (i + 2) * sizeof(int)); // enough space for storing another number
        if( !a ) return NULL;
    i++;

    } while (a[i-1] != 0);

    return a;
}

答案 1 :(得分:1)

假设你以通常的方式调用它:

void read(int **a, int *cnt)
{
a = malloc(sizeof(int)); // This overwrites local a disconnecting it from the main a
*a = malloc(sizeof(int)); // so this will only change the memory pointed by local a and leak memory
...
}

int main()
{
  int *a;
  int cnt = 0;
  read(&a, &cnt);
  ...
}

正在发生的事情是你给函数指针a的地址,然后在函数中你立即用内存分配覆盖它。重要的是,函数中的a和主要的a完全独立的实体。然后,如果您为*a分配内存,则只将其存储在 local a中,而主a仍会指向它发生的任何内容。所以它是未初始化的并导致未定义的行为。

请删除第a = malloc(sizeof(int))行,您的代码也会正确影响主a

您还需要*a使用read中的所有内容,包括scanfwhile。因此,最好使函数处理分配并返回指针,如另一个答案中所建议的那样。

另请注意,您应该检查realloc的返回值,这样您就不会泄漏内存或崩溃,并且在分配指针时应使用sizeof(int*),无论int的大小如何和int*是一样的。它看起来更清晰。

答案 2 :(得分:0)

您可以使用POSIX getline()功能设置您的功能。

模式非常简单。您的函数接收对用于数据的指针(即指向指针的指针)的引用,动态调整大小;以及指向分配给该指针的大小的指针。它将返回读取到数组的元素数。

如果您正在阅读double而不是int s,并且希望从输入读取所有双打直到输入结束(文件末尾,如果从文件重定向,或者直到用户键入非数字并按 Enter ,或直到用户在行的开头按 Ctrl + D ,代码看起来像这样:

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

size_t read_doubles(double **dataptr, size_t *sizeptr, FILE *in)
{
    double *data;      /* A local copy of the pointer */
    size_t  used = 0;  /* Number of doubles in data */
    size_t  size;      /* Number of doubles allocated for data */

    /* Sanity checks against NULL pointers. */
    if (!dataptr || !sizeptr || !in) {
        errno = EINVAL; /* "Invalid parameter" */
        return 0;
    }

    /* If *dataptr is NULL, or *sizeptr is zero,
       there is no memory allocated yet. */
    if (*dataptr != NULL && *sizeptr > 0) {
        data = *dataptr;
        size = *sizeptr;
    } else {
        data = NULL;
        size = 0;
        *dataptr = NULL;
        *sizeptr = 0;
    }

    while (1) {

        /* Ensure there is room in the data. */
        if (used >= size) {
            /* Need to allocate more.
               Note: we have a copy of (data) in (*dataptr),
               and of (size) in (*sizeptr). */

            /* Reallocation policy. This one is simple,
               reallocating in fixed-size chunks, but
               better ones are well known: you probably
               wish to ensure the size is at least some
               sensible minimum (maybe a thousand or so),
               then double the size up to a few million,
               then increase the size in fixed-size chunks
               of a few million, in a real-world application. */
            size = used + 500;

            /* Note: malloc(size) and realloc(NULL, size)
               are equivalent; no need to check for NULL. */
            data = realloc(data, size * sizeof data[0]);
            if (!data) {
                /* Reallocation failed, but the old data
                   pointer in (*dataptr) is still valid,
                   it isn't lost. Return an error. */
                errno = ENOMEM;
                return 0;
            }

            /* Reallocation succeeded; update the originals,
               that are visible to the caller. */
            *dataptr = data;
            *sizeptr = size;
        }

        /* Read one more element, if possible.
           Note: "&(data[used])" and "data+used"
           are completely equivalent expressions.
        */
        if (fscanf(input, " %lf", data + used) != 1)
            break; /* No more input, or not a number. */

        /* Yes, read a new data element. */
        used++;
    }

    /* If we encountered a true read error,
       return an error. */
    if (ferror(input)) {
        errno = EIO;
        return 0;
    }

    /* Not an error; there just weren't more
       data, or the data was not a number.
    */

    /* Normally, programs do not set errno
       except in case of an error. However,
       here, used==0 just means there was no
       data, it does not indicate an error per se.
       For simplicity, because we know no error
       has occurred, we just set errno=0 here,
       rather than check if used==0 and only then
       set errno to zero.
       This also means it is safe to examine errno
       after a call to this function, no matter what
       the return value is. errno will be zero if no
       errors have occurred, and nonzero in error cases.
    */

    errno = 0;
    return used;
}

库中包含<errno.h>以显示errno<string.h>代表strerror()。这些都是标准C。

但是,我在上面使用的error constants EINVALENOMEMEIO仅由POSIXy系统定义,并且可能并非在所有系统中都存在。好的,可以;您可以选择任何小的非零值并使用它们,因为该函数始终设置errno。但是,在这种情况下,您需要检查每个并为每个打印相应的错误消息。就我而言,我使用的所有系统都为我定义了这三个错误代码,我可以使用strerror(errno)将代码转换为标准错误消息(Invalid argumentNot enough spaceInput/output error分别在非本地化程序中。)

使用如上定义的函数非常简单:

int main(void)
{
    double *data = NULL; /* NULL for "Not allocated yet" */
    size_t  size = 0;    /* 0 for "Not allocated yet" */
    size_t  used;

    size_t  i; /* Just a loop variable. */

    used = read_doubles(&data, &size, stdin);
    if (!used) {
        /* No data read. Was it an actual error, or just no data? */
        if (errno)
            fprintf(stderr, "Error reading standard input: %s.\n", strerror(errno));
        else
            fprintf(stderr, "No numbers in standard input!\n");
        return EXIT_FAILURE;
    }

    printf("Read %zu numbers from standard input.\n", used);
    printf("(The dynamically allocated array has room for %zu.)\n", size);

    for (i = 0; i < used; i++)
        printf("    %f\n", data[i]);

    /* Array no longer needed, so we can free it.
       Explicitly NULLing and zeroing them means
       we can safely reuse them later, if we were
       to extend this program. So, it's not necessary
       to clear them this way, but it is a good practice
       considering it makes long-term maintenance easier. */
    free(data);
    data = NULL;
    size = 0;
    used = 0;

    /* This version of the program has nothing more to do. */
    return EXIT_SUCCESS;
}

基本上,在调用之前,您只需将提供地址的指针设置为NULL,并将地址提供给0,以指示尚未动态分配数组。没有必要malloc()一个初始数组; realloc(NULL, size)完全安全,并且完全符合malloc(size)的要求。实际上,我经常编写在其中没有malloc()的代码,并且只使用realloc()

请注意,上述代码段未经测试,因此可能存在拼写错误。 (我确实选择使用double而不是int s和不同的输入结束条件,以确保您不会复制粘贴代码并按原样使用,不要先阅读并理解它。)如果您发现或怀疑您发现了任何内容,请在评论中告诉我,我会检查。

另请注意,上面的代码片段很长,只是因为我尝试编写描述性注释 - 实际上大部分是&#34;代码&#34;在他们是评论。编写描述性注释 - 描述代码的 intent ,而不仅仅是代码实际执行的内容;后者很容易从代码本身读取,但前者是您或其他人以后阅读代码需要知道,检查代码是否健全或错误 - 非常难,甚至在二十多年后,我仍然在努力改善它。

如果您喜欢编写代码,我会热烈建议您立即开始练习写出好的,有意向性的评论,而不是像我一样与它斗争几十年。令人惊讶的是,有多少好的评论,偶尔用一双新眼睛来审查代码的好夜晚,会有所帮助。