Передача массива в качестве аргумента в C ++ - PullRequest
13 голосов
/ 18 апреля 2009

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

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

Ответы [ 7 ]

30 голосов
/ 18 апреля 2009

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

Обновление

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

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

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

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

int ar[100];

это означает, что мы говорим компилятору, что нам нужно пространство для 100 int, мы хотим, чтобы он был распределен в одном фрагменте, и мы будем использовать для него имя ar. Оператор sizeof дает количество байтов, используемых типом или объектом, поэтому наш массив ar будет занимать 100 & times; 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, так что next 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)). Если это в вашем массиве, хорошо, но если это просто случайная память, это тоже хорошо. 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, то вам нужно подняться в 42 раза шире, чем int, то есть sizeof(int).

Теперь, это все C, и, поскольку C ++ определяется как "вид" C, все это также относится и к C ++. КРОМЕ

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

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

18 голосов
/ 18 апреля 2009

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

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

3 голосов
/ 18 апреля 2009

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

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

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

0 голосов
/ 19 апреля 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!
}

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

0 голосов
/ 19 апреля 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);

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

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

0 голосов
/ 18 апреля 2009

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...