char数组的内存分配

时间:2015-09-07 16:09:23

标签: c arrays char

对于字符串char *或其他什么来说,C语言存在很大问题......所以在这个特殊情况下我遇到了一个很大的问题。我想创建一系列字符,但我还不知道它的大小。我想写这样的东西:

char test[];

但是,在那之后,当我知道会有多少元素时,我想写下这样的东西:

char test[num_of_elements];
虽然我知道这是错的,但我无法做到这一点。那么,我该怎么做呢?如何宣布阵列然后定义它的大小?

5 个答案:

答案 0 :(得分:12)

声明静态字符数组(字符串)

当你知道(或者合理地了解你的数组需要多大)时,你可以简单地声明一个足够大小的数组来处理你的输入(即如果你的名字不超过25个字符,那么你可以安全地声明name[26]。对于字符串,您始终需要至少 number of chars to store + 1(对于空终止字符)。< / p>

如果可能有超过25个字符,那么将数组声明的字节数比防止数据末尾之外的意外写入所需的时间长几个字节就没有错。说name[32]

让我们在下面声明一个5-characters数组,看看信息是如何存储在内存中的。

char name[5] = {0}; /* always initialize your arrays */

上面的声明在堆栈上创建了一个5-contiguous bytes数组供您使用。例如,您可以将初始化为0的5字节内存可视化,如下所示:

        +---+---+---+---+---+
 name   | 0 | 0 | 0 | 0 | 0 |
        +---+---+---+---+---+
          \
           'name' holds the starting address for
            the block of memory that is the array
            (i.e. the address of the first element
             like: 0x7fff050bf3d0)

注意:使用name来保存&#39;字符串&#39;时,实际字符串不能超过 4 字符,因为你必须结束使用空终止字符的刺激,这是空字符'\0'(或简称数字0,两者都是等价的)

要在name中存储信息,您可以通过分配一次一个字符来执行此操作:

name[0] = 'J';
name[1] = 'o';
name[2] = 'h';
name[3] = 'n';
name[4] =  0;   /* null-terminate. note: this was already done by initialization */

在记忆中你现在有:

        +---+---+---+---+---+
 name   | J | o | h | n | 0 |    /* you actually have the ASCII value for */
        +---+---+---+---+---+    /* each letter stored in the elements    */

当然,没有人以这种方式一次分配一个角色。您可以使用C提供的功能之一选择很多,例如: strcpystrncpymemcpy,或通过fgetsgetline或使用简单{{}来读取文件流或文件描述符中的信息1}}和loop进行赋值,或者使用其中一个字符串格式化函数,例如index variable等等......例如,您可以使用以下任何方式完成相同的操作:

sprintf

