Передача массива в качестве аргумента в С++
Я пишу функцию сортировки слиянием, и сейчас я просто использую массив тестовых случаев (пока нет ввода - пока что это статично). Я не знаю, как передать массив в качестве аргумента. Вот мой код прямо сейчас:
//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++ в качестве первого языка, и имею только базовые знания о языковых особенностях. Спасибо за помощь.
Ответы
Ответ 1
Jut, чтобы немного расширить это, помните, что массивы С++ являются в точности массивами C. Таким образом, все, что у вас есть, - это адрес части памяти, которая подразумевает (без каких-либо гарантий) массив множеств.
Update
Хорошо, мы расширим еще немного.
C (и поэтому С++) на самом деле не имеет "массивов" как таковых. Все, что у него есть, это адреса, указатели. Поэтому, когда вы делаете что-то "массив", что действительно происходит, вы говорите компилятору, что какая-то переменная представляет собой адрес.
Полезно провести различие в C между объявлением и определением. В объявлении вы просто даете что-то имя и тип; в определении вы фактически выделяете пространство.
Итак, если мы начнем с определения массива типа
int ar[100];
Это означает, что мы говорим компилятору, что хотим пространства для 100 int
, мы хотим, чтобы все они были выделены в одном фрагменте, и мы будем использовать для него имя ar
. Оператор sizeof
задает количество байтов, используемых типом или объектом, поэтому наш массив ar
будет занимать 100 и раз; sizeof(int)
bytes. На большинстве машин это будет 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 плюс 1 раз размер int
, поэтому следующий int over. И на самом деле это всегда верно в C.
Это называется ссылкой на массив.
Когда мы используем синтаксис *ar_p
, мы говорим компилятору, чтобы он получил информацию по адресу, указанному в ar_p
. `.
Это называется разыменованием указателя.
Если мы скажем
ar_p = ar;
то *ar_p
и ar[0]
относятся к одной и той же вещи.
Когда мы говорим ar[0]
, мы говорим компилятору, что хотим, чтобы вещь находилась в адресе 0 байт от ar
. ar[1]
- это адрес int
, или 4 байта, из ar
. Итак, *(ar_p+3)
относится к тому же, что и ar[3]
. (Нам нужны скобки, потому что мы хотим сначала добавить 3 к адресу, а затем посмотреть содержимое. *ar_p+3
сначала получит содержимое, на которое указывает ap_p
, а затем добавит 3. к ним.
Дело в том, что C не знает или не заботится о том, насколько велик массив. Если я приеду и сделаю ar[365]
, компилятор с радостью сгенерирует код для поиска в ячейке 1000+ (365 & times; sizeof(int)
). Если это в вашем массиве, отлично, но если это просто случайная память, это тоже хорошо. C не заботится.
(Помните, что C приходит из телефонной компании. "Нам все равно, мы не должны. Мы - Телефонная компания".)
Итак, теперь мы знаем некоторые правила, которые я переместил сюда. Читайте "& equiv;" как "эквивалентно" или "совпадает с".
От чего вы можете зависеть:
-
foo(TYPE t[])
& equiv; foo(TYPE * t)
Так как C не знает разницы между указателями и массивами, вы можете объявить один из них. Когда вы определяете функцию, вы можете написать
void foo(int[] ar){
или
void foo(int* ar){
и получить точно такой же эффект.
Это было выше. В любом месте, где вы могли бы написать ar[i]
, вы можете заменить его на *(ar+i)
. (На самом деле это странный боковой случай, который нарушает это, но вы не столкнетесь с ним как новичок.)
- где
TYPE *t
, (t+i)
будет равно адресу в t
плюс i*sizeof(TYPE)
Объяснил это выше. Когда вы индексируете в массив, например ar[42]
, это означает, что вы хотите, чтобы 42-й из всех начальных адресов. Итак, если вы используете int
, тогда вам нужно переместить более 42 раз, а int
- это sizeof(int)
.
Теперь, что все C, а так как С++ определяется как "вид" C, все это выполняется и для С++. КРОМЕ
- если
TYPE
- это определенный пользователем тип, который перегружает operator[]
и operator*
.
в С++, вы можете решить, хотите ли вы определить новый тип, который действует так же, как и любой другой тип, но вы можете изменить способ, которым язык выполняет определенные вещи. Таким образом, программист может решить "перегрузить", т.е. Заменить - поведение по умолчанию ссылки на массив и операторы разыменования указателей с чем-то из их собственных разработок. Как новичок, вы не должны столкнуться с этим в ближайшее время, но вы должны знать об этом.
Ответ 2
Нельзя использовать sizeof(originalarray)/sizeof(int)
. Он будет работать только для статически объявленных массивов (размер известен во время компиляции). Вы должны передать размер вместе с ним. Почему бы вам просто не сделать vector
из массива и передать его вместо этого?
Боковое примечание. Как правило, всегда отмечайте, что sizeof
будет переведен во время компиляции. Таким образом, он не мог узнать размер массива, переданного в качестве аргумента.
Ответ 3
Я вижу, что вы включили <vector>
. Я предлагаю вам избавиться от всех видов использования массивов и использовать только класс vector
. Вы можете увидеть примеры использования контейнеров STL, таких как vector
здесь.
Ответ 4
-
Когда вы передаете массивы в функции, они распадаются на указатели на первый элемент массива, несмотря на обозначение. Таким образом, ваш sizeof
работает не так, как ожидалось.
-
Когда вы передаете массив, лучше передать размер массива, чтобы вы знали, где остановиться. Добавьте его в качестве дополнительного параметра.
Ответ 5
В дополнение ко всем приведенным выше ответам вы также можете проверить Q & As на массивах c-faq.com: http://c-faq.com/aryptr/index.html
Ответ 6
К сожалению, очень сложно сделать то, что вы хотите сделать на 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);
Другими словами, вы передаете указатель на первый элемент и количество элементов.
Кроме того, вы можете иметь дело с векторами. Вектор инкапсулирует те же две вещи (указатель на первый элемент и размер) в единый объект, называемый "объект". Кроме того, он управляет памятью для вас, поэтому вы можете расширить количество элементов по мере необходимости. Это путь С++. Слишком плохо, вы не можете инициализировать вектор с помощью {...}, как вы можете массив.
Ответ 7
Похоже, вы используете как динамически распределенные массивы, так и векторы, когда я считаю, что просто использовать std::vector будет достаточно.
Во-первых, пусть ваш входной массив будет изменен на std::vector и заполнит его вашими входными данными.
int main()
{
std::vector<int> originalarray;
for (int data = 1; data <= 10; data++)
{
originalarray.push_back(data);
}
mergeSort(originaldata);
}
Теперь важно объявить вашу функцию mergesort ссылкой на 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!
}
Просто заметьте, похоже, что вы не занимаетесь сортировкой inplace, поэтому ожидайте, что ваш сорт займет некоторое время, так как вы копируете большое количество данных.