我目前正在学习C,想写一个程序,读取一行数字(从文件),由空格分隔(例如:43 2 6 120 5 23),然后将这些数字存储在一个动态数组中。到目前为止,我的想法是将行读取为字符串,将其分成标记(strtok),然后将这些标记转换为int数字(atoi)并存储它们。我的问题是,我必须使用malloc分配内存,但不能手动告诉程序需要多少内存,因为它必须能够处理任何长度的行和任何大小的数字,我不知道如何做到这一点。我不应该使用realloc。
我不想让任何人替我做我的工作,只是一个简单的例子和/或一个很好的解释一种方法来做我想要的会很有帮助。如果还需要其他信息,我就给你。我知道这里有一些关于这个话题的问题,我已经看过了,我只是很难理解一些没有解释的事情,因为我对C编程完全陌生。当然,如果有人能给我一个链接,可能会帮助我,那就太好了。这是一个非常标准的动态内存使用应用程序。你要做的就是为初始整数声明内存,读到这个数,再把数组重新分配到原来的两倍大,一直读到所有的数据。
虽然将整行数据读入缓冲区,然后根据需要解析缓冲区通常是最佳实践,但在本例中,fscanf
专门用于处理单个整数值的读取和转换(并且它将自动使用分隔值的空白)。
(下面使用calloc
将所有元素初始化为0
, malloc
在本例中是可以的,但是初始化可以防止在更复杂的情况下无意中读取未初始化的元素)
下面是一个简短的例子:
#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] : %dn", 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)
您可以编写一个单独的函数,该函数将从字符串中提取数字并将其存储在动态分配的数组中,而无需使用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
如果您不介意连续的空白,那么您可以简单地计算它们的数量并加一个:
#include <stdio.h>
int main(void)
{
char line[] = "43 2 6 120 5 23";
size_t numCount = 1;
char *ptr = line;
while (*ptr != ' ') {
if (*ptr == ' ') {
numCount++;
}
ptr++;
}
// malloc call, etc.
return 0;
}
读取一行后,代码将需要扫描该行两次,一次是计算数字(以便可以分配正确的内存量),另一次是转换数字并将它们存储在数组中。
使用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
为数组分配内存,然后可以使用strtok
和strtol
来转换数字。注意,我没有对字符既不是数字也不是空格的情况进行错误检查。