【问题标题】:Arrays passed to function in C++ giving different lengths在 C++ 中传递给函数的数组给出不同的长度
【发布时间】:2015-08-03 09:59:37
【问题描述】:

首先我想说的是,我对“幕后”发生的导致此问题的事情感兴趣,就像我对代码的盲目修复一样。也许 C++ 标准或我不熟悉的东西决定了它的原因:-)

无论如何...

我正在尝试将 3 个数组 A, B, C 传递给一个函数,该函数将合并 AB,并将结果存储在 C 中。

函数声明:void merge(int* a, int* b, int* c)

main()传递:

int A[] = {1,2,3,4};
int B[] = {6,7,8,9};
int* C;  //this could be wrong, btw!

merge(A, B, C);

问题 1。 奇怪的是,在main() 中,如果我打印sizeof(A)/sizeof(A[0]) 的结果,它会为我提供数组“长度”的正确结果-4-B 也是如此。但是当我通过数组到函数,我尝试再次计算大小,通过相同的方法,但我得到了两个数组的结果2merge()的第一行:

void merge(int* a, int* b, int* c)
{
    int sizeA = sizeof(a)/sizeof(a[0]);
    int sizeB = sizeof(b)/sizeof(b[0]);
    int totalsize = sizeA + sizeB;

    std::cout << "size A = " << sizeA << std::endl;      //prints 2
    std::cout << "size B = " << sizeB << std::endl;      //prints 2
    std::cout << "total  = " << totalsize << std::endl;
...

问题 2。 只是为了好玩,我尝试遍历 ab 传递给 merge()(在合并函数内):

for (int i = 0; i < 4; ++i)
    std::cout << a[i]; //prints "1234" as expected

一切都很酷。但是当我将索引限制增加到 8...

for (int i = 0; i < 8; ++i)
    std::cout << a[i]; //prints "12346789" - A and B concatenated!

再提高几次最大索引,因为为什么不呢:

for (int i = 0; i < 10; ++i)
    std::cout << a[i]; //prints "1234678900"
...
for (int i = 0; i < 11; ++i)
    std::cout << a[i]; //prints "1234678900-444896443"

我猜是由于越界索引和访问其他内存造成的未定义行为。

以同样的方式打印b做类似的事情:

  • 循环到i = 4 打印数组 - 6789
  • 6 添加两个零 - 678900
  • 8 添加其他内容 - 678900-126926969126613

当然,打印 C 不会产生任何结果。


这些怪事是因为

  • 事实上我使用的是C++ Shell(带有选项-std=c++14 -Wpedantic -O2)?
  • merge() 传递数组不正确?
  • main() 中的初始化不正确?
  • 需要有终止符的数组,比如 char 数组?
  • 以上所有?

【问题讨论】:

    标签: c++ arrays


    【解决方案1】:
    1. 在第一种情况下,您有一个数组,与指针不同。所以sizeof 是正确计算的。而int* 是一个指针,传递给函数的数组总是衰减为指针(通过引用传递时除外)。此外,sizeof(int*)/sizeof(int) 是您机器上指针的大小除以 int 的大小,因此如果您的系统是 64 位(8 字节)并且 int 的典型大小为 4,则获取8/4=2
    2. 索引越界导致的典型未定义行为。

    建议:改用std::vector&lt;int&gt;,您将避免为C 分配内存并使用sizeof。您可以简单地使用std::vector::size() 成员函数来获取向量的大小。

    【讨论】:

    • 向量是否和 C 风格的数组一样“高效”,顺便说一句?
    • @jaska 大多数标准库容器都是按照“零开销”的原则设计的。因此,如果您使用reserve() 预先分配内存(这样您就不会通过push_back() 导致重新分配),那么是的,它们可能会一样快。 std::vector 也使用连续内存,类似于 C 样式的数组。
    【解决方案2】:

    当您将数组传递给void merge(int* a, int* b, int* c) 这里的a and b 不再是数组,而是指针。因此,当您使用

    计算它的大小时

    int sizeA = sizeof(a)/sizeof(a[0]);这里sizeof(a)会给你指针的大小,sizeof(a[0]给你int的大小。结果就是这样。

    对于您的第二个问题,当您增加索引并连接两个数组时,这只是因为两个数组都被分配了连续的内存块,尽管它们总是被分配连续的内存块,但没有必要,和其他输出仅是由于未定义的行为

    【讨论】:

      【解决方案3】:
      1. 你需要记住 sizeof 是一个关键字,它在编译时扩展到相关的大小。 sizeof(T) 将扩展为 (T) 的字节大小。 在 main 函数中,sizeof(a) 将给出int[4] 的字节数,在不同的函数中,数组衰减为一个指针sizeof(T*) 不是 sizeof(T[4])!您正在计算指针的大小(衰减后),而不是实际数组的大小。现在假设我的对象重量为 1 MB,sizeof(OBJ) == sizeof(OBJ*)?当然不是。

      2. 你有未定义的行为。在此特定示例中,AB 在堆栈上紧挨着另一个。因此,在这种超级特定情况中,当迭代堆栈时,您实际上会打印两个数组,因为它们一个接一个地放置。但同样,这是未定义的行为。不同的编译器可以填充数组之间的区域,或者其他操作系统甚至可能会杀死您的程序。

      【讨论】:

        【解决方案4】:

        您无法使用传递给函数的简单指针来计算数组的大小。

        sizeof(integers) / sizeof(integers[0])
        将被简单地替换为
        sizeof(pointer on integer) / sizeof(integer)

        但您可以提前计算它,然后将大小传递给如下函数。

        #include <iostream>
        using namespace std;
        
        void doSomeWork(int* integers, const size_t size)
        {
            for (size_t i = 0; i < size; ++i)
            {
                std::cout << integers[i] << std::endl;
            }
        }
        
        int main()
        {
            int integers[] { 0, 1, 2, 3 };
            const size_t size = sizeof(integers) / sizeof(integers[0]);
        
            doSomeWork(integers, size);
        
            return 0;
        }
        

        如下所述,std::vector&lt;int&gt; 比玩 C 数组要好得多。

        【讨论】:

          【解决方案5】:

          这里已经有很多好的答案了。简而言之,C++ 数组是固定大小的。如果你想改变它,你必须使用指针和动态分配。这可能很乏味。这里大多数人建议您改用向量的原因。

          矢量是为动态和可调整大小而构建的。您几乎可以像使用数组一样使用它们。你的代码在这里改编:

          void merge(vector<int> &a, vector<int>& b, vector<int> &c) // pass by reference
          {
              int sizeA = a.size(); 
              int sizeB = b.size();
              int totalsize = sizeA + sizeB;
              c.resize(totalsize);           // easy !
          
              // ...
          }
          int main() {
              vector<int> A{1,2,3,4};
              vector<int> B {6,7,8,9};
              vector<int> C;         
          
              cout <<A[2]<<endl;   // access elements exacly like arrays
              merge(A, B, C);
          }
          

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 2022-12-17
            • 1970-01-01
            • 1970-01-01
            • 2020-08-16
            • 2013-07-11
            • 1970-01-01
            • 2021-03-14
            • 1970-01-01
            相关资源
            最近更新 更多