如何创建字符串数组?
How do I create an array of strings?
我需要创建一个数组 I,其中列数组是固定大小,行数组是动态分配的(使用 malloc
)。
我查看了其他 similar questions,他们将整个内容固定或动态分配。你怎么能两者兼得?
char A[malloc][100];
我的猜测是:
char *A[100];
根据我的评论,您可以静态声明一个指针数组,然后为从文件中读取的每一行动态分配存储空间。下面是一个展示这种方法的简短例子。您必须保留读取行数的索引或计数器,以防止写入超出指针数组的末尾。
注意下面的 xcalloc
只是 calloc
的包装器,它为每一行分配 space 并进行错误检查。下面的分配是长途(注意:您可以用 strdup
替换长途,如评论中所示)完整的 allocate/validate/nul-terminate 分配方法如下所示 example/explanation。
您可以通过更改 #define MAXL
值来更改声明的指针数,并通过更改 #define MAXC
值来调整每行的最大字符数。查看并了解每行的每个部分在做什么。请注意静态缓冲区 buf
的使用,在分配内存之前将初始行读入其中,并将内容分配给数组中的索引。这非常有用,因为它允许您在为其分配 space 并将字符串复制到新内存之前验证您所阅读的内容。它允许您跳过空行等,这比 free/unindex 不需要的行要简单得多:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 256 /* max chars per-line */
#define MAXL 100 /* initial num pointers */
void *xcalloc (size_t n, size_t s);
int main (int argc, char **argv) {
char *array[MAXL] = {NULL};
char buf[MAXC] = {0};
size_t i, idx = 0;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, MAXC, fp)) /* read all lines from fp into array */
{
size_t len = strlen (buf);
/* validate complete line read */
if (len + 1 == MAXC && buf[len - 1] != '\n')
fprintf (stderr, "warning: line[%zu] exceeded '%d' chars.\n",
idx, MAXC);
/* strip trailing '\r', '\n' */
while (len && (buf[len-1] == '\n' || buf[len-1] == '\r'))
buf[--len] = 0;
/* allocate & copy buf to array[idx], nul-terminate
* note: this can all be done with array[idx] = strdup (buf);
*/
array[idx] = xcalloc (len + 1, sizeof **array);
strncpy (array[idx], buf, len);
array[idx++][len] = 0;
/* MAXL limit check - if reached, break */
if (idx == MAXL) break;
}
if (fp != stdin) fclose (fp);
printf ("\n lines read from '%s'\n\n", argc > 1 ? argv[1] : "stdin");
for (i = 0; i < idx; i++)
printf (" line[%3zu] %s\n", i, array[i]);
for (i = 0; i < idx; i++)
free (array[i]); /* free each line */
return 0;
}
/* simple calloc with error checking */
void *xcalloc (size_t n, size_t s)
{
void *memptr = calloc (n, s);
if (memptr == 0) {
fprintf (stderr, "xcalloc() error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
return memptr;
}
编译
gcc -Wall -Wextra -O3 -o bin/fgets_lines_stat_dyn fgets_lines_stat_dyn.c
Use/Output
$ ./bin/fgets_lines_stat_dyn dat/captnjack.txt
lines read from 'dat/captnjack.txt'
line[ 0] This is a tale
line[ 1] Of Captain Jack Sparrow
line[ 2] A Pirate So Brave
line[ 3] On the Seven Seas.
内存Leak/Error检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 个责任:(1) 始终保留指向内存块起始地址的指针,因此,(2) 它可以是不再需要时释放。您必须使用内存错误检查程序来确保您没有写入 beyond/outside 您分配的内存块并确认您已释放所有分配的内存。对于 Linux valgrind
是正常的选择。滥用内存块的微妙方法有很多,可能会导致真正的问题,没有理由不这样做。每个平台都有类似的内存检查器。它们都易于使用。只是运行你的程序通过它。
$ valgrind ./bin/fgets_lines_stat_dyn dat/captnjack.txt
==22770== Memcheck, a memory error detector
==22770== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==22770== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==22770== Command: ./bin/fgets_lines_dyn dat/captnjack.txt
==22770==
lines read from 'dat/captnjack.txt'
line[ 0] This is a tale
line[ 1] Of Captain Jack Sparrow
line[ 2] A Pirate So Brave
line[ 3] On the Seven Seas.
==22770==
==22770== HEAP SUMMARY:
==22770== in use at exit: 0 bytes in 0 blocks
==22770== total heap usage: 6 allocs, 6 frees, 1,156 bytes allocated
==22770==
==22770== All heap blocks were freed -- no leaks are possible
==22770==
==22770== For counts of detected and suppressed errors, rerun with: -v
==22770== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)
我需要创建一个数组 I,其中列数组是固定大小,行数组是动态分配的(使用 malloc
)。
我查看了其他 similar questions,他们将整个内容固定或动态分配。你怎么能两者兼得?
char A[malloc][100];
我的猜测是:
char *A[100];
根据我的评论,您可以静态声明一个指针数组,然后为从文件中读取的每一行动态分配存储空间。下面是一个展示这种方法的简短例子。您必须保留读取行数的索引或计数器,以防止写入超出指针数组的末尾。
注意下面的 xcalloc
只是 calloc
的包装器,它为每一行分配 space 并进行错误检查。下面的分配是长途(注意:您可以用 strdup
替换长途,如评论中所示)完整的 allocate/validate/nul-terminate 分配方法如下所示 example/explanation。
您可以通过更改 #define MAXL
值来更改声明的指针数,并通过更改 #define MAXC
值来调整每行的最大字符数。查看并了解每行的每个部分在做什么。请注意静态缓冲区 buf
的使用,在分配内存之前将初始行读入其中,并将内容分配给数组中的索引。这非常有用,因为它允许您在为其分配 space 并将字符串复制到新内存之前验证您所阅读的内容。它允许您跳过空行等,这比 free/unindex 不需要的行要简单得多:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAXC 256 /* max chars per-line */
#define MAXL 100 /* initial num pointers */
void *xcalloc (size_t n, size_t s);
int main (int argc, char **argv) {
char *array[MAXL] = {NULL};
char buf[MAXC] = {0};
size_t i, idx = 0;
FILE *fp = argc > 1 ? fopen (argv[1], "r") : stdin;
if (!fp) {
fprintf (stderr, "error: file open failed '%s'.\n", argv[1]);
return 1;
}
while (fgets (buf, MAXC, fp)) /* read all lines from fp into array */
{
size_t len = strlen (buf);
/* validate complete line read */
if (len + 1 == MAXC && buf[len - 1] != '\n')
fprintf (stderr, "warning: line[%zu] exceeded '%d' chars.\n",
idx, MAXC);
/* strip trailing '\r', '\n' */
while (len && (buf[len-1] == '\n' || buf[len-1] == '\r'))
buf[--len] = 0;
/* allocate & copy buf to array[idx], nul-terminate
* note: this can all be done with array[idx] = strdup (buf);
*/
array[idx] = xcalloc (len + 1, sizeof **array);
strncpy (array[idx], buf, len);
array[idx++][len] = 0;
/* MAXL limit check - if reached, break */
if (idx == MAXL) break;
}
if (fp != stdin) fclose (fp);
printf ("\n lines read from '%s'\n\n", argc > 1 ? argv[1] : "stdin");
for (i = 0; i < idx; i++)
printf (" line[%3zu] %s\n", i, array[i]);
for (i = 0; i < idx; i++)
free (array[i]); /* free each line */
return 0;
}
/* simple calloc with error checking */
void *xcalloc (size_t n, size_t s)
{
void *memptr = calloc (n, s);
if (memptr == 0) {
fprintf (stderr, "xcalloc() error: virtual memory exhausted.\n");
exit (EXIT_FAILURE);
}
return memptr;
}
编译
gcc -Wall -Wextra -O3 -o bin/fgets_lines_stat_dyn fgets_lines_stat_dyn.c
Use/Output
$ ./bin/fgets_lines_stat_dyn dat/captnjack.txt
lines read from 'dat/captnjack.txt'
line[ 0] This is a tale
line[ 1] Of Captain Jack Sparrow
line[ 2] A Pirate So Brave
line[ 3] On the Seven Seas.
内存Leak/Error检查
在您编写的任何动态分配内存的代码中,您对分配的任何内存块负有 2 个责任:(1) 始终保留指向内存块起始地址的指针,因此,(2) 它可以是不再需要时释放。您必须使用内存错误检查程序来确保您没有写入 beyond/outside 您分配的内存块并确认您已释放所有分配的内存。对于 Linux valgrind
是正常的选择。滥用内存块的微妙方法有很多,可能会导致真正的问题,没有理由不这样做。每个平台都有类似的内存检查器。它们都易于使用。只是运行你的程序通过它。
$ valgrind ./bin/fgets_lines_stat_dyn dat/captnjack.txt
==22770== Memcheck, a memory error detector
==22770== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al.
==22770== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info
==22770== Command: ./bin/fgets_lines_dyn dat/captnjack.txt
==22770==
lines read from 'dat/captnjack.txt'
line[ 0] This is a tale
line[ 1] Of Captain Jack Sparrow
line[ 2] A Pirate So Brave
line[ 3] On the Seven Seas.
==22770==
==22770== HEAP SUMMARY:
==22770== in use at exit: 0 bytes in 0 blocks
==22770== total heap usage: 6 allocs, 6 frees, 1,156 bytes allocated
==22770==
==22770== All heap blocks were freed -- no leaks are possible
==22770==
==22770== For counts of detected and suppressed errors, rerun with: -v
==22770== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)