在C++中作为参数传递数组

13

我正在编写一个归并排序函数,目前我只是使用了一个测试用例数组(没有输入 - 目前是静态的)。我不知道如何将数组作为参数传递。这是我的代码:

//merge sort first attempt

#include <iostream>

#include <algorithm>

#include <vector>

int mergeSort(int[]);
int main() {
    int originalarray[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 10 };
    mergeSort(originalarray[]);
}

int mergeSort(int[] originalarray) {
    int num = (sizeof(originalarray) / sizeof(int));
    std::vector < int > original(num);

    if (num > 2) {
        return num;
    }

    // Fill the array using the elements of originalarray
    // This is just for demonstration, normally original will be a parameter,
    // so you won't be filling it up with anything.
    std::copy(originalarray, originalarray + num, original.begin());

    // Create farray and sarray of the appropriate size
    std::vector < int > farray(num / 2);
    std::vector < int > sarray(num - farray.size());

    // Fill those using elements from original
    std::copy(original.begin(), original.begin() + farray.size(), farray.begin());
    std::copy(original.begin() + farray.size(), original.end(), sarray.begin());

    mergeSort(farray);
    mergeSort(sarray);
}

请注意,这个mergeSort函数目前还没有实现,因为我还没有弄清楚如何将它们合并(这是我的任务)。在处理这个问题之前,我希望先将我的两个向量排序,但由于需要将数组作为参数传递,所以我无法编译此代码。我不明白指针,所以如果这是解决方案,我的借口就是无知。我正在学习编程,用的是C++作为第一门语言,对这门语言的特性只有基本的了解。谢谢你的帮助。

一些推荐阅读:http://www.gnomesane.net/code/doc/ptrarray/ - Evan Teran
mergeSort(originalarray[]) 的格式不正确。 您需要使用以下函数定义: mergeSort(originalarray, arraySize);数组在函数中始终以地址/引用传递,您还需要数组的大小来迭代它们。 - Syed Tayyab Ali
7个回答

33
只是稍微扩展一下,记住 C++ 数组就是 完全相同的 C 数组。因此,你所拥有的只是一个内存地址,它声称(但并不保证)是某些东西的数组。

更新

好的,我们再扩展一下。

C(因此也包括 C++)实际上没有像样的“数组”。它只有地址、指针。因此,当你将某个变量设置为“数组”时,实际上是告诉编译器该变量表示一个地址。

在 C 中,声明和定义之间有一个有用的区别。在声明中,你只是给了某个东西一个名称和类型;而在定义中,你实际上分配了空间。

因此,如果我们首先像这样定义一个数组:

int ar[100];

这意味着我们告诉编译器,我们想要为100个整数腾出空间,希望它全部分配在一个块中,并且我们将使用名称ar。运算符sizeof给出类型或对象所使用的字节数,因此我们的数组ar将占用100×sizeof(int)字节。在大多数机器上,它将是400字节,但它因机器而异。
如果我们定义一个变量:
int * ar_p;   // using '_p' as a reminder this is a pointer

我们正在定义一个变量的空间,它将包含一个地址。它的大小将是sizeof(int*),通常为4或8,但在一些机器上可能是2到16,在一些不太可能很快遇到的机器上。
数组的名称ar。编译器将该名称转换为一个地址,因此我们可以保存该地址。
ar_p = ar ;     // THIS WORKS

现在,为了方便起见,假设我们的数组ar恰好从内存中的位置1000开始。
这个名字ar没有分配任何空间;它像一个常数,一个数字。因此,您无法撤消该赋值。
ar = ar_p ;     // THIS WON'T WORK

出于同样的原因,你不能说

1000 = ar_p ;   // THIS WON'T WORK EITHER

比如说,你不能改变1000的值。(在FORTRAN的早期版本中,这个技巧会奏效,但是因为种种复杂的原因而被视作错误。曾经有一段时间,你必须试图在一个将“2”的值设定为3的程序中进行调试。)

C语言中的数组总是以零为基础,也就是说,第一个索引永远是零。任何其他索引都只是使用索引计算出的地址。所以,ar [0]只是地址1000加上0个字节的偏移量,即1000。ar [1]是1000再加上一个int类型所占的大小,所以下一个整数的位置。实际上,在C语言中始终如此。

这被称为数组引用

当我们使用语法*ar_p时,我们告诉编译器获取包含在ar_p中的地址处的内容。

这被称为解引用指针

如果我们声明:

ar_p = ar;

那么,*ar_par [0]指的是同一件事情。

