【问题标题】:C++ - Regarding the scope of dynamic arraysC++ - 关于动态数组的范围
【发布时间】:2012-07-19 06:34:43
【问题描述】:

我有一个关于动态数组范围的快速问题,我认为这会导致我正在编写的程序中出现错误。这个 sn-p 检查函数参数并根据用户传递的内容分支到第一个或第二个。

但是,当我运行程序时,我得到了一个范围相关的错误:

错误:“数组”未在此范围内声明

除非我对 C++ 的了解使我失败,否则我知道在分支完成时在条件内创建的变量会超出范围。但是,我动态分配了这些数组,所以我不明白为什么我不能在程序的后面操作这些数组,因为指针应该保留。

        //Prepare to store integers
        if (flag == 1) {
                int *Array;
                Array = new int[input.length()]; 
            } 
        //Prepare to store chars
        else if (flag == 2) {
                char *Array;
                Array = new char[input.length()];
            }

有人能解释一下吗?

【问题讨论】:

  • 由于这里的 cmets,我可能最终会更改此方法的整个结构。感谢所有帮助和各种解决方案!

标签: c++ pointers scope


【解决方案1】:

在if 之前声明Array。而且你不能将不同类型的数组声明为一个变量,所以我认为你应该使用指针。

int *char_array = nullptr;
int *int_array  = nullptr;

//Prepare to store integers
if (flag == 1) {
    int_array = new int[input.length()]; 
} 
//Prepare to store chars
else if (flag == 2) {
    char_array = new char[input.length()];
}

if (char_array)
{
    //do something with char_array
}
else if (int_array)
{
    //do something with int_array
}

正如 j_random_hacker 指出的那样,您可能希望更改程序设计以避免大量 if

【讨论】:

  • 这不适用于第二个路径,它通知使用字符数组代替。
【解决方案2】:

虽然你是对的,因为你是在堆上动态分配它们的,所以在你明确删除它(或程序结束)之前,内存不会释放给系统,指针指向当它声明的块退出时,内存超出范围。因此,如果要在块之后使用您的指针,则它们需要存在于更广泛的范围内。

【讨论】:

  • 您是对的,但您的答案与您在 Andrew 的答案中指出的问题相同,您显然不赞成...?
  • @j_random_hacker 我试图更笼统地回答他/她的问题,因为他似乎不理解指针和内存的区别。 (只是添加了“(s)”以使其更加明显。)我还认为安德鲁会给出代码。 (是的,我在他修好后投了赞成票。)
【解决方案3】:

内存仍然分配(即占用宝贵的空间),在关闭} 后无法访问它,因为此时程序失去了处理它的能力。为避免这种情况,您需要将new[] 返回的指针分配给在外部作用域中声明的指针变量。

作为一个单独的问题,您似乎正在尝试分配两种不同类型之一的内存。如果您想以便携方式执行此操作,则必须使用void * 来保存指针,或者(不太常见)包含每种类型指针的union 类型。无论哪种方式,您都需要维护状态信息,让程序知道已经进行了哪种分配。 通常,想要这样做表明设计不佳,因为每次访问都需要打开此状态信息。

【讨论】:

    【解决方案4】:

    如果我正确理解您的意图,您正在尝试做的是:根据某些逻辑分配内存来存储 int 或 char 的 n 元素,然后在你的函数中 以int 或char 的形式访问该数组,而无需单个if 语句。

    如果上面的理解是正确的,那么简单的答案就是:“C++是一种强类型语言,你想要的东西是不可能的”。

    但是... C++ 也是一种极其强大和灵活的语言,所以可以这样做:

    铸造。类似于以下内容:

    void * Array;
    if(flag1) Array = new int[len]
    else Array = new char[len];
    // ... later in the function
    if(flag) // access as int array
      int i = ((int*)Array)[0];
    

    是的,这很丑陋,您必须在函数周围撒上ifs。所以这里有一个替代方案:模板

    template<class T> T foo(size_t _len)
    {
      T* Array = new T[_len];
      T element = Array[0];
      return element;
    }
    

    另一种更晦涩的做事方式可能是使用unions:

    union int_or_char {int i; char c;};
    int_or_char *Array = new int_or_char[len];
    if(flag) // access as int
      int element = Array[0].i;
    

    但无论如何(或第三种),编译器必须知道如何处理您尝试处理的数据这一事实是无法解决的。

    【讨论】:

    • 你仍然需要创建模板数组来指定它的类型。它不会消除如果
    • @Andrew:是的,但是您可以编写all需要将数组操作为函数模板的代码(即使类型之间的行为有时不同,这也可以通过编写函数模板并专门化它们来处理)。那么一个顶级的if (flag == 1) use&lt;int&gt;(); else use&lt;char&gt;(); 就足够了。
    【解决方案5】:

    Turix 的回答是正确的。您需要记住,这里分配了两件事,数组的内存和存储数组位置时的内存。

    因此,即使数组中的内存是从堆中分配的,并且可以在任何需要的地方用于代码,但存储数组位置的内存(数组变量)是在堆栈中分配的,并且将一旦超出范围就会丢失。在这种情况下,当 if 块结束时。你甚至不能在同一个 if 的 else 部分使用它。

    我要给出的另一个来自 Andrew 的不同代码建议是:

    void *Array = nullptr;
    
    if (flag == 1) {
        Array = new int[input.length()];
    } else if (flag == 2) {
        Array = new char[input.length()];
    }
    

    那你就可以直接使用 if 了。

    这部分我不确定:如果您想知道它是 int 还是 char,您可以使用 typeid 文字。 不起作用,至少我无法获得它可以工作。

    或者,您可以使用标志变量来猜测它是什么类型。

    【讨论】:

    • 这里建议如何使用typeid?
    • typeid 仅适用于 (a) 静态可知类型(例如 typeid (int))或 (b) 基类的指针或引用实际指向的几个派生类中的哪一个,以及即使这样,它也要求基类是多态的(具有 >= 1 个虚函数)。它不可能知道void * 指向的地方生活着什么。否则很好。
    • 嗯,我不确定 typeid 是如何工作的,但是 wiki 说它在运行时确定类型.. 所以理论上它可能知道哪种对象位于特定的内存位置。但这似乎不起作用。我会把它剪掉。
    • typeid 如何知道一个字节是否包含char、signed char 或unsigned char?没有人在该内存位置上贴便利贴;)(这是 C++ BTW 中的 3 种不同类型。)
    猜你喜欢
    • 2016-12-06
    • 2013-04-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多