使用C返回数组

Returning an array using C

我对C比较陌生,在处理数组的方法方面需要一些帮助。来自Java编程,我习惯于能够说EDCOX1×0Ω,以便返回数组。但是,我发现使用C时,返回数组时必须使用指针。作为一个新的程序员,我真的完全不理解这一点,即使有很多论坛我都看过。

基本上,我正试图编写一个用C语言返回char数组的方法。我将为该方法(让我们调用它)提供一个数组。它将从上一个数组创建一个新数组,并返回指向它的指针。我只需要一些关于如何开始的帮助,以及如何在指针从数组中发出后读取指针。如有任何解释,我们将不胜感激。

数组返回函数的建议代码格式

1
2
3
4
5
char *returnArray(char array []){
 char returned [10];
 //methods to pull values from array, interpret them, and then create new array
 return &(returned[0]); //is this correct?
}

函数的调用方

1
2
3
4
5
6
7
8
int main(){
 int i=0;
 char array []={1,0,0,0,0,1,1};
 char arrayCount=0;
 char* returnedArray = returnArray(&arrayCount); ///is this correct?
 for (i=0; i<10;i++)
  printf(%d,",", returnedArray[i]);  //is this correctly formatted?
}

我还没有测试这个,因为我的C编译器目前不工作,但我想解决这个问题。


不能从C中的函数返回数组。也不能(不应该)这样做:

1
2
3
4
5
char *returnArray(char array []){
 char returned [10];
 //methods to pull values from array, interpret them, and then create new array
 return &(returned[0]); //is this correct?
}

returned是用自动存储持续时间创建的,一旦它离开声明范围(即当函数返回时),对它的引用就会失效。

您将需要在函数内部动态分配内存,或者填充调用者提供的预分配缓冲区。

选项1:

在函数内部动态分配内存(调用方负责释放ret)

1
2
3
4
5
6
7
8
9
10
char *foo(int count) {
    char *ret = malloc(count);
    if(!ret)
        return NULL;

    for(int i = 0; i < count; ++i)
        ret[i] = i;

    return ret;
}

这样称呼它:

1
2
3
4
5
6
7
8
9
int main() {
    char *p = foo(10);
    if(p) {
        // do stuff with p
        free(p);
    }

    return 0;
}

选项2:

填充调用方提供的预分配缓冲区(调用方分配buf并传递给函数)

1
2
3
4
void foo(char *buf, int count) {
    for(int i = 0; i < count; ++i)
        buf[i] = i;
}

就像这样称呼它:

1
2
3
4
5
6
7
8
9
int main() {
    char arr[10] = {0};
    foo(arr, 10);
    // No need to deallocate because we allocated
    // arr with automatic storage duration.
    // If we had dynamically allocated it
    // (i.e. malloc or some variant) then we
    // would need to call free(arr)
}


C对数组的处理与Java非常不同,因此你必须相应地调整你的思维。C中的数组不是第一类对象(也就是说,数组表达式在大多数上下文中不保留它的"数组性")。在C语言中,"n-element array of EDOCX1"(0)类型的表达式将被隐式转换("decay")为"pointer to EDOCX1"(0)类型的表达式,除非数组表达式是sizeof或一元&运算符的操作数,或者数组表达式是字符串文字,用于初始化数据中的另一个数组。外宣。

除此之外,这意味着您不能将数组表达式传递给函数并将其作为数组类型接收;该函数实际上接收指针类型:

1
2
3
4
5
6
7
8
9
10
void foo(char *a, size_t asize)
{
  // do something with a
}

int bar(void)
{
  char str[6] ="Hello";
  foo(str, sizeof str);
}

在对foo的调用中,表达式str由类型char [6]转换为char *,这就是为什么foo的第一个参数被声明为char *a而不是char a[6]的原因。在sizeof str中,由于数组表达式是sizeof运算符的操作数,因此它不会转换为指针类型,因此可以得到数组(6)中的字节数。

