【问题标题】:Returning an array using C使用 C 返回一个数组
【发布时间】:2018-04-08 14:51:26
【问题描述】:

我对 C 语言比较陌生,在处理数组的方法方面需要一些帮助。来自 Java 编程,我习惯于说 int [] method() 以返回一个数组。但是,我发现使用 C 语言时,您必须在返回数组时使用指针。作为一个新程序员,我真的一点也不明白,即使我浏览过很多论坛。

基本上,我正在尝试在 C 中编写一个返回 char 数组的方法。我将为该方法(我们称之为 returnArray)提供一个数组。它将从前一个数组创建一个新数组并返回一个指向它的指针。我只需要一些帮助来了解如何开始以及如何在指针被发送出数组后读取指针。任何解释这一点的帮助表示赞赏。

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

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

函数调用者

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 中的工作方式,您将不知道它有多大。
  • 是的,我一直都知道传入数组的大小。输入和输出数组的大小不会改变。

标签: c arrays pointers char


【解决方案1】:

您不能从 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)
}

【讨论】:

  • 选项3:(静态数组)
  • @moooeeeep:是的,为了简单起见,我故意省略了它,但是是的,您可以返回指向从函数内声明的静态数据的指针。
  • @user1506919:我实际上更喜欢选项 2,因为很清楚谁分配和取消分配内存,但我会为您添加一个示例。
  • 选项 4:返回一个包含固定大小数组的结构体。
  • 选项5:返回一个包含固定大小数组的联合。
【解决方案2】:

您可以像此处报告的其他答案一样使用堆内存(通过 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 个字节,因此请适当调整大小!

【讨论】:

  • 分发指向函数内部存储器的指针有多好?忘了,多线程,这在串行代码中很糟糕。
【解决方案3】:

这个邪恶的实现怎么样?

array.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;    \
}

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

【讨论】:

  • 这道菜好吃到让我好奇。你能多解释一下你在上面做了什么,或者建议你读一读你所说的这种美味吗?提前致谢。
  • @Unheilig - 请注意,这里有一些潜在的错误,这只是概念的证明。也就是说,诀窍是将struct 作为数组容器/对象返回。把它想象成一个 C++ std::vector。预处理器会将 int 版本扩展为 struct intArray { int* contents; int size; };
  • 我喜欢这种方法。亲:这是通用解决方案;对比:内存密集型解决方案。对于已知大小的向量不是最优的。无论如何,这可以通过初始大小分配进行升级。我会明确添加一些分配检查。非常好的建议开始:)
  • 面向对象的 esk prepossessing mix-mash。我喜欢它。
  • 美丽。我印象深刻
【解决方案4】:

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

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

}

【讨论】:

  • C 中没有new 运算符。那是C++。
  • sizeof(char) 保证为1,因此在这种情况下,您可以从malloc 中删除该位。
  • 好的,如果我想打印出新数组的内容,我可以只做我的'printf'语句,但用'arr'替换'returnedArray'吗?
  • 您没有正确调用函数(签名需要两个参数时只有一个参数)。
  • 你传入&amp;arr。您希望 arr 成为 char *,并使用 arr 传递它。
【解决方案5】:

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

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

我邀请 cmets 讨论这项技术的优缺点。我懒得这么做。

【讨论】:

  • 不清楚为什么这不是公认的答案。问题不在于是否可以返回指向数组的指针。
  • CHAR_ARRAY returned分配的内存是否在堆上?它肯定不能在堆栈上(在returnArray() 的堆栈框架中对吗?
  • 是的,这是我的问题的答案:C 函数可以返回一个数组吗?是的,它可以,@Indinfer 使用 C 自己的 struc 数据类型来回答它。当然它应该是固定长度的数组。这是 C,除非您有时间为简单的函数返回而玩指针、地址、malloc、free 等的痛苦,否则您必须预先确定性。干杯。
  • @MinhTran 参考 godbolt.org/z/1rYocv3PT - 本质上,ring_slice 被转换为一个接受要存储的地址的函数。您可以看到main 在堆栈上为 Mem2 (sub rsp, 32) 保留了 32 个字节,并将其地址通过rdi 传递给ring_slice。我真的不知道我的调用约定,但我认为rdi 通常是函数的第一个参数。 ring_slice 然后将其结果存储在那里并返回相同的地址 (mov rax, rdi)。
【解决方案6】:

C 对数组的处理非常与 Java 不同,你必须相应地调整你的想法。 C 中的数组不是一等对象(也就是说,在大多数情况下,数组表达式不保留它的“数组特性”)。在 C 中,“T 的 N 元素数组”类型的表达式将被隐式转换(“衰减”)为“指向 T 的指针”类型的表达式,除非数组表达式是 @ 的操作数987654325@ 或一元 &amp; 运算符,或者如果数组表达式是用于在声明中初始化另一个数组的字符串文字。

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

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

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

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

如果您真的感兴趣,可以阅读 Dennis Ritchie 的 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 库函数释放数组。

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

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

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 是一个编译时常量,这意味着该函数只能使用一个数组大小。其次,您必须在应用下标之前取消引用指针,这会使代码混乱。在处理多维数组时,指向数组的指针效果更好。

【讨论】:

  • 您与“C 的开发”的链接已断开...看起来它应该将我们指向这里:bell-labs.com/usr/dmr/www/chist.html
  • @Kundor:bar 收到的是指针,而不是数组。在函数参数声明的上下文中,T a[N]T a[] 都被视为 T *a
  • @JohnBode:你是对的!出于某种原因,我认为固定大小的数组是在堆栈上传递的。我记得很多年前的一个场合,当我发现必须在参数签名中指定数组的大小时,我一定很困惑。
  • @JohnBode ,在第二个代码部分第一行:void returnArray(const char *srcArray, size_t srcSize, char *dstArray, char dstSize) 最后一个参数应该是size_t 类型而不是char
【解决方案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 来释放内存。

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

【讨论】:

    【解决方案8】:

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

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

    【讨论】:

      猜你喜欢
      • 2021-05-11
      • 1970-01-01
      • 2016-11-20
      • 2012-04-01
      • 2012-07-05
      • 2011-01-25
      相关资源
      最近更新 更多