注意:上面有/* direct copy */ strcpy (name, "John"); strncpy (name, "John", 5); memcpy (name, "John", sizeof "John"); /* include copy of the terminating char */ /* read from stdin into name */ printf ("Please enter a name (4 char max): "); scanf ("%[^\n]%*c", name); ,如果你有 NOT 将所有元素初始化为strncpy(最后一个元素将作为你的{ {1}}字符,然后使用0您需要手动终止字符串null-terminating,否则您将没有有效字符串(您将无法终止如果您使用strncpy (name, "John", 4);预期字符串,则会导致name[4] = 0;的字符数组。)

如果您没有明确理解停止,请阅读并了解undefined behavior是什么以及它与name的区别。说真的,现在停下来去学习,这是C的基础(如果它不以null-terminated string字符结尾 - 它不是一个c字符串。

如果我不知道需要存储多少个字符怎么办?

字符串的动态分配

当您不知道需要存储多少个字符(或者通常有多少数据类型)时,通常的方法是声明指向的指针,然后分配合理预期的内存量(仅基于您对所处理内容的最佳理解),然后重新分配以根据需要添加额外内存。它没有什么神奇之处,它只是告诉编译器如何管理内存的另一种方式。请记住,当你分配内存时,你拥有它。您负责(1)保留指向内存块起始地址的指针(以便以后可以释放); (2)完成记忆后释放记忆。

一个简单的例子会有所帮助。大多数内存分配/自由函数都在array of characters中声明。

null-terminating

注意: stdlib.h不会初始化它分配的内存的内容。如果要初始化新的内存块(如我们对静态数组所做的那样),则使用char *name = NULL; /* declare a pointer, and initialize to NULL */ name = malloc (5 * sizeof *name); /* allocate a 5-byte block of memory for name */ if (!name) { /* validate memory was allocated -- every time */ fprintf (stderr, "error: name allocation failed, exiting.\n"); exit (EXIT_FAILURE); } /* Now use name, just as you would the statically declared name above */ strncpy (name, "John", 5); printf (" name contains: %s\n", name); free (name); /* free memory when no longer needed. (if reusing name, set 'name = NULL;') */ 而不是malloc。您也可以使用calloc,然后再拨打malloc

如果我分配内存会发生什么,然后需要更多?

如上所述,讨论动态内存时,一般方案是根据需要分配合理的预期金额,然后malloc。您使用memset重新分配realloc创建的原始内存块。 realloc本质上创建了一个新的内存块,将内存从旧块复制到新块,然后释放旧的内存块。由于旧的内存块已释放,因此您需要使用临时指针进行重新分配。如果重新分配失败,您仍然可以使用原始内存块。

malloc的任何通话中,您都可以随意添加尽可能少的内存。通常看到的标准方案是从一些初始分配开始,然后在每次用完时重新分配两次。 (这意味着你需要跟踪当前分配的内存量。)

为了解决这个问题,让我们以一个简单的例子结束,它只是读取任意长度的字符串作为程序的第一个参数(如果你的字符串包含realloc引号realloc空格)。然后它将分配空间来保存字符串,然后重新分配以将更多文本附加到原始字符串的末尾。最后,它将在退出之前释放所有正在使用的内存:

"

使用/输出

"

内存检查

当您动态分配内存时,您可以验证是否正确使用了内存,并且您可以跟踪并释放所分配的所有内存。使用像#include <stdio.h> #include <stdlib.h> #include <string.h> int main (int argc, char **argv) { if (argc < 2) { /* validate input */ fprintf (stderr, "error: insufficient input. usage: %s \"name\"\n", argv[0]); return 1; } size_t len = strlen (argv[1]); /* length of input */ size_t sz_mem = len + 1; /* memory required */ char *name = malloc (sz_mem * sizeof *name); /* allocate memory for name */ if (!name) { /* validate memory created successfully or throw error */ fprintf (stderr, "error: virtual memory exhausted allocating 'name'\n"); return 1; } printf ("\n allocated %zu bytes of memory for 'name'\n", sz_mem); memset (name, 0, sz_mem); /* initialize memory to zero (optional) */ strncpy (name, argv[1], sz_mem); /* copy the null-terminator as well */ printf (" name: '%s' (begins at address: %p)\n", name, name); /* realloc - make name twice as big */ void *tmp = realloc (name, 2 * sz_mem); /* use a temporary pointer */ if (!tmp) { /* check realloc succeeded */ fprintf (stderr, "error: virtual memory exhausted, realloc 'name'\n"); return 1; } memset (tmp + sz_mem, 0, sz_mem * sizeof *name); /* zero new memory */ name = tmp; /* assign new block to name */ sz_mem += sz_mem; /* update current allocation size */ printf (" reallocated 'name' to %zu bytes\n", sz_mem); strncat (name, " reallocated", sizeof " reallocated"); printf ("\n final name : '%s'\n\n", name); free (name); return 0; } 这样的内存错误检查器来确认您的内存使用是否正确。 (没有理由不这样做,这很简单 - 只需输入$ ./bin/arraybasics "John Q. Public" allocated 15 bytes of memory for 'name' name: 'John Q. Public' (begins at address: 0xf17010) reallocated 'name' to 30 bytes final name : 'John Q. Public reallocated'

valgrind

在输出中,以下行具有特别重要的意义:

valgrind yourprogramexe

这告诉您在程序中分配的所有内存都已正确释放。 (确保关闭所有打开的文件流,它们也是动态分配的。)

同样重要的是$ valgrind ./bin/arraybasics "John Q. Public" ==19613== Memcheck, a memory error detector ==19613== Copyright (C) 2002-2012, and GNU GPL'd, by Julian Seward et al. ==19613== Using Valgrind-3.8.1 and LibVEX; rerun with -h for copyright info ==19613== Command: ./bin/arraybasics John\ Q.\ Public ==19613== allocated 15 bytes of memory for 'name' name: 'John Q. Public' (begins at address: 0x51e0040) reallocated 'name' to 30 bytes final name : 'John Q. Public reallocated' ==19613== ==19613== HEAP SUMMARY: ==19613== in use at exit: 0 bytes in 0 blocks ==19613== total heap usage: 2 allocs, 2 frees, 45 bytes allocated ==19613== ==19613== All heap blocks were freed -- no leaks are possible ==19613== ==19613== For counts of detected and suppressed errors, rerun with: -v ==19613== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

==19613== HEAP SUMMARY:
==19613==     in use at exit: 0 bytes in 0 blocks
==19613==   total heap usage: 2 allocs, 2 frees, 45 bytes allocated

内存使用没有错误。如果您尝试从块外部的位置读取,写入或释放内存,或者从单元化位置读取或释放内存,或者使其他内存无法访问,则该信息将显示为错误。

(抑制:2中的2只与我系统中不存在的其他调试库有关)

这最终比预期更长,但如果有帮助,那就值得了。祝你好运。

答案 1 :(得分:5)

  

如何宣布数组,然后定义它的大小?

别;当你知道它需要的大小时,“宣布”它。无论如何,你之前不能使用它。

在C99及更高版本中,您可以在需要时定义变量 - 在语句块中的任何位置。您还可以使用VLA(可变长度数组),其中大小在运行时才知道。不要创建巨大的阵列作为VLA(例如1 MiB或更高 - 但调整限制以适应您的机器和偏见);毕竟使用动态内存分配。

如果您坚持使用古老的C89 / C90标准,那么您只能在块的开头定义变量,并且数组在编译时具有已知的大小,因此您必须使用动态内存分配 - {{1 },malloc()等。

答案 2 :(得分:1)

首先声明一个指向“char”的指针。 然后请(系统)使用malloc存储所需数量的值,然后将元素添加到此“数组”。

char * test;
int num_of_elements = 99;
test = malloc(sizeof(char) * num_of_elements); 
//test points to first array elament
test[0] = 11;
test[1] = 22;
//etc

答案 3 :(得分:1)

有两种方法可以解决这个问题。

方法#1:使用最大大小来定义阵列。这是代码的样子:

char test[max_size];

然后,您需要跟踪实际用完的元素数量。这在一些老式网络代码中常用。

方法#2:使用动态内存。请注意,这里(可能)存在一些性能问题,因为您每次都要求操作系统获取一块内存。这里有一个答案,告诉你如何做到这一点。一旦完成使用阵列,请确保调用free()。​​

答案 4 :(得分:0)

取决于您的(i)工具链和(ii)您将如何以及何时知道您的尺寸 - 您可以选择使用(a)可变长度数组或(b)动态存储器分配功能。

if(工具链支持C99及更高版本)或(您将在运行时知道数组长度)    使用可变长度数组

if(较旧的工具链)或(您希望分配和释放内存的灵活性)     使用动态内存分配功能

这里有样品

1个可变长度数组

void f(int m, char C[m][m])
{
    char test[m];
    :
}

2使用动态内存分配功能

void somefunc(int n)
{
    char *test;

    test = malloc (n * sizeof (char));

    // check for test != null

    // use test

    free (test);
}

可以使用VLA编写

int n = 5;
char test[n];