如果你真的很感兴趣,你可以阅读丹尼斯·里奇的《C语言的发展》,了解这种治疗的来源。

结果是函数不能返回数组类型,这很好,因为数组表达式也不能作为赋值的目标。

最安全的方法是让调用者定义数组,并将其地址和大小传递给应该写入该数组的函数:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
void returnArray(const char *srcArray, size_t srcSize, char *dstArray, char dstSize)
{
  ...
  dstArray[i] = some_value_derived_from(srcArray[i]);
  ...
}

int main(void)
{
  char src[] ="This is a test";
  char dst[sizeof src];
  ...
  returnArray(src, sizeof src, dst, sizeof dst);
  ...
}

另一种方法是函数动态分配数组并返回指针和大小:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
char *returnArray(const char *srcArray, size_t srcSize, size_t *dstSize)
{
  char *dstArray = malloc(srcSize);
  if (dstArray)
  {
    *dstSize = srcSize;
    ...
  }
  return dstArray;
}

int main(void)
{
  char src[] ="This is a test";
  char *dst;
  size_t dstSize;

  dst = returnArray(src, sizeof src, &dstSize);
  ...
  free(dst);
  ...
}

在这种情况下,调用方负责使用free库函数解除数组的分配。

注意,上面代码中的dst是指向char的简单指针,而不是指向char数组的指针。c的指针和数组语义可以将下标运算符[]应用于数组类型或指针类型的表达式;src[i]dst[i]都将访问数组的i第四个元素(即使只有src具有数组类型)。

您可以声明指向T的n元素数组的指针,并执行类似的操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
char (*returnArray(const char *srcArr, size_t srcSize))[SOME_SIZE]
{
  char (*dstArr)[SOME_SIZE] = malloc(sizeof *dstArr);
  if (dstArr)
  {
    ...
    (*dstArr)[i] = ...;
    ...
  }
  return dstArr;
}

int main(void)
{
  char src[] ="This is a test";
  char (*dst)[SOME_SIZE];
  ...
  dst = returnArray(src, sizeof src);
  ...
  printf("%c", (*dst)[j]);
  ...
}

上面有几个缺点。首先,C的旧版本希望SOME_SIZE是编译时常量,这意味着该函数只能使用一个数组大小。其次,在应用下标之前必须取消对指针的引用,这会使代码混乱。当处理多维数组时,指向数组的指针工作得更好。


这个恶作剧怎么办?

数组H

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
#define IMPORT_ARRAY(TYPE)    \
    \
struct TYPE##Array {    \
    TYPE* contents;    \
    size_t size;    \
};    \
    \
struct TYPE##Array new_##TYPE##Array() {    \
    struct TYPE##Array a;    \
    a.contents = NULL;    \
    a.size = 0;    \
    return a;    \
}    \
    \
void array_add(struct TYPE##Array* o, TYPE value) {    \
    TYPE* a = malloc((o->size + 1) * sizeof(TYPE));    \
    TYPE i;    \
    for(i = 0; i < o->size; ++i) {    \
        a[i] = o->contents[i];    \
    }    \
    ++(o->size);    \
    a[o->size - 1] = value;    \
    free(o->contents);    \
    o->contents = a;    \
}    \
void array_destroy(struct TYPE##Array* o) {    \
    free(o->contents);    \
}    \
TYPE* array_begin(struct TYPE##Array* o) {    \
    return o->contents;    \
}    \
TYPE* array_end(struct TYPE##Array* o) {    \
    return o->contents + o->size;    \
}

主C

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <stdlib.h>
#include"array.h"

IMPORT_ARRAY(int);

struct intArray return_an_array() {
    struct intArray a;
    a = new_intArray();
    array_add(&a, 1);
    array_add(&a, 2);
    array_add(&a, 3);
    return a;
}

