C:在增长的数组中存储令牌

时间:2015-08-20 16:32:11

标签: c arrays memory-management dynamic malloc

我正在学习C并希望编写一个程序,该程序读取一行数字(来自文件),用空格分隔(例如:43 2 6 120 5 23),然后将这些数字存储在动态数组中。 到目前为止,我的想法是将该行作为字符串读取,将其分隔为标记(strtok),然后将这些标记转换为int数字(atoi)并存储它们。 我的问题是我必须使用 malloc 分配内存,但无法告诉程序手动需要多少内存,因为它必须能够处理任何长度和任意数量的行。大小,我不知道该怎么做。我应该使用 realloc

我不希望任何人为我做我的工作,只是一个简单的例子和​​/或一个方法来做我想要的方式真的很有帮助。如果需要更多信息,我会把它给你。 我知道这里有关于这个主题的一些问题,我已经看了它们,我很难理解一些事情,而没有解释它们,因为我对C编程完全不熟悉。 当然,如果有人能给我一个可以帮助我的链接,那也会很棒。

4 个答案:

答案 0 :(得分:1)

这是一个非常标准的动态内存使用应用程序。你将要做的是为初始的整数数量声明内存,读取直到你达到那个数字,重新分配你的数组两倍大,然后继续,直到你读完所有数据。

将整行数据读入缓冲区然后解析缓冲区以获得所需内容通常是最佳实践,在这种情况下,fscanf是为了处理单个整数值的读取和转换而量身定做的(它将自动消耗分隔值的空格。)

(下面使用calloc将所有元素初始化为0,在此示例中malloc很好,但初始化可防止在更复杂的情况下无意中读取未初始化元素)< / p>

这是一个简短的例子:

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

#define MAXI 64

int main (int argc, char **argv) {

    int *array = NULL;
    size_t idx = 0, max_idx = 0;
    size_t arraysize = MAXI;
    FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;

    if (!fp) {
        fprintf (stderr, "error: file open failed. '%s'\n", argc > 1 ? argv[1] : "stdin");
        return 1;
    }

    /* allocate initial array */
    array = calloc (MAXI, sizeof *array);

    /* read values from file */
    while (fscanf (fp, "%d", &array[idx]) == 1) {
        idx++;

        /* realloc if necessary */
        if (idx == arraysize) {
            int *tmp = realloc (array, arraysize * sizeof *array * 2);
            if (!tmp) {
                fprintf (stderr, "error: realloc - virtual memory exhausted.\n");
                return 1;
            }
            array = tmp;
            memset (array + arraysize, 0, arraysize);  /* zero new memory */
            arraysize *= 2;
        }
    }

    /* close file */
    if (fp != stdin) fclose (fp);

    max_idx = idx;

    /* print array */
    for (idx = 0; idx < max_idx; idx++)
        printf (" array[%3zu] : %d\n", idx, array[idx]);

    free (array);   /* free memory */

    return 0;
}

示例数据

$ cat dat/100intspace.txt
27086 29317 32736 3356 12059 13921 9388 25672 19828 25390 -1190 25857 ...

<强>输出

$ ./bin/array_dyn_read_int dat/100intspace.txt
 array[  0] : 27086
 array[  1] : 29317
 array[  2] : 32736
 array[  3] : 3356
 array[  4] : 12059
 array[  5] : 13921
 array[  6] : 9388
 array[  7] : 25672
 array[  8] : 19828
 array[  9] : 25390
 array[ 10] : -1190
 array[ 11] : 25857
 ...

验证您的记忆使用

无论何时动态分配内存,您都有责任(1)跟踪您分配的内容; (2)保留一个指向起始地址的指针(这样你以后可以释放它); (3)在不再需要时释放内存。 valgrind或类似的内存错误检查器很容易使用,应该用于验证你的内存使用:

$ valgrind ./bin/array_dyn_read_int dat/100intspace.txt
==7348== Memcheck, a memory error detector
==7348== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==7348== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==7348== Command: ./bin/array_dyn_read_int dat/100intspace.txt
==7348==
 array[  0] : 27086
 array[  1] : 29317
 array[  2] : 32736
 array[  3] : 3356
 array[  4] : 12059
 array[  5] : 13921
 array[  6] : 9388
 array[  7] : 25672
 array[  8] : 19828
 array[  9] : 25390
 array[ 10] : -1190
 array[ 11] : 25857
 ....
==7348==
==7348== HEAP SUMMARY:
==7348==     in use at exit: 0 bytes in 0 blocks
==7348==   total heap usage: 3 allocs, 3 frees, 1,336 bytes allocated
==7348==
==7348== All heap blocks were freed -- no leaks are possible
==7348==
==7348== For counts of detected and suppressed errors, rerun with: -v
==7348== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

如果您有任何问题,请与我们联系。

答案 1 :(得分:0)

如果您不介意连续的空格,那么您可以简单地计算它们的数量并添加一个:

#include <stdio.h>

int main(void)
{
    char line[] = "43 2 6 120 5 23";

    size_t numCount = 1;
    char *ptr = line;
    while (*ptr != '\0') {
        if (*ptr == ' ') {
            numCount++;
        }
        ptr++;
    }

    // malloc call, etc.

    return 0;
}

答案 2 :(得分:0)

读取一行后,代码需要扫描两次,一次计算数字(以便分配正确的内存量),再次转换数字并将它们存储在数组中。 / p>

使用strtok的问题在于它会修改该行,这会阻止代码使用strtok再次扫描该行。

因此,解决方案是编写一个简单的for循环来解析该行并对数字进行计数。要做到这一点,您需要一个变量来跟踪您是在数字内部还是在数字之间。这是伪代码:

state = outside
count = 0
for each character in the line
    if ( state == outside )
    {
        if ( isdigit(character) )
            count++
            state = inside
    }
    else
    {
        if ( isspace(character) )
            state = outside
    }

在循环结束时,count可用于为阵列分配内存,然后您可以使用strtokstrtol转换数字。请注意,对于字符既不是数字也不是空格的情况,我都没有错误检查。

答案 3 :(得分:0)

您可以编写一个单独的函数,该函数将从字符串中提取数字并将其存储在动态分配的数组中,而不使用realloc。 要在分配数组之前计算字符串中的数字,可以使用标准C函数sscanf

这是一个示范程序

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

size_t get_data( const char *s, int **a )
{    
    int x;
    size_t n;
    int m;

    *a = NULL;
    n = 0;

    for ( const char *p = s; sscanf( p, "%d%n", &x, &m ) == 1; p += m ) ++n;

    if ( n && ( *a = malloc( n * sizeof( int ) ) ) )
    {
        const char *p = s;
        for ( size_t i = 0; i < n; i++, p += m ) sscanf( p, "%d%n", *a + i, &m );
    }

    return n;
}    

int main( void )
{
    char s[] = " 43 2 6 120 5 23";
    int *a;
    size_t n = get_data( s, &a );

    if ( n )
    {
        for ( size_t i = 0; i < n; i++ ) printf( "%d ", a[i] );
        printf( "\n" );
    }        

    free( a );
}

程序输出

43 2 6 120 5 23