Передача массива в качестве аргумента в C++

Я пишу функцию сортировки слиянием, и прямо сейчас я просто использую массив тестовых случаев (входных данных нет - пока это статично). Я не знаю, как передать массив в качестве аргумента. Вот мой код прямо сейчас:

//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++ в качестве первого языка, и имею только базовое представление о функциях языка. Спасибо за помощь.


person jkeys    schedule 18.04.2009    source источник
comment
рекомендуемое чтение: gnomesane.net/code/doc/ptrarray   -  person Evan Teran    schedule 19.04.2009
comment
mergeSort(originalarray[]) имеет неверный формат. Вам нужно следующее определение вашей функции mergeSort(originalarray, arraySize); массив всегда передается по адресу/ссылке в функции, вам также нужен размер массива для их итерации.   -  person Syed Tayyab Ali    schedule 19.04.2009


Ответы (7)


Просто чтобы немного расширить это, помните, что массивы C++ - это точно массивы C. Итак, все, что у вас есть, — это адрес участка памяти, который претендует (без каких-либо гарантий) на массив чего-то.

Обновлять

Хорошо, мы расширим немного больше.

C (и, следовательно, C++) на самом деле не имеет массивов как таковых. Все, что у него есть, это адреса, указатели. Итак, когда вы делаете что-то массивом, на самом деле происходит следующее: вы сообщаете компилятору, что какая-то переменная представляет собой адрес.

В C полезно различать декларацию и определение. В объявлении вы просто даете чему-то имя и тип; в определении вы фактически выделяете место.

Итак, если мы начнем с определения массива, например

int ar[100];

это означает, что мы сообщаем компилятору, что нам нужно место для 100 int, мы хотим, чтобы все они были выделены в одном фрагменте, и мы собираемся использовать для него имя 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 плюс 1, умноженный на размер int, поэтому следующий int закончен. И на самом деле, это всегда верно в 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×sizeof(int)). Если это в вашем массиве, хорошо, но если это просто случайная память, это тоже хорошо. Си не волнует.

(Помните, что С происходит из телефонной компании. Нам все равно, нам это не нужно. Мы телефонная компания.)

Итак, теперь мы знаем некоторые правила, которые я перенес сюда. Прочитайте ≡ as эквивалентно или совпадает с.

На что вы можете положиться:

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

Поскольку C не знает разницы между указателями и массивами, вы можете объявить любой из них. Когда вы определяете функцию, вы можете написать

void foo(int[] ar){

or

void foo(int* ar){

и получить точно такой же эффект.

  • t[i]*(t+i)

Это было выше. Везде, где вы можете написать ar[i], вы можете заменить его на *(ar+i). (На самом деле есть странный побочный случай, который нарушает это, но вы не столкнетесь с ним как новичок.)

  • где TYPE *t, (t+i) будут равны адресу t плюс i*sizeof(TYPE)

Объяснил и это выше. Когда вы индексируете массив, например ar[42], это означает, что вы хотите, чтобы 42-й номер был от начального адреса. Итак, если вы используете int, то вам нужно переместиться более 42 раз, независимо от ширины int, то есть sizeof(int).

Итак, это все С, и поскольку С++ определяется как разновидность С, все это верно и для С++. КРОМЕ

  • если только TYPE не является определяемым пользователем типом, который перегружает operator[] и operator*.

в C++ вы можете решить, что хотите определить новый тип, который действует так же, как и любой другой тип, но вы можете изменить то, как язык выполняет определенные действия. Таким образом, программист может решить перегрузить, т. е. заменить, стандартное поведение операторов ссылки на массив и разыменования указателя чем-то собственным. Как новичок, вы не должны сталкиваться с этим в ближайшее время, но вы должны знать об этом.

person Charlie Martin    schedule 18.04.2009
comment
Я думаю, вы переоцениваете мои знания, потому что я знаю, что вы только что сказали. В настоящее время я изучаю программирование прямо сейчас, и пока не разбираюсь в указателях или ООП-частях С++. - person jkeys; 18.04.2009
comment
Это действительно хорошая разбивка массивов в C++. Я рад, что использую .NET/Java! :) - person Alex; 20.07.2010
comment
Спасибо за добрые слова, Алекс, но на самом деле понимание этого будет иметь большое значение для вашего понимания Java/C#. Например, что вы получаете, когда говорите Object a = new Object(); в Яве. Что такое «а» на самом деле? - person Charlie Martin; 29.07.2010

Вы не должны использовать sizeof(originalarray)/sizeof(int) таким образом. Это будет работать только для статически объявленных массивов (размер известен во время компиляции). Вы должны передать размер вместе с ним. Почему бы вам просто не сделать vector из массива и передать его вместо этого?

Примечание. Как правило, всегда помните, что sizeof будет переведено во время компиляции. Таким образом, он никак не может узнать размер массива, переданного в качестве аргумента.

person mmx    schedule 18.04.2009
comment
Я не знаю, что ты имеешь в виду. Должен ли я просто std:: скопировать все элементы в вектор и передать это? Как вы используете sizeof() с вектором? - person jkeys; 18.04.2009
comment
Вы не используете sizeof для вектора. v.size() получит размер. Вы должны либо сделать это, либо, если вы хотите передать массив, передать размер в качестве другого параметра. - person mmx; 18.04.2009

Я вижу, вы включаете <vector>. Я предлагаю вам отказаться от любого использования массивов и использовать только класс vector. Вы можете увидеть примеры использования контейнеров STL, таких как vector здесь .

person Assaf Lavie    schedule 18.04.2009

  • Когда вы передаете массивы функциям, они распадаются на указатели на первый элемент массива, несмотря на нотацию. Итак, ваш sizeof не работает должным образом.

  • Когда вы передаете массив, лучше всего передать размер массива, чтобы вы знали, где остановиться. Добавьте его как дополнительный параметр.

person dirkgently    schedule 18.04.2009

В дополнение ко всем приведенным выше ответам вы также можете ознакомиться с вопросами и ответами по массивам на сайте c-faq.com: http://c-faq.com/aryptr/index.html

person Ye Liu    schedule 18.04.2009

К сожалению, очень сложно сделать именно то, что вы хотите, на 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);

Другими словами, вы передаете указатель на первый элемент и количество элементов.

Кроме того, вы можете иметь дело с векторами. Vector инкапсулирует одни и те же две вещи (указатель на первый элемент и размер) в один объект, называемый «объектом». Кроме того, он управляет памятью за вас, поэтому вы можете увеличить количество элементов по мере необходимости. Это путь С++. Жаль, что вы не можете инициализировать вектор с помощью {...}, как массив.

person Community    schedule 18.04.2009

Похоже, вы используете как динамически распределяемые массивы, так и векторы, когда я считаю, что просто использовать 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!
}

Просто примечание: похоже, вы не выполняете сортировку на месте, поэтому ожидайте, что ваша сортировка займет некоторое время, так как вы копируете много данных.

person Snazzer    schedule 19.04.2009