提问人:user1506919 提问时间:7/26/2012 最后编辑:Peter Mortensenuser1506919 更新时间:11/4/2022 访问量:548697
使用 C 返回数组
Returning an array using C
问:
我对 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 编译器目前无法正常工作,但我想弄清楚这一点。
答:
在您的例子中,您正在堆栈上创建一个数组,一旦您离开函数范围,该数组将被释放。相反,创建一个动态分配的数组并返回指向它的指针。
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);
}
评论
new
sizeof(char)
1
malloc
&arr
arr
char *
arr
您的方法将返回一个局部堆栈变量,该变量将严重失败。要返回数组,请在函数外部创建一个数组,按地址将其传递到函数中,然后对其进行修改,或者在堆上创建一个数组并返回该变量。两者都可以工作,但第一个不需要任何动态内存分配即可使其正常工作。
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);
}
不能从 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)
}
评论
您可以像这样使用代码:
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 来释放内存。
还有其他选择。例程可能会返回指向数组(或数组的一部分)的指针,该数组是某些现有结构的一部分。调用方可能会传递一个数组,而例程只是写入数组,而不是为新数组分配空间。
C 对数组的处理方式与 Java 的处理方式非常不同,您必须相应地调整您的思维方式。C 语言中的数组不是第一类对象(也就是说,数组表达式在大多数上下文中不保留其“数组性”)。在 C 中,类型为 “N-element array of ” 的表达式将被隐式转换为 “pointer to” 类型的表达式,除非数组表达式是 or 一元运算符的操作数,或者数组表达式是用于初始化声明中另一个数组的字符串文本。T
T
sizeof
&
除其他事项外,这意味着您不能将数组表达式传递给函数并将其作为数组类型接收;该函数实际上接收指针类型:
void foo(char *a, size_t asize)
{
// do something with a
}
int bar(void)
{
char str[6] = "Hello";
foo(str, sizeof str);
}
在对 的调用中,表达式从 类型转换为 ,这就是为什么声明第一个参数 而不是 。在 中,由于数组表达式是运算符的操作数,因此它不会转换为指针类型,因此您可以获得数组中的字节数 (6)。foo
str
char [6]
char *
foo
char *a
char a[6]
sizeof str
sizeof
如果你真的有兴趣,你可以阅读丹尼斯·里奇(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 个元素(即使只有数组类型)。dst
char
char
[]
src[i]
dst[i]
i
src
您可以声明一个指向 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
评论
bar
T a[N]
T a[]
T *a
void returnArray(const char *srcArray, size_t srcSize, char *dstArray, char dstSize)
size_t
char
使用这个美味的邪恶实现:
数组.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;
}
评论
struct
int
struct intArray { int* contents; int size; };
你可以像这里报告的其他答案一样使用堆内存(通过 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 字节,因此请正确调整其大小!
评论
我并不是说这是给定问题的最佳解决方案或首选解决方案。但是,记住函数可以返回结构可能很有用。虽然函数不能返回数组,但数组可以包装在结构体中,函数可以返回结构体,从而携带数组。这适用于固定长度的数组。
#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;
}
评论
CHAR_ARRAY returned
returnArray()
ring_slice
main
sub rsp, 32
rdi
ring_slice
rdi
ring_slice
mov rax, rdi
评论