如何从C中的函数返回数组?

时间:2016-10-06 13:23:40

标签: c arrays pointers

大多数网站都这样说:

  

C编程不允许将整个数组作为参数返回   一个功能。但是,您可以通过返回指向数组的指针   指定没有索引的数组名称。

我刚从指针开始,据我所知,指针变量是一个存储内存地址的变量。当我们使用*取消引用它时,我们得到那个内存地址并保存那里存储的值。此外,在数组的情况下,指针必须指向第一个元素。

现在,如果我们的函数返回一个指向数组第一个元素的指针,如下例所示:

int * myFunction() {
   .
   .
   .
}
  1. 我们要取消引用它的内容是什么?
  2. "函数返回指针"是指它返回指针指向的内存地址?
  3. 在那种情况下,

      

    要记住的第二点是C不提倡返回   函数外部的局部变量的地址,所以你会这样做   必须将局部变量定义为静态变量

    1. 完全是一个静态变量? [我冲浪得足够,但没有发现任何令人满意的事情]。维基百科定义:
    2.   

      在计算机编程中,静态变量是一个变量   静态分配,以便其生命周期或"范围"延伸到   整个计划的运行。

      另一个网站说,

        

      静态存储类指示编译器保留本地   在程序的生命周期中存在的变量而不是   每次进入和离开时创建和销毁它   范围。

      有人请给我一个明确的基本解释,静态变量究竟是什么以及它在这个上下文中的相关性(从函数返回数组)。

      我真的很困惑。

3 个答案:

答案 0 :(得分:1)

  

我们将取消引用它的内容是什么以及如何解决?

函数返回的指针变量。

通过使用适当的运算符*,示例:

int z = 5;
int* pointer_to_z = &z; // get memory address of z and store that in pointer_to_z
int can_i_have_my_z_back_please = *z; // check what value is at that memory address and store it, a copy is made here.
  

“返回指针的函数”是否表示它正在返回   指针指向的内存地址?

它返回一个指针变量,该变量保存值的内存地址。基本上,“指向”一个值与“拥有它的地址”相同。

  

究竟什么是静态变量? [我冲浪得足够但没找到   任何令人满意的事情。

关于静态变量已经存在很多好的SO答案。总结(仅进入变量的生命周期而不是它的链接)静态变量一旦初始化就对程序的其余部分有效,这意味着它的生命周期不像局部变量那样受范围限制:

void hello()
{
  int x = 5;
} // x destroyed here..

void hello_static()
{
  static int x = 5;
} // x will only be destroyed at the "end" of the program

这反过来意味着返回本地静态变量的指针(内存地址)是完全安全的,因为静态变量仍然可以访问:

int* return_my_static()
{
  static int a = 5;
  return &a;
}

int main()
{
  int* pointer_to_static = return_my_static(); // get the memory address of the static
  printf("%i", *pointer_to_static); // print out the value by dereferencing
}

但是,对于本地非静态变量这样做会导致未定义的行为,因为指向的变量(它的内存地址)不再有效,因为它已被销毁:

int* return_local()
{
  int a = 5;
  return &a;
} // a is destroyed here.. oopsies

int main()
{
     int* pointer_to_local = return_local(); // get the memory address of the local.
     //local variable has been destroyed now and 'pointer_to_static' now points to garbage memory!
      printf("%i", *pointer_to_local); // Try to print out the value by dereferencing, this is undefined behaviour, anything can happen.
}

请注意,上面的代码可能会运行并输出预期的结果,但这很幸运,这是未定义的行为,应该不惜一切代价避免,因为此时可能发生任何事情。

答案 1 :(得分:0)

问题3:

int a;

void foo() {
   int b
   static int c;
}

int main() {
    foo();
}

当程序启动时,a和c的内存被分配并保持分配,直到程序退出。因此,在任何给定时间,确切地存在于a和c上。每当有人(这里是主要的)调用foo时,b就被分配在堆栈上,直到该函数返回。

关于问题3前面的引用:

返回a和c的地址没有问题,因为只要程序持续存在就会存在,但是将地址返回给b是一个错误,因为只要调用者手中拿到指针,指针就会指向内存无效。

问题1:

通过在前面放一个星号来取消引用指针。指针指向的并不重要。它是一个数组,你可以递增或递减指针以获得你想要达到的索引,就像一个简单的补充:*(p + 4)将访问第5个元素(因为*(p + 0)是第一个,所以*(p + 1)是第二个,依此类推。)

您可以写p [4]而不是写*(p + 4)。

所以假设你的功能看起来像这样:

int * myFunction() {
    static int array[8];
    return array;
}

然后return语句将返回数组的地址,该地址与数组的第一个元素的地址完全相同。

所以,有int * p = myFunction();然后,您可以使用直观的语法p [0] = 42访问该数组; ...; p [7] = 23;

问题2:

返回指针的函数是返回指针的函数。指针是一个东西,它指向内存中的一个点。通常称为内存地址,但C语言并不关心。所以,实际上,“返回指针的函数”意味着它返回指针所指向的内存地址,是的。

答案 2 :(得分:0)

回到前面:

  

静态变量究竟是什么?

static存储持续时间的对象在程序首次启动时为其分配了存储空间,并且该存储一直保持到程序退出为止。如果对象具有static存储持续时间:

  • 其标识符在文件范围内声明(在任何函数的主体之外);或
  • 其标识符使用static关键字
  • 声明

示例:

#include <stdio.h>

int g_var;                      // static storage duration

void foo( void )
{
  static int s_var = 10;        // static storage duration
  int l_var = 10;               // auto storage duration

  printf( "g_var = %d, s_var = %d, l_var = %d\n", g_var++, s_var++, l_var );
}

在此代码段中,g_vars_var都有static个存储时间; g_var因为它是在文件范围s_var声明的,因为它是使用static关键字声明的。凭借static存储持续时间,g_var被隐式初始化为0.请注意s_var在程序启动时初始化一次 - 它将 not 在后续调用10时重新初始化为foo。因此,每次调用foo时,输出都将是

g_var = 0, s_var = 10, l_var = 10
g_var = 1, s_var = 11, l_var = 10
g_var = 2, s_var = 12, l_var = 10
...

l_var的存储时间为auto - 其生命周期仅限于foo函数的生命周期。每次调用foo时, l_var实例的存储都会在函数入口处分配和初始化,并在函数退出时释放。这很重要 - 当l_var退出时,foo 不再存在,因此当foo返回<时,指向它的任何指针都将变为无效 SUP> 1

这就是为什么你不能做像

这样的事情
int * bar( void )
{
  int array[N];
  ...
  return array;
}

因为array bar退出后不再存在,并且返回的指针无效

现在,解决这个问题的一种方法是将数组声明为static

int * bar( void )
{
  static int array[N];
  ...
  return array;
}

在这种情况下,当函数退出时array不会消失,因此指针仍然有效。

...然而

这会产生其他问题。只创建了一个array实例,它包含了另一个bar调用写入的最后一个实例。代码不再是 re-entrant ;它不能在执行中期安全地中断,然后在第一次调用完成之前由另一个函数调用。创建一个static数组只是为了让你可以干净地返回指向它的指针通常错误的答案。

将目标数组作为参数传递给函数:

void foo( int *array, size_t arraySize )
{
  ...
  array[i] = some_value;
  ...
}

动态分配一个数组并将指针返回给它:

int * bar( void )
{
  int *array = malloc( sizeof *array * N );
  if ( array )
  {
    // initialize array contents
  }
  return array;
}

这个问题是其他人负责在你完成后释放那些记忆。

  

“返回指针的函数”是否意味着它返回指针指向的内存地址?

该函数返回指针的,这是另一个对象的地址。在上面的代码中,bar返回表达式 array的值,结果是array的第一个元素的地址。

在上面bar的第二种情况中,返回的值等于&array[0]

  

我们将取消引用它的内容是什么以及如何解决?

您可以通过两种方式取消引用指针 - 使用*取消引用运算符,或使用[]下标运算符。

下标操作a[i] 定义*(a + i) - 给定地址a,偏移i元素(不是字节< / strong>)来自a并取消引用结果。因此,您可以从bar返回指针,并使用它执行以下操作:

int *p = bar();

printf( "p[0] = %d\n", *p );
printf( "p[0] = %d\n", *(p + 0) );
printf( "p[0] = %d\n", p[0] );

那么,这是否意味着数组和指针是一回事? 没有。数组不是指针;但是,在大多数情况下,数组表达式(即“{元素数组T”类型的表达式)将被转换(“衰减”)为指针表达式(“指向T“的指针。

<小时/>

  1. 显然,l_var占用的内存位置仍然存在,因此指针的值不会突然变成垃圾或类似的东西;但是,该内存位置现在可供其他人使用,如果您尝试读取或写入该位置,则可能会导致问题。