当我们说ar [0]时,我们告诉编译器我们想要的是从ar开始0字节地址处的东西。 ar [1]是从ar开始一个int或4个字节的地址。 因此,*(ar_p + 3)ar [3]指的是同一件事情。 (我们需要括号,因为我们首先要将3添加到地址,然后再查看内容。 * ar_p + 3 将首先获取由ap_p指向的内容,然后将其加上3。

问题在于,C不知道数组的大小,也不太关心。 如果我来做ar [365],编译器将愉快地生成代码,以查看1000 +(365×sizeof(int))单元格中的内容。 如果在您的数组中,那很好,但如果只是随机内存,那也没关系。 C不关心。

(请记住,C来自电话公司。 “我们不关心; 我们不必这样做。 我们是电话公司。”)

所以,现在我们知道了一些规则,我已经将它们移到了这里。 将“≡”读作“等同于”或“与…相同”。

您可以依赖的内容:

  • foo(TYPE t [])foo(TYPE * t)

由于C不知道指针和数组之间的区别,因此您可以声明任何一个。 当您定义函数时,可以编写

void foo(int[] ar){

或者

void foo(int* ar){

并获得完全相同的效果。

  • t[i]*(t+i)

这是上面提到的。任何你可能写ar[i]的地方,都可以用*(ar+i)代替。(实际上有一种奇怪的情况会破坏这个规则,但作为初学者,你不会遇到它。)

  • 其中TYPE *t(t+i)将等于t地址加上i*sizeof(TYPE)

上面已经解释过了。当你索引一个数组时,比如ar[42],它意味着你想要从起始地址开始往后移动第42个元素。所以,如果你使用的是int,那么你需要移动sizeof(int)个字节42次。

现在,这都是关于C的,因为C++被定义为“一种”C,所以它也适用于C++。除非:

  • 除非TYPE是一个用户定义的类型,它重载了operator[]operator*

在C++中,你可以决定定义一个新类型,它的行为就像任何其他类型一样,但你可以改变语言执行特定操作的方式。因此,程序员可以决定“重载”——即替换——数组引用和指针解引用运算符的默认行为,并使用他们自己设计的东西。作为初学者,你不应该很快遇到这种情况,但你应该意识到它。


1
我认为你高估了我的知识,因为我不知道你刚才说的是什么。我目前正在学习编程,但我还没有学习指针或C++的面向对象部分。 - jkeys
1
这是对C++数组的非常好的分解。我很高兴我使用.NET/Java! :) - Alex
2
谢谢你的赞美,Alex,但实际上理解这个问题对你理解Java/C#也会有很大帮助。例如,在Java中,当你说Object a = new Object();时,你得到了什么? 'a' 究竟是什么? - Charlie Martin

19
你不应该像那样使用sizeof(originalarray)/sizeof(int)。它只适用于静态声明数组 (大小在编译时已知)。你必须连同大小一起传递。为什么不将数组变成一个vector, 然后传递它呢?
附注:作为经验准则,始终注意sizeof将在编译时翻译。因此,它无法知道作为参数传递的数组的大小。

我不知道你的意思是什么。我应该将所有元素std :: copy到向量中并传递吗?如何在向量中使用sizeof()? - jkeys
你不应该在 vector 上使用 sizeof。v.size() 可以获取大小。你应该这样做,或者如果你想传递一个数组,就将大小作为另一个参数传递。 - Mehrdad Afshari

4
我看到你使用了<vector>。我建议你放弃所有数组的使用,只使用vector类。你可以在这里看到如何使用STL容器,例如vectorhere的示例。

2
  • 当您将数组传递给函数时,它们会衰变为指向数组第一个元素的指针,尽管符号不同。因此,您的sizeof无法按预期工作。

  • 当您传入数组时,最好传入数组大小,以便知道何时停止。将其作为附加参数添加。


0

很遗憾,在C或C++中要做到你想做的事情非常困难。你可以像这样传递一个固定大小的数组:

int mergeSort(int originalarray[20])
{
    // do something
}

然而,你的数组大小并非由数字定义,而是由初始化列表中元素的数量定义。

在你的情况下需要做的事情(尽管这样做实际上是错误的)是分两步进行:

int originalarray[] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
const size_t arraySize = sizeof originalarray / sizeof originalarray[0];
int mergeSort(int array[arraySize])
{
    // do something
}

很遗憾,它不能完成你需要的任务:像这样将数组传递给函数会复制数组,而排序的目的是改变原始数组。

事实上,如果不理解“指针”的概念,你无法进一步深入。

你需要开发的函数应该像这样:

int originalarray[] = {1, 3, 5, 7, 9, 2, 4, 6, 8, 10};
const size_t arraySize = sizeof originalarray / sizeof originalarray[0];

int mergeSort(int *array, const size_t size)
{
    // do something
}

mergeSort(&(originalArray[0]), arraySize);

换句话说,你传递指向第一个元素的指针和元素数量。

或者,你可以使用向量。向量将相同的两个内容(指向第一个元素的指针和大小)封装在一个名为“对象”的实体中。此外,它还为你管理了内存,所以你可以根据需要扩展元素数量。这是C++的方式。不过遗憾的是,无法像数组一样使用“{...}”来初始化向量。


0

看起来你同时使用了动态分配数组和向量,但我认为仅使用std::vector就足够了。

首先,将你的输入数组改为一个std::vector,并用输入数据填充它。

int main()
{
   std::vector<int> originalarray;
   for (int data = 1; data <= 10; data++)
   {
      originalarray.push_back(data);
   }
   mergeSort(originaldata);
}

现在重要的是声明你的归并排序函数,以引用std::vector。
int mergeSort(std::vector<int>& originalarray)
{
   // The rest of your code, note that now you are passing 
   // in your array for sorting, so you can continue with your code to split
   // the vector into farray and sarray

   // then call sort on your halves.
   mergeSort(farray);
   mergeSort(sarray);

   // I'm guessing at this point you'd write code to combine your farray sarray, and
   // put it back into originalarray...don't forget to clear original array first!
}

注意一下,看起来你没有进行原地排序,因此预计你的排序会花费一些时间,因为你要复制出大量的数据。


0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接