使用 C 返回数组

Returning an array using C

提问人:user1506919 提问时间:7/26/2012 最后编辑:Peter Mortensenuser1506919 更新时间:11/4/2022 访问量:548697

问:

我对 C 相对较新,我需要一些关于处理数组的方法的帮助。来自Java编程,我习惯于能够说出一个数组。但是,我发现使用 C 时,您必须在返回数组时使用指针作为数组。作为一个新程序员,我真的完全不明白这一点,即使我浏览过许多论坛。int [] method()

基本上,我正在尝试编写一个在 C 中返回 char 数组的方法。我将为方法(我们称之为 returnArray)提供一个数组。它将从前一个数组创建一个新数组,并返回指向它的指针。我只是需要一些关于如何开始以及如何在指针从数组中发送后读取指针的帮助。

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

char *returnArray(char array []){
  char returned [10];
  // Methods to pull values from the array, interpret
  // them, and then create a new array
  return &(returned[0]); // Is this correct?
}

函数的调用方

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 数组指 char

评论

0赞 strangefreeworld 7/26/2012
返回数组是否为代码示例中所示的已知大小?除了答案中提到的堆栈问题之外,我看到的唯一另一个问题是,如果你的返回数组是一个不确定的大小,考虑到指针/数组在 C 中的工作方式,你不会知道它有多大。
0赞 user1506919 7/26/2012
是的,我随时都知道传入阵列的大小。输入和输出数组的大小不会改变。
1赞 x4444 12/31/2017
C 语言的发展* - bell-labs.com/usr/dmr/www/chist.html
0赞 Peter Mortensen 11/4/2022
对于返回对函数本地堆栈的(无效)引用的初学者错误,规范可能是在 C++ 中返回对局部变量的引用。虽然它可能隐藏在前 3 年的问题中,但搜索引擎非常不愿意(无论出于何种原因)指向这些问题(这并不意味着这是由于他们的年龄)。

答:

8赞 Man of One Way 7/26/2012 #1

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

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);

}

评论

3赞 Eric Postpischil 7/26/2012
C 中没有运算符。那就是C++。new
1赞 Ed S. 7/26/2012
并且保证是 ,所以在这种情况下,您可以从 中删除该位。sizeof(char)1malloc
0赞 user1506919 7/26/2012
好的,所以如果我想打印出新数组的内容,我可以只做我的'printf'语句,但用'arr'替换'returnedArray'吗?
0赞 Ed S. 7/26/2012
您没有正确调用函数(当签名需要两个参数时,只有一个参数)。
0赞 chris 7/26/2012
你路过.你想成为一个 ,并使用 传入它。&arrarrchar *arr
3赞 Michael Dorgan 7/26/2012 #2

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

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);
}
301赞 Ed S. 7/26/2012 #3

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

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)

char *foo(int count) {
    char *ret = malloc(count);
    if(!ret)
        return NULL;

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

    return ret;
}

这样称呼它:

int main() {
    char *p = foo(10);
    if(p) {
        // do stuff with p
        free(p);
    }

    return 0;
}

选项 2:

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

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

并这样称呼它:

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)
}

评论

48赞 moooeeeep 7/26/2012
选项 3:(静态数组)
5赞 Ed S. 7/26/2012
@moooeeeep:是的,为了简单起见,我故意省略了这一点,但是是的,您可以返回指向从函数中声明的静态数据的指针。
4赞 Ed S. 7/26/2012
@user1506919:我实际上更喜欢选项 2,因为谁分配和释放内存很清楚,但我要为你添加一个例子。
15赞 Todd Lehman 5/21/2015
选项 4:返回包含固定大小数组的结构。
2赞 sqr163 5/19/2017
选项 5:返回包含固定大小数组的联合。
1赞 Eric Postpischil 7/26/2012 #4

您可以像这样使用代码:

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 来释放内存。

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

45赞 John Bode 7/26/2012 #5

C 对数组的处理方式与 Java 的处理方式非常不同,您必须相应地调整您的思维方式。C 语言中的数组不是第一类对象(也就是说,数组表达式在大多数上下文中不保留其“数组性”)。在 C 中,类型为 “N-element array of ” 的表达式将被隐式转换为 “pointer to” 类型的表达式,除非数组表达式是 or 一元运算符的操作数,或者数组表达式是用于初始化声明中另一个数组的字符串文本。TTsizeof&

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

void foo(char *a, size_t asize)
{
  // do something with a
}

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

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

如果你真的有兴趣,你可以阅读丹尼斯·里奇(Dennis Ritchie)的《C语言的发展》(The Development of the C Language),了解这种治疗方法的来源。

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

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

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);
  ...
}

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

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