int main() {
    struct intArray a;
    int* it;
    int* begin;
    int* end;
    a = return_an_array();
    begin = array_begin(&a);
    end = array_end(&a);
    for(it = begin; it != end; ++it) {
        printf("%d", *it);
    }
    array_destroy(&a);
    getchar();
    return 0;
}


我不是说这是针对给定问题的最佳解决方案或首选解决方案。但是,记住函数可以返回结构可能很有用。虽然函数不能返回数组,但数组可以包装在结构中,并且函数可以返回结构,从而携带数组。这适用于固定长度的数组。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>

    typedef
    struct
    {
        char v[10];
    } CHAR_ARRAY;



    CHAR_ARRAY returnArray(CHAR_ARRAY array_in, int size)
    {
        CHAR_ARRAY returned;

        /*
        . . . methods to pull values from array, interpret them, and then create new array
        */


        for (int i = 0;  i < size; i++ )
            returned.v[i] = array_in.v[i] + 1;

        return returned; // Works!
    }




    int main(int argc, char * argv[])
    {
        CHAR_ARRAY array = {1,0,0,0,0,1,1};

        char arrayCount = 7;

        CHAR_ARRAY returnedArray = returnArray(array, arrayCount);

        for (int i = 0; i < arrayCount; i++)
            printf("%d,", returnedArray.v[i]);  //is this correctly formatted?

        getchar();
        return 0;
    }

我邀请对这种技术的优点和缺点的评论。我没费心这么做。


在这种情况下,您将在堆栈上创建一个数组,一旦离开函数范围,该数组将被释放。相反,创建一个动态分配的数组并返回指向它的指针。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
char * returnArray(char *arr, int size) {
    char *new_arr = malloc(sizeof(char) * size);
    for(int i = 0; i < size; ++i) {
        new_arr[i] = arr[i];
    }
    return new_arr;
}

int main() {

    char arr[7]= {1,0,0,0,0,1,1};
    char *new_arr = returnArray(arr, 7);

    // don't forget to free the memory after you're done with the array
    free(new_arr);

}


您可以使用堆内存(通过malloc()调用),就像这里报告的其他答案一样,但必须始终管理内存(每次调用函数时都使用free()函数)。您还可以使用静态数组:

1
2
3
4
5
6
7
8
char* returnArrayPointer()
{
static char array[SIZE];

// do something in your array here

return array;
}

你不必担心内存管理就可以使用它。

1
2
3
4
5
6
int main()
{
char* myArray = returnArrayPointer();
/* use your array here */
/* don't worry to free memory here */
}

在本例中,必须在数组定义中使用static关键字将数组的生存期设置为application long,这样在返回语句之后就不会销毁它。当然,在整个应用程序生命周期中,您会占用内存中的大小字节,所以要正确地调整大小!


您的方法将返回一个将严重失败的局部堆栈变量。若要返回数组,请在函数外部创建一个数组,将其按地址传递给函数,然后修改它,或者在堆上创建一个数组并返回该变量。两者都可以工作,但第一个不需要任何动态内存分配来使其正常工作。

1
2
3
4
5
6
7
8
9
10
11
12
13
void returnArray(int size, char *retArray)
{
  // work directly with retArray or memcpy into it from elsewhere like
  // memcpy(retArray, localArray, size);
}

#define ARRAY_SIZE 20

int main(void)
{
  char foo[ARRAY_SIZE];
  returnArray(ARRAY_SIZE, foo);
}

您可以使用如下代码:

1
2
3
4
5
6
7
char *MyFunction(some arguments...)
{
    char *pointer = malloc(size for the new array);
    if (!pointer)
        An error occurred, abort or do something about the error.
    return pointer; // Return address of memory to the caller.
}

当您这样做时,应该稍后通过将地址传递给free来释放内存。

还有其他选择。例程可能返回指向某个现有结构的一部分的数组(或数组的一部分)的指针。调用方可能会传递一个数组,而例程只会写入该数组,而不是为新数组分配空间。