在C中创建百万(或更多)字符串数组
我在创建一个足够大的数组来存储大型文本文档(ThinkBooks)中的单词时遇到了一个问题 通常,我会做:在C中创建百万(或更多)字符串数组,c,arrays,string,memory,C,Arrays,String,Memory,我在创建一个足够大的数组来存储大型文本文档(ThinkBooks)中的单词时遇到了一个问题 通常,我会做: char wordList[1000000][30]; 但是,正如所料,程序在尝试初始化数组时立即崩溃。因此,我尝试了一些不同的方法,例如: char *wordList[30] int k=0; while(k<1000000){ wordList[k]= malloc(sizeof(char)*30); k++; } char*wordList[30] int k
char wordList[1000000][30];
但是,正如所料,程序在尝试初始化数组时立即崩溃。因此,我尝试了一些不同的方法,例如:
char *wordList[30]
int k=0;
while(k<1000000){
wordList[k]= malloc(sizeof(char)*30);
k++;
}
char*wordList[30]
int k=0;
而(kwordList
是一个由30个char*
指针组成的数组。您访问的空间远远超出了此数组的限制。具体来说,您访问的空间最多为百万个,但该数组只有30个空间。这将导致
相反,您需要确保wordList
有足够的空间容纳1000000
指针
这应该是:
char *wordList[1000000];
这使得单词的长度具有灵活性。这里唯一固定的大小是数组大小
如果您使用:
wordList[k]= malloc(sizeof(char)*30);
此外,如果单词超过29个
字符(不包括结尾的\0
字符),这将遇到问题。尽管它们的单词长度不超过29个
字符。只要:
超级翼状体
很难找到
此外,这取决于您如何从文本文档中读取这些单词。如果您解析这些单词,并使用临时缓冲区存储它们,则可以执行以下操作:
wordList[k]= malloc(strlen(word)+1); /* +1 for null-terminator */
这将为您复制到wordList[k]
中的任何大小的单词分配内存。这将更有效地用于较小的单词,如“
和”或“
”,而不是为任何单词分配30个空格
注意:预先在堆上分配一百万个指针也是非常浪费的,这个过程应该根据需要进行。甚至最好使用char**wordList
,这样可以更灵活地分配多少单词
例如,您可以分配起始尺寸:
size_t start_size = 1000;
char **wordList = malloc(start_size * sizeof(*wordlist));
然后,如果找到更多的单词,您可以根据需要提供更多的空间
realloc()
调整它指向的内存块的大小,并返回指针
例如:
if (start_size == word_count) {
start_size *= 2;
wordList = realloc(wordList, start_size * sizeof(*wordList));
if (wordList == NULL) {
/* handle exit */
它将返回一个包含开始大小*2
空格的指针
您还应该检查malloc()
和realloc()
的返回,因为如果不成功,它们可以返回NULL
。在程序结束时,您还应该检查从malloc()
分配的指针。实际上,在C中编程时一个好的经验法则是“大”应在堆内存中分配数据
(我猜你是在为一台运行普通操作系统的普通笔记本电脑或台式机编写代码;我实际上想到的是一台台式Linux电脑,就像我在这里回答的那台电脑;但你可以将我的答案改编为运行一些Windows的台式机、运行一些Android的平板电脑、运行一些MacOSX的苹果电脑)
请注意,您当前的wordList
是30兆字节(即sizeof(wordList)==30000000
)。这是很大的!但在某些情况下还不够大(可能不足以容纳整个圣雅各圣经,当然也不足以容纳整个美国或法国的法律、法令和司法,也不足以容纳一份百年历史的报纸的档案)。今天,您可以轻松找到超过几十兆字节的文本数据,例如StackOverflow上的所有消息
您可能需要了解更多关于current的信息,才能理解我的所有答案;我建议您阅读,如果您的计算机运行Linux,并且希望为Linux编写代码,请参阅
您不想将大数组(或任何类型的大数据)分配为本地变量(或带有…),因为调用堆栈是(通常为1兆字节,或其中的一些)。在某些特殊计算机上(想想运行某些特殊配置Linux的昂贵服务器),您可以提高该值(机器调用堆栈)限制,但可能不容易,到几十兆字节。期望一个千兆字节的调用堆栈是不合理的
您可能不想拥有固定大小的大量全局或静态数据(在编译时分配给您的数据)。如果这样做,您的程序可能仍然缺少内存(因为您低估了固定大小),或者甚至无法在较小的计算机上启动(例如,如果您的数据段有20GB,您的可执行文件可能在我的桌面上以32GB启动,但在您的笔记本电脑上(当时只有16GB)无法启动)
剩下的选项是通常的做法:通过间接使用原语来增加内存,从而在中分配所有“大”数据。在标准C中,您将广泛使用和朋友(例如,calloc
)-withfree
来释放内存。FWIW,底层原语(用于增加虚拟地址空间)在Linux上,包含和相关(可能由系统上的malloc
实现调用)。但是标准技术(即malloc
&free
)在(因此,使用malloc
和free
的代码可以在您的部分的努力下变得更加自由)
阅读一些编码规则,例如GNU规则,与以下内容相关,尤其是:
通过动态分配所有数据结构,避免任意限制任何数据结构的长度或数量,包括文件名、行、文件和符号
(重点是我的)
实际上,您的wordList
(这是一个糟糕的名称,它不是一个列表,而是一个向量或一个表)应该是一个动态分配的指向动态分配字符串的指针数组。您可以将其声明为char**wordList;
,并希望保留其分配的大小和使用的长度(可能在另外两个全局变量中,size\u t allocatedSize,usedLength;
..)您可能更喜欢使用以
别忘了给我打电话
allocatedSize=1000;
usedLength=0;
wordList= calloc(allocatedSize, sizeof(char*));
if (!wordList) { perror("initial calloc wordlist"); exit(EXIT_FAILURE); };
void add_new_word(const char*w) {
if (usedLength >= allocatedSize) {
size_t newsize = 4*usedLength/3+10;
char**newlist = calloc(newsize*sizeof(char*));
if (!newlist) { perror("calloc newlist"); exit(FAILURE); };
memcpy (newlist, wordList, usedLength*sizeof(char*));
free (wordList);
wordList = newlist;
allocatedSize = newsize;
};
// here we are sure that wordList is not full,
// so usedLength < allocatedSize
char *dw = strdup(w);
if (!dw) { perror("strdup failure"); exit(EXIT_FAILURE); };
wordList[usedLength++] = dw;
} // end of add_new_word
void destroy_word_list(void) {
if (!wordList) return;
for (size_t ix=0; ix<usedLength; ix++) free(wordList[ix]);
free (wordList);
usedLength = 0;
allocatedSize = 0;
wordList = NULL;
} // end of destroy_word_list