【问题标题】:What happens to array[1]数组[1]会发生什么
【发布时间】:2013-10-03 15:03:30
【问题描述】:

我有一大段代码,旨在获取一个数组并处理它。在当前项目中只有一个元素,因此我没有将变量更改为 char,而是将其声明为 char array[1]。这样我就不需要修改我的代码和冒险添加任何错误,并且可以在需求增长时轻松增加它。

它似乎可以编译,但我对引擎盖下发生的事情感到好奇,我是在浪费内存吗?这是否增加了额外的处理时间,编译器会优化它,所以如果我输入它也不会有什么不同?

任何人都可以用这种方式解释使用数组的任何可能的缺点。

我使用c和c++,它们之间有什么不同吗?

【问题讨论】:

    标签: c++ c arrays optimization


    【解决方案1】:

    听起来是个不错的策略,而且没有缺点。您绝对不会在 C 或 C++ 中浪费内存。大小为 1 的数组占用的内存与同类型变量占用的内存相同。

    编译器可能会生成微观上效率较低的代码,但这真的不值得担心。

    【讨论】:

      【解决方案2】:

      标准规定您可以获取不是数组的对象的地址并将其视为大小为 1 的数组(因此您可以获取指向过去的指针)。

      请参阅 C++11 标准的第 5.7.4 节:

      对于这些运算符,指向非数组对象的指针 行为与指向数组的第一个元素的指针相同 长度为一,对象的类型作为其元素类型。

      【讨论】:

        【解决方案3】:

        首先,你的代码是有效的,但如果你关心缺点,我可以看到下面列出的问题:

        使用数组,当您不小心循环数组时,您会增加通过越界访问访问它的机会。

        另一个缺点是数组不与多态性交互。有时您尝试将派生对象存储到基本类型的数组中,对象将被切片而您可能没有注意到。

        所以我不会编写 array[1] 代码。希望这能回答您的一些问题。

        【讨论】:

        • +1 可能的循环错误。我希望特别是在 C 中。
        • 好点,但由于 OP 说基于数组的代码已经存在,我认为可能的越界错误是尝试 array[1] 想法的奖励,因为它可能会捕获错误尚未找到的通用array[n] 代码。
        • 数组如何以非数组变量引起的方式导致切片?考虑 A 派生自 B。 B b,b2[1]; b = 一个; b2[0] = 一个;你在这两个任务中都得到了切片。你不会在 B b3& = a; 中切片。但这是一个参考,而不是开场问题的内容。同样的推理也适用于多态性。
        • 我记得在尝试(有意)使用-D_FORTIFY_SOURCE (gcc/glibc)超越char[n] 时出现了一些奇怪的问题。这个选项显然告诉 GCC 插入运行时标记/边界检查
        【解决方案4】:

        这里我们面临 2 块代码。

        • 设计用于获取数组并工作的大型代码块 通过这。
        • 一段代码使用前一个代码块 一些数据。

        构建您的代码。

        大块代码应该是一个函数,可能分为几个子函数。 另一段代码将调用此函数。

        函数的参数。数组或单个字符。

          (a) void work( char  c );
          (b) void work( char& c );
          (c) void work( const char v[], size_t size);
          (d) void work(       char v[], size_t size);
        

        如果这种工作对数组没有意义,则应使用选项 (a) 和 (b)。事实并非如此。
        如果工作对数组有意义,则应使用选项 (c) 和 (d)。

        所以使用数组。

        保存数据的变量。数组或单个字符。

        如果您只需要保存单个字符,则使用单个非数组字符。你仍然可以调用数组函数。

        char c;
        c = 'b';
        work( &c, 1 );
        //////
        char a[1];
        a[0] = 'b';
        work( a, 1 );
        

        work 函数将单个变量和数组视为大小为 1 的数组。代码在这两种情况下都可以正常工作,并且没有效率问题。

        测试

        让我们看看真实的代码是否包含我之前的陈述。

        #include <iostream>
        #include <ctime>
        #include <vector>
        #include <cstddef>
        #include <chrono>
        
        using namespace std;
        
        unsigned long miliTime()
        {
            return std::chrono::system_clock::now().time_since_epoch() /
                   std::chrono::milliseconds(1);
        }
        // An hypotetical work function with arrays
        void workArray( char v[], size_t size )
        {
          for ( size_t n=0; n<size; ++n )
          {
            // large block of code
            for ( int i=0; i<1000; ++i )
            {
              v[n] += 3 + i;
              if (v[n] == '3' )
                v[n] = 'J' - v[n];
              v[n] = toupper( v[n] ) + '-';
            }
          }
        }
        
        // Same function just for a single character
        void workSingle( char& c )
        {
          // large block of code
          for ( int i=0; i<1000; ++i )
          {
            c += 3 + i;
            if (c == '3' )
              c = 'J' - c;
            c = toupper( c ) + '-';
          }
        }
        
        int main(void)
        {
          const long int repeats =1000000;
          long int n;
          unsigned long start;
          double dif;
        
          start = miliTime();
          char c;
          c = 'b';
          for ( n=0; n<repeats; ++n)
            workArray( &c, 1 );
          dif = miliTime() - start;
          cout << "Result = " << c << endl;
          cout << "Non-array var passed to array code = " << dif << " ms" << endl;
        
          start = miliTime();
          char a[1];
          a[0] = 'b';
          for ( n=0; n<repeats; ++n)
            workArray( a, 1 );
          dif = miliTime() - start;
          cout << "Result = " << a[0] << endl;
          cout << "Array var passed to array code = " << dif << "ms" << endl;
        
          start = miliTime();
          char c2;
          c2 = 'b';
          for ( n=0; n<repeats; ++n)
            workSingle( c2 );
          dif = miliTime() - start;
          cout << "Result = " << c2 << endl;
          cout << "Non-array var passed to non-array code = " << dif << "ms" << endl;
        
          start = miliTime();
          char a2[1];
          a2[0] = 'b';
          for ( n=0; n<repeats; ++n)
            workSingle( a2[0] );
          dif = miliTime() - start;
          cout << "Result = " << a2[0] << endl;
          cout << "Array var passed to non-array code = " << dif << "ms" << endl;
        }
        

        在 gcc-4.7 下使用此命令行编译并在我的计算机中执行时:

        g++ -O2 -Wall -std=c++11 x.cpp -o x.out && ./x.out
        

        我得到这个输出:
        结果 = z
        传递给数组代码的非数组 var = 5520 毫秒
        结果 = z
        数组 var 传递给数组代码 = 5515ms
        结果 = z
        非数组 var 传递给非数组代码 = 5203ms
        结果 = z
        数组 var 传递给非数组代码 = 5203ms

        正如预期的那样,结果总是一样的。 对于这两种实现,将数组或非数组变量传递给工作函数没有显着差异。

        workSingle 比 workArray 快 6%。
        外部循环的执行(workSingle 中不存在)不太可能是原因,因为内部循环执行了 1000 次。原因可能是由于间接访问 v[n] 比访问 c 慢。
        虽然如果你在内部循环中为从 std::cin 读取的全局变量更改 1000,那么 workSingle 实际上给出的时间比 workArray 慢!

        某种优化、缓存未命中或其他低级别的东西可能是原因。我不会为了 workSingle 不确定的效率而牺牲 workArray 的可重用性,除非时间如此紧迫以至于您愿意进入汇编级别。

        结论。

        将您的变量声明为非数组,因为它只需要包含一个字符。
        将大部分代码实现为采用数组参数的函数。如果它这么大,可能会分成几个小节。

        此帖子CC-BY-SA 3.0下向公众授权

        【讨论】:

        • 我永远不会使用选项 (b),因为我不希望 work(foo) 修改 foo。对于不熟悉work() 内部运作的读者来说,这是一个地雷。如果你想修改一个参数,说出来并要求一个带有void work(char* c);的指针; work(&amp;foo) 清楚地告诉读者他应该期待 foo 改变。
        • 不,work(&foo) 并没有告诉读者他应该期望 foo 改变。您需要为此检查函数声明和文档。考虑 void(const char* foo)。而通过引用而不是指针传递是C++中传递IN-OUT参数的常用方式。
        猜你喜欢
        • 1970-01-01
        • 2014-12-17
        • 2012-05-27
        • 1970-01-01
        • 2016-06-28
        • 1970-01-01
        • 2014-05-03
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多