C语言 字符数组的内存分配

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/32442600/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 12:06:48  来源:igfitidea点击:

Memory allocation for char array

carrayschar

提问by user1499804

I have a big problem with C language when it comes to strings, char *'s or whatever... So in this particular case I have a huge problem. I want to create an array of chars and I don't know yet what size it will be. I want to write something like this:

当涉及到字符串、char *'s 或其他任何东西时,我对 C 语言有一个大问题......所以在这种特殊情况下我有一个大问题。我想创建一个字符数组,但我还不知道它的大小。我想写这样的东西:

char test[];

But, after that, when I will know how many elements there will be, I want to write something like this:

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

char test[num_of_elements];

though I know it's wrong and that I wouldn't be able to do that. So, how can I do this? How can I announce the array and then define it's size?

尽管我知道这是错误的,而且我无法做到这一点。那么,我该怎么做呢?如何宣布数组然后定义它的大小?

回答by David C. Rankin

Declaring Static Character Arrays (strings)

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

When you know (or have a reasonable idea how large your array needs to be, you can simply declare an array of sufficient size to handle your input (i.e. if you names are no longer than 25 characters, then you could safely declare name[26]. For strings, you always need at minimumthe number of chars to store+1(for the null-terminating character).

当您知道(或对您的数组需要多大有一个合理的想法时,您可以简单地声明一个足够大的数组来处理您的输入(即,如果您的名字不超过 25 个字符,那么您可以安全地声明name[26]. 对于字符串) ,你总是需要最小number of chars to store+1(为空的结束字符)。

If there may be a few characters more than 25, there's nothing wrong with declaring your array few bytes longer than needed to protect against accidental writing beyond the end of the array. Say name[32].

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

Let's declare an array of 5-charactersbelow and look at how the information is stored in memory.

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

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

The above declaration creates an array of 5-contiguous byteson the stack for your use. e.g., you can visualize the 5-bytes of memory initialized to zero as follows:

上面的声明5-contiguous bytes在堆栈上创建了一个供您使用的数组。例如,您可以将初始化为零的 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)

Note:when using nameto hold a 'character string', the actual string can be no longer than 4chars because you mustend a sting with a null-terminating character, which is the null-character '\0'(or simply numeric 0, both are equivalent)

注意:name用于保存“字符串”时,实际字符串不能超过4 个字符,因为您必须空终止字符结束字符串,这是空字符'\0'(或简单的数字0,两者是等效的)

To store information in name, you can either do it by assigning characters one-at-a-time:

要将信息存储在 中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 */

In memory you now have:

在内存中,您现在拥有:

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

Of course, nobody assigns one character at a time in this manner. You options are many, using one of the functions provided by C, e.g. strcpy, strncpy, memcpy, or by reading information from a file stream, or file descriptor with fgets, getline, or by using a simple loopand index variableto do the assignment, or by using one of the string formatting functions, e.g. sprintf, etc... For example you can accomplish the same thing with any of the following:

当然,没有人以这种方式一次指定一个字符。您的选择有很多,使用 C 提供的函数之一,例如strcpy, strncpy, memcpy,或通过从文件流中读取信息,或使用fgets, 的文件描述符getline,或通过使用简单的loopandindex 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);

Note:above with strncpy, if you had NOTinitialized all element to 0(the last of which will serve as your null-terminatingcharacter, and then used strncpy (name, "John", 4);you would need to manually terminatethe string with name[4] = 0;, otherwise you would not have a valid string (you would have an unterminated array of chars which would lead to undefined behaviorif you used namewhere a string was expected.)

注意:上面使用strncpy,如果您没有将所有元素初始化为0(最后一个将作为您的null-terminating字符,然后使用strncpy (name, "John", 4);您需要手动终止字符串name[4] = 0;,否则您将没有有效的字符串(您将有一个未终止的字符数组,undefined behavior如果您name在预期字符串的位置使用,则会导致。)

If you do not explicitly understand this STOP, go read and understand what a null-terminated stringis and how it differs from an array of characters. Seriously, stop now and go learn, it is that fundamental to C. (if it doesn't end with a null-terminatingcharacter - it isn't a c-string.

如果您没有明确理解这个STOP,请阅读并了解什么是 anull-terminated string以及它与a 的区别array of characters。说真的,现在停下来继续学习,这是 C 的基础。(如果它不以null-terminating字符结尾- 它不是 c 字符串。

What if I don't know how many characters I need to store?

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

Dynamic Allocations of Character Strings

字符串的动态分配

When you do not know how many characters you need to store (or generally how many of whatever data type), the normal approach is to declare a pointer to type, and then allocate a reasonably anticipated amount of memory(just based on your best understanding of what you are dealing with), and then reallocate to add additional memory as required. There is no magic to it, it is just a different way of telling the compiler how to manage the memory. Just remember, when you allocate the memory, you own it. You are responsible for (1) preserving a pointer to the beginning address of the memory block (so it can be freed later); and (2) freeing the memory when you are done with it.

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

A simple example will help. Most of the memory allocation/free functions are declared in stdlib.h.

一个简单的例子会有所帮助。大多数内存分配/释放函数都在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 */
    fputs ("error: name allocation failed, exiting.", stderr);
    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;') 
                 */

Note:mallocdoes NOT initialize the contents of the memory it allocates. If you want to initialize your new block of memory with zero (as we did with the static array), then use callocinstead of malloc. You can also use mallocand then call memsetas well.

注意:malloc不会初始化它分配的内存的内容。如果你想用零初始化你的新内存块(就像我们对静态数组所做的那样),那么使用calloc代替malloc。您也可以使用malloc然后调用memset

What happens if I allocate memory, then need More?

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

As mentioned above discussing dynamic memory, the general scheme is to allocate a reasonable anticipated amount, then reallocas required. You use reallocto reallocate the original block of memory created by malloc. reallocessentially creates a new block of memory, copies the memory from your old block to the new, and then frees the old block of memory. Since the old block of memory is freed, you want to use a temporary pointer for reallocation. If reallocation fails, you still have your original block of memory available to you.

正如上面讨论动态内存时提到的,一般的方案是分配一个合理的预期数量,然后realloc根据需要。您realloc用来重新分配由malloc. realloc本质上创建一个新的内存块,将内存从旧块复制到新块,然后释放旧内存块。由于旧的内存块被释放,您希望使用临时指针进行重新分配。如果重新分配失败,您仍然可以使用原始内存块。

You are free to add as little or as much memory as you like at any call to realloc. The standard scheme usually seen is to start with some initial allocation, then reallocate twice that amount each time you run out. (the means you need to keep track of how much memory is currently allocated).

您可以在任何调用realloc. 通常看到的标准方案是从一些初始分配开始,然后每次用完时重新分配两倍的数量。(这意味着您需要跟踪当前分配了多少内存)。

To sew this up, let's end with a simple example that simply reads a string of any length as the first argument to the program (use "quotes"if your string contains whitespace). It will then allocates space to hold the string, then reallocate to append more text to the end of the original string. Finally it will free all memory in use before exit:

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

#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 */
        fputs ("error: name allocation failed, exiting.", stderr);
        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;
}

Use/Output

使用/输出

$ ./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'

Memory Check

内存检查

When you dynamically allocate memory, it is up to you to validate you are using the memory correctly and that you track and free all the memory you allocate. Use a memory error checker like valgrindto veryify your memory use is correct. (there is no excuse not to, it is dead-bang-simple to do) Just type valgrind yourprogramexe

当您动态分配内存时,由您来验证您是否正确使用了内存以及您是否跟踪并释放了您分配的所有内存。使用内存错误检查器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)

In the output, the following lines are of particular significance:

在输出中,以下几行特别重要:

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

This tells you that all memory allocated during your program has been properly freed. (make sure you close all open file streams, they are dynamically allocated as well).

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

Of equal importance is the ERROR SUMMARY:

同样重要的是ERROR SUMMARY

==19613== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 2 from 2)

There are no errors in the memory use. If you attempt to read, write or free memory from a location outside your block, or from an unitialized location or that would leave other memory unreachable, that information will show as an error.

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

(the suppressed: 2 from 2 just relate to additional debug libraries not present on my system)

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

This ended up longer than intended, but if it helps, it was worth it. Good luck.

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

回答by Jonathan Leffler

How can I announce the array and then define it's size?

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

Don't; 'announce' it when you know what size it needs to be. You can't use it before then anyway.

别; 当你知道它需要什么尺寸时“宣布”它。无论如何,在那之前你不能使用它。

In C99 and later, you can define variables when needed — anywhere in a statement block. You can also use VLAs (variable-length arrays) where the size is not known until runtime. Don't create enormous arrays as VLAs (e.g. 1 MiB or more — but tune the limit to suit your machine and prejudices); use dynamic memory allocation after all.

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

If you're stuck with the archaic C89/C90 standard, then you can only define variables at the start of a block, and arrays have sizes known at compile time, so you have to use dynamic memory allocation — malloc(), free()etc.

如果你坚持古老的C89 / C90的标准,那么你只能在一个块的开始定义变量和数组在编译时已知的大小,所以你必须使用动态内存分配- malloc()free()等等。

回答by jpd

First declare a pointer to a "char". Then ask (the system) for space to store your required number of values using malloc and then add elements to this "array".

首先声明一个指向“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

回答by It'sPete

There are two ways of solving this issue.

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

Method #1: Use a maximum size to define your array. Here's what the code looks like:

方法#1:使用最大大小来定义数组。代码如下所示:

char test[max_size];

You then need to keep track of how many elements are actually used up. This is used commonly in some old-school networking code.

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

Method #2: Use dynamic memory. Note that there is a bit of a performance issue here (potentially) since you have the ask the OS each time for a chunk of memory. There is an answer up here already that shows you how to do this. Just be sure to call free() once you are done using your array.

方法#2:使用动态内存。请注意,这里(可能)存在一些性能问题,因为您每次都需要向操作系统询问一块内存。这里已经有一个答案告诉你如何做到这一点。使用完数组后,请务必调用 free() 。

回答by Abhi

Depending upon your (i) tool-chain and (ii) how and when you will know you the size - you have the option to use either (a) Variable Length Arrays or (b) Dynamic Memory Allocation functions.

根据您的 (i) 工具链和 (ii) 如何以及何时知道大小 - 您可以选择使用 (a) 可变长度数组或 (b) 动态内存分配函数。

if (tool-chain supports C99 and later) or (you will know the array length at runtime) use Variable Length Array

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

if (older tool-chain) or (you want the flexibility of allocating and releasing the memory) use Dynamic Memory allocation function

如果(旧工具链)或(您想要分配和释放内存的灵活性)使用动态内存分配功能

here are samples

这是样品

1 Variable Length Array

1 变长数组

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

or

或者

2 using Dynamic Memory Allocation function

2 使用动态内存分配功能

void somefunc(int n)
{
    char *test;

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

    // check for test != null

    // use test

    free (test);
}

can be written using VLA as

可以用 VLA 写成

int n = 5;
char test[n];

回答by No Body

This is how. It is actually quite simple.

这是如何。其实很简单。

char *String = malloc(size); //size can just be the length of the string in characters

Then you can alter your string however you wish. malloc determines the amount to allocate at runtime. This allocates it on the heap, rather than the stack. You can reallocate more memory with realloc():

然后你可以随意改变你的字符串。malloc 确定在运行时分配的数量。这将它分配在堆上,而不是堆栈上。您可以使用以下方法重新分配更多内存realloc()

String = realloc(newSize);

IMPORTANT:Once you are done with the string, you must free it, like this, if it was allocated with malloc():

重要提示:一旦你完成了字符串,你必须像这样释放它,如果它被分配了malloc()

free(String);

Or to be safe:

或者为了安全:

if (String) {
    free(String);
}

Another thing, is if you are only dealing with string literals, the program will deal with it for you. Behind the scenes, string literals allocate memory, then "return" the a pointer to the first character. This is only works because string literals' lengths are compile time constant. Following will actually work:

另一件事是,如果您只处理字符串文字,程序会为您处理。在幕后,字符串文字分配内存,然后“返回”指向第一个字符的指针。这只有效,因为字符串文字的长度是编译时常量。以下将实际工作:

char *str;
str = "Hello";
str = "Hello World"; //works!
//BUT
strcat(str, "!"); //doesn't work

Don't believe me? Try it...

不相信我?尝试一下...