请注意,在上面的代码中是指向 的简单指针,而不是指向 数组的指针。C 的指针和数组语义使得您可以将下标运算符应用于数组类型指针类型的表达式;两者都将访问数组的第 'th 个元素(即使只有数组类型)。dstcharchar[]src[i]dst[i]isrc

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

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

评论

3赞 Dr.Queso 3/25/2016
您与“C 开发”的链接已断开......看起来它应该将我们引导到这里:bell-labs.com/usr/dmr/www/chist.html
0赞 John Bode 4/14/2016
@Kundor:接收的是指针,而不是数组。在函数参数声明的上下文中,和 都被视为 。barT a[N]T a[]T *a
0赞 Nick Matteo 4/14/2016
@JohnBode:你说得对!出于某种原因,我认为固定大小的数组是在堆栈上传递的。我记得很多年前的一次,当时我发现必须在参数签名中指定数组的大小,但我一定很困惑。
0赞 Seyfi 11/17/2018
@JohnBode,在第二代码部分第一行:最后一个参数应该在类型中不。void returnArray(const char *srcArray, size_t srcSize, char *dstArray, char dstSize)size_tchar
12赞 pyrospade 7/26/2012 #6

使用这个美味的邪恶实现:

数组.h

#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

#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;
}

评论

4赞 Unheilig 1/9/2014
这真是太好吃了,足以引起我的好奇心。你能再解释一下你在那里做了什么吗,或者建议读一下你所说的这种美味?提前致谢。
1赞 pyrospade 1/9/2014
@Unheilig - 请注意,这其中存在潜在的错误,这只是概念的证明。也就是说,诀窍是返回一个作为数组容器/对象。把它想象成一个C++ std::vector。预处理器会将此版本扩展为 。structintstruct intArray { int* contents; int size; };
1赞 urkon 1/4/2018
我喜欢这种方法。优点:这是通用解决方案;CONTRA:内存密集型解决方案。对于kown大小的载体来说不是最佳选择。无论如何,这可以通过初始大小分配进行升级。我肯定会添加一些分配检查。非常好的建议,从:)开始
1赞 Jack G 1/22/2018
面向对象的 esk 预设 mix-mash。我喜欢。
0赞 JaDogg 9/17/2022
这让我想起了stb_ds
12赞 mengo 12/2/2014 #7

你可以像这里报告的其他答案一样使用堆内存(通过 malloc() 调用)来做到这一点,但你必须始终管理内存(每次调用函数时都使用 free() 函数)。

您也可以使用静态数组来执行此操作:

char* returnArrayPointer()
{
    static char array[SIZE];

    // Do something in your array here

    return array;
}

然后,您可以使用它而不必担心内存管理。

int main()
{
    char* myArray = returnArrayPointer();
    /* Use your array here */
    /* Don't worry to free memory here */
}

在此示例中,您必须在数组定义中使用 static 关键字将数组生存期设置为 application-long,这样在 return 语句后不会销毁它。

当然,通过这种方式,您在整个应用程序生命周期中都会占用内存中的 SIZE 字节,因此请正确调整其大小!

评论

0赞 user426 11/14/2021
向函数的内部存储器分发指针有多好?忘记,多线程,这在串行代码中是不好的。
0赞 sueszli 12/3/2022
此页面中有很多关于如何解决这个问题的建议,但我也发现使用“静态”返回数组是最好的方法,只要您知道返回的值从那时起就是一个全局变量。
25赞 Indinfer 3/11/2015 #8

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

    #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;
    }

评论

2赞 5/19/2020
不清楚为什么这不是公认的答案。问题不在于是否可以返回指向数组的指针。
1赞 Minh Tran 6/3/2020
是否在堆上分配了内存?它当然不能在堆栈上(在堆栈框架中,对吧?CHAR_ARRAY returnedreturnArray()
0赞 KokoEfraim 5/7/2021
是的,这是我问题的答案:C 函数可以返回数组吗?是的,它可以,@Indinfer使用 C 自己的 struc 数据类型来回答它。当然,它应该是固定长度的数组。这是 C,你必须预先确定,除非你有时间玩指针、地址、malloc、free 等的痛苦,只是为了一个简单的函数返回。干杯。
0赞 ekipan 8/2/2021
@MinhTran 引用 godbolt.org/z/1rYocv3PT - 实质上被转换为一个函数,该函数接受要存储的地址。您可以看到 Mem2 () 在堆栈上保留了 32 个字节,并通过 将其地址传递给 。我真的不知道我的调用约定,但我认为通常是函数的第一个参数。 然后将其结果存储在那里并返回相同的地址 ()。ring_slicemainsub rsp, 32rdiring_slicerdiring_slicemov rax, rdi