【问题标题】:How to access a local variable from a different function using pointers?如何使用指针从不同的函数访问局部变量?
【发布时间】:2011-06-02 00:07:54
【问题描述】:

我可以访问不同函数中的局部变量吗?如果有,怎么做?

void replaceNumberAndPrint(int array[3]) {
    printf("%i\n", array[1]);
    printf("%i\n", array[1]);
}

int * getArray() {
    int myArray[3] = {4, 65, 23};
    return myArray;
}

int main() {
    replaceNumberAndPrint(getArray());
}

上面这段代码的输出:

65
4202656

我做错了什么? “4202656”是什么意思?

我是否必须复制replaceNumberAndPrint() 函数中的整个数组才能比第一次访问更多?

【问题讨论】:

  • 很难知道对你的意图最好的建议,在这里。但是您可能想阅读共享指针(shared_ptr 和朋友)。它们通过引用计数提供了垃圾收集语言的一些不错的属性。但不同,所以要小心。
  • 我投票决定重新打开它并将其用作规范副本,以解决“我在返回指向局部变量的指针时崩溃,为什么?”的性质,而不是 @987654321 @ 经典的规范副本,而是“我没有崩溃,为什么不呢?”
  • @Lundin 有点问题,它同时具有 C 和 C++。
  • @AnttiHaapala 问题中没有 C++ 独有的东西,但不幸的是有 C++ 答案,所以标签必须保留。
  • 如果在嵌入式系统上使用,那么动态分配内存是危险的,所以有 3 种可能性,使变量成为全局变量,使其成为静态变量,或者从调用例程传入指向变量的指针。

标签: c++ c pointers local-variables


【解决方案1】:

myArray 是一个局部变量,因此指针仅在其作用域结束之前有效(在本例中为包含函数getArray)。如果您稍后访问它,您会得到未定义的行为。

实际上,对printf 的调用会覆盖myArray 使用的堆栈部分,然后它会包含一些其他数据。

要修复您的代码,您需要在一个足够长的范围内声明数组(您的示例中的 main 函数)或在堆上分配它。如果你在堆上分配它,你需要手动释放它,或者在 C++ 中使用 RAII。

我错过的另一种选择(可能是这里最好的一种,前提是数组不太大)是将数组包装到结构中,从而使其成为值类型。然后返回它会创建一个在函数返回后仍然存在的副本。有关详细信息,请参阅 tp1answer

【讨论】:

  • 或者你可以声明它是静态的。
  • 将其设为静态具有完全不同的语义,尤其是在多线程应用程序中,除非数组的内容是常量。
  • 可以,但这是解决问题的方法,应该指出来,对吧?
  • 指出来固然有用,但你也需要指出缺点,所以你的第一条评论有点不完整。
  • 我会说推荐 static 作为解决这个问题的方法是有害的,而且从来没有帮助。对于新手来说,它变成了一个公式化的创可贴,代替了真正理解问题和编写正确的代码,后来当其他人继承了新手的代码时,他们会对到处都是无意义的静态变量感到恐惧。跨度>
【解决方案2】:

一旦超出范围,您将无法访问局部变量。这就是局部变量的含义。

当您在replaceNumberAndPrint 函数中访问数组时,结果未定义。它似乎第一次起作用的事实只是一个幸运的巧合。可能您指向的内存位置在堆栈上未分配并且仍然为第一次调用正确设置,但是对printf 的调用然后通过在其操作期间将值压入堆栈来覆盖它,这就是为什么第二次调用@ 987654323@ 显示不同的东西。

您需要将数组数据存储在堆上并传递一个指针,或者存储在一个仍在作用域内的变量中(例如,一个全局变量或主函数内的某个变量)。

【讨论】:

    【解决方案3】:

    尝试类似的方法。你这样做的方式“杀死”myArray 原因,如果它是本地定义的。

    #include <stdio.h>
    #include <stdlib.h>
    
    void replaceNumberAndPrint(int * array) {
     printf("%i\n", array[0]);
     printf("%i\n", array[1]);
     printf("%i\n" , array[2]);
     free(array);
    }
    
    int * getArray() {
     int * myArray = malloc(sizeof(int) * 3);
     myArray[0] = 4;
     myArray[1] = 64;
     myArray[2] = 23;
     //{4, 65, 23};
     return myArray;
    }
    
    int main() {
     replaceNumberAndPrint(getArray());
    }
    

    更多:http://www.cplusplus.com/reference/clibrary/cstdlib/malloc/

    编辑: 正如评论正确指出的那样:更好的方法是:

    #include <stdio.h>
    #include <stdlib.h>
    
    void replaceNumberAndPrint(int * array) {
        if(!array)
            return;
    
        printf("%i\n", array[0]);
        printf("%i\n", array[1]);
        printf("%i\n" , array[2]);
    }
    
    int * createArray() {
        int * myArray = malloc(sizeof(int) * 3);
    
        if(!myArray)
            return 0;
    
        myArray[0] = 4;
        myArray[1] = 64;
        myArray[2] = 23;
        return myArray;
    }
    
    int main() {
        int * array = createArray();
        if(array)
        {
            replaceNumberAndPrint(array);
            free(array);
        }
        return 0;
    }
    

    【讨论】:

    • 如果您在代码中使用此“功能”,请务必评论此功能。由一个函数分配内存并由另一个函数释放它是非常危险的,除非正确记录,即使这样也容易发生内存泄漏!最好将数组分配在main 中,并在不再需要时释放它。
    • @Shaihi,这是真的,而且这段代码非常幼稚,因为它没有检查malloc()是否分配成功。但我认为 OP 会理解整点。
    • 虽然这可行,但它很丑。您应该更改函数名称(getArray => createArray)以更好地描述它们的行为。让replaceNumberAndPrint 删除源数组对我来说似乎不是一个好主意。我宁愿将删除和打印分成两个不同的功能。
    • 添加了一个更“正确”的版本。
    • @Muggen:你不喜欢array[i]而不是*(array+i)吗?
    【解决方案4】:

    一旦您离开 getArray,myArray 就会超出范围。您需要在堆上为其分配空间。

    【讨论】:

      【解决方案5】:

      您的代码调用未定义行为,因为一旦getArray() 返回并且任何尝试使用(取消引用)悬空指针都是 UB,myArray 就会超出范围。

      【讨论】:

        【解决方案6】:

        局部变量在返回时超出范围,因此您不能返回指向局部变量的指针。

        您需要使用mallocnew 动态分配它(在堆上)。示例:

        int *create_array(void) {
            int *array = malloc(3 * sizeof(int));
            assert(array != NULL);
            array[0] = 4;
            array[1] = 65;
            array[2] = 23;
            return array;
         }
         void destroy_array(int *array) {
             free(array);
         }
         int main(int argc, char **argv) {
             int *array = create_array();
             for (size_t i = 0; i < 3; ++i)
                 printf("%d\n", array[i]);
             destroy_array(array);
             return 0;
         }
        

        或者,您可以将数组声明为静态,记住语义不同。示例:

        int *get_array(void) {
            static int array[] = { 4, 65, 23 };
            return array;
         }
         int main(int argc, char **argv) {
             int *array = get_array();
             for (size_t i = 0; i < 3; ++i)
                 printf("%d\n", array[i]);
             return 0;
         }
        

        如果您不知道static 的含义,请阅读this question & answer

        【讨论】:

          【解决方案7】:

          正确的做法如下:

          struct Arr {
             int array[3];
          };
          Arr get_array() {
             Arr a;
             a.array[0] = 4;
             a.array[1] = 65;
             a.array[2] = 23;
             return a;
          }
          int main(int argc, char **argv) {
             Arr a = get_array();
             for(size_t i=0; i<3; i++)
                 printf("%d\n", a.array[i]);
             return 0;
          }
          

          要了解为什么需要这样做,您需要了解 sizeof(array) 的工作原理。 C(以及因此 C++)努力避免复制数组,并且您需要 struct 越过它。为什么需要复制是因为作用域—— get_array() 函数的作用域消失了,该作用域中仍然需要的每个值都需要复制到调用作用域。

          【讨论】:

          • 我知道这是很久以前的事了,但是您不需要typedef 或像struct Arr a 这样分配结构吗?
          • @sherrellbc 在 C 中,是的。在 C++ 中 struct Foo { ... }; 自动定义类型 struct Foo Foo,不需要typedef
          【解决方案8】:

          C++ 解决方案:

          “我可以访问不同函数中的局部变量吗?如果可以,如何访问?”

          答案是否定的,不是在函数结束后。局部变量在此时被销毁。

          C++ 中处理返回数组的方法是在容器 中管理它们,例如std::array(固定大小)或std::vector(动态大小)。

          例如:

          void replaceNumberAndPrint(const std::array<int, 3>& array) {
              printf("%i\n", array[0]);
              printf("%i\n", array[1]);
              printf("%i\n", array[2]);
          }
          
          std::array<int, 3> getArray() {
              std::array<int, 3> myArray = {4, 65, 23};
              return myArray;
          }
          

          在第二个函数中,返回值由编译器优化,因此您无需支付实际复制数组的代价。

          【讨论】:

            【解决方案9】:

            在这段代码中,您使用了指向局部对象的指针,但是当函数返回时,所有局部变量都超出了范围。如果您将分配内存(使用malloc() 函数进行分配),则不会丢失或覆盖任何数据。

            int* getArray(int size) {
                int *myArray = (int*)malloc(size*sizeof(int));
                myArray[0] = 4;
                myArray[1] = 65;
                myArray[2] = 23;
                return myArray;
            }
            
            int main() {
                int i;
                int *vector = getArray(3);
                for(i=0;i<3;i++)
                {
                    printf("%i\n",vector[i]);
                }
                getch();
                return 0;
            }
            

            此代码将打印所有数组元素,不会发生覆盖。

            【讨论】:

              【解决方案10】:

              Static ..or.. Global 中的 .c 就可以了;)

              但是程序将占用这 3 个字节的整个时间,但是您避免在这样的简单事情上执行 malloc(建议将 malloc 用于大型数组)

              另一方面,如果外部函数修改指针,那么内部的'myArray'将被修改,因为它指向它,就是这样

              int myArray[3];
              int * getArray() {
                  myArray[0] = 4;
                  myArray[1] = 65;
                  myArray[2] = 23;
                  return myArray;
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 2011-04-15
                • 2013-01-14
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多