Przekazywanie tablic jako odniesienia


W C ++, jak mogę przejść

tablica jako odniesienie

jeśli nie znam jego rozmiaru w czasie kompilacji? Jak dotąd doszedłem do wniosku, że jedynym sposobem, aby to zadziałało, jest użycie czegoś takiego
const double( &numbers ) [size]

ale to oznacza, że ​​potrzebuję

znać rozmiar tablicy w czasie kompilacji

więc nie mogę go używać w funkcji zewnętrznej.
Moje pytania to:
  • Jeśli nie przekażę tablicy jako
    (const double (& amp; numbers) [length])
    bo na przykład nie znam jej rozmiaru, jak mogę się upewnić, że nie zostanie skopiowany ale na nim odnosić się
  • Jeśli przekażę tablicę jak w powyższym przykładzie
    (double array [])
    odnosi się do niego lub skopiowane

Zaproszony:
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Inne odpowiedzi są świetne, ale nikt nie wspomina o użyciu szablonów do rozwiązania tego problemu. Nadal powinieneś sprawić, by funkcja akceptowała wskaźnik i rozmiar, ale szablony mogą wypełniać je automatycznie:
void f( double const numbers[], std::size_t length )
{ ... }template< std::size_t Length >
void f( double const (&numbers)[ Length ] )
{
return f( numbers, Length );
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

W C ++ powinieneś używać

std::vector
http://en.cppreference.com/w/cpp/container/vector
.
W C/C ++ nie można przekazywać tablic jako kopii. Tablice są zawsze przekazywane przez odwołanie.

EDIT

W C ++

tablice przekazywane przez odwołanie

mają inne znaczenie. Zarówno w C, jak i C ++, tablice rozpadają się na wskaźnik do pierwszego elementu tablicy. Sprawdź poniższe komentarze.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Kilka rzeczy:
  • C ++ i tak nie zezwala na tablice o zmiennej wielkości. Dlatego wszystkie tablice muszą mieć znane rozmiary w czasie kompilacji. Nie jestem więc do końca pewien, czy Twoje pierwotne pytanie w ogóle ma zastosowanie, ponieważ w pierwszej kolejności nie będziesz w stanie utworzyć tablicy o nieznanym rozmiarze.
  • Kiedy przekazujesz tablicę, odbywa się to przez odniesienie. Nie jest kopiowany.

W każdym razie prawdopodobnie zechcesz zamiast tego rozważyć użycie
wektor
.

EDYCJA: patrz komentarze.

double average(const double *arr, size_t len){
// Compute average
return accumulate(arr, arr + len, 0)/(double)len;
}int main(){ double array[10] =// Initialize it cout << average(array, 10) << endl;// Alternatively: This could probably be made a macro.
// But be careful though since the function can still take a pointer instead
// of an array.
cout << average(array, sizeof(array)/sizeof(double)) << endl; return 0;
}
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

W C ++ nazwa tablicy to po prostu

stały wskaźnik

do pierwszego elementu. Stały

wskaźnik

oznacza wskaźnik, który jest w stanie zmienić wszystko, na co wskazuje, ale nie można go zmienić, aby wskazywał na coś innego.
Oznacza to, że zawsze, gdy transmitujesz

szyk
, faktycznie mijasz

stały wskaźnik

do tej tablicy. Innymi słowy, już przekazujesz go przez odniesienie, bez dodatkowego wysiłku. Mówiąc dokładniej, w rzeczywistości

skopiowane

ten

stały wskaźnik

więc ostatnie (miejmy nadzieję, że niezbyt mylące) sformułowanie brzmi: zdałeś

stały wskaźnik

na tablicę

według wartości

.
Jeśli nie znasz rozmiaru swojej tablicy w czasie kompilacji, po prostu użyj (zwykłego) wskaźnika do swojego typu danych zamiast jawnej tablicy. Więc cokolwiek
T my_array []
(gdzie
T
to typ, na przykład
int
,
double
lub nawet jeden z Twoje klasy) zmieni się na
T * my_array
, a składnia jest dokładnie taka sama ...
my_array [i]
będzie działać dobrze (jest inna składnia, ale nie tak elegancka ). Użyj operatora
new
, aby zainicjować:
T* my_array;my_array = new T[3];

lub
T* my_array;my_array = new T[x];

gdzie
x
jest liczbą całkowitą (niekoniecznie stałą, jak w przypadku zwykłych tablic). Możesz więc pobrać to
x
od użytkownika w czasie wykonywania, a następnie utworzyć swoją „tablicę”. Po prostu uważaj, aby nie zapomnieć o
usunięciu [] my_array
po zakończeniu jej używania, aby uniknąć wycieku pamięci.
[Ostatnia uwaga] wykorzystanie takich

przydzielane dynamicznie

tablica jest dobrym wyborem tylko wtedy, gdy wiesz dokładnie, ile elementów potrzebujesz ... w czasie kompilacji lub nawet w czasie wykonywania. Na przykład, jeśli użytkownik poda swoje
x
na pewno ich użyjesz, to w porządku. W przeciwnym razie grozi Ci przepełnienie tablicy (jeśli potrzebujesz więcej niż
x
) - co zwykle powoduje awarię aplikacji - lub po prostu marnowanie miejsca. Mimo to sam zaimplementujesz większość funkcji potrzebnych do manipulowania tablicami. Dlatego lepiej jest używać kontenerów udostępnianych przez standardową bibliotekę C ++, takich jak
std :: vector
(jak wspomniał Donotalo). Chciałem tylko rozwinąć tę kwestię.
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Jeśli nie przekażę tablicy jako A (const double (& amp; numbers) [length]),
bo np. nie znam jego rozmiaru, skąd mam mieć pewność, że to
nie skopiowane, ale przywoływane?

Tak, to znaczy, że przekazujesz tablicę jako odniesienie,
void Foo(const double( &numbers ) [length]);

Zwróć uwagę, że
length
jest stałą liczbą całkowitą.

Jeśli przekażę do niej tablicę jak w powyższym przykładzie (double array [])
czy są połączone czy skopiowane?

Nie, nie jest kopiowane. Oznacza to, że przekazujesz wskaźnik do swojej tablicy, który jest równoważny,
void Foo(const double *length);
Anonimowy użytkownik

Anonimowy użytkownik

Potwierdzenie od:

Inne języki, takie jak Java i Python, przechowują długość tablic w czasie wykonywania. W tablicach C ++ długość tablicy nie jest zachowywana. Oznacza to, że musisz go gdzieś zapisać ręcznie.
Zawsze, gdy w kodzie masz tablicę o stałym rozmiarze, kompilator zna rozmiar tablicy, ponieważ odczytuje ją z samego kodu źródłowego. Ale gdy tylko kod zostanie skompilowany, informacja o długości zostanie utracona. Na przykład:
void f1(double array[10]) {...}

Kompilator nie wymusi rozmiaru tablicy. Poniższy kod zostanie automatycznie skompilowany, ponieważ parametr tablicy
f1
jest tylko wskaźnikiem do pierwszego elementu tablicy:
void h1() {
double a[10];
double b[5]; f1(a);// OK.
f2(b);// Also OK.
}

Ponieważ kompilator ignoruje statyczny rozmiar tablicy podczas przekazywania jej do funkcji, jedynym sposobem na poznanie rozmiaru tablicy o dowolnym rozmiarze przekazanej jako odniesienie jest jawne określenie jej rozmiaru:
void f2(double array[], size_t array_size) {...}

Następnie możesz wywołać tę funkcję z dowolną tablicą:
void h2() {
double a[10];
double b[19]; f2(a, sizeof(a)/sizeof(a[0]));
f2(b, sizeof(a)/sizeof(a[0]));
}

Parametr
array_size
zawiera rzeczywisty rozmiar tablicy.
Na marginesie,
sizeof (array)
działa tylko z tablicami zdefiniowanymi statycznie. Przekazanie tej tablicy do innych funkcji powoduje utratę informacji o rozmiarze.
Tablica to nie to samo, co wskaźnik. Jednak nieokreślona tablica, taka jak parametr
f2
, jest po prostu wskaźnikiem do pierwszego elementu
double
w sekwencji:
void f3(double array*, size_t array_size) {...}

Z praktycznego punktu widzenia
f2
i
f3
są równoważne.
Tak działa
std :: vector
. Wewnętrznie wektor jest klasą z dwoma polami: wskaźnikiem do pierwszego elementu i liczbą elementów w wektorze. Ułatwia to trochę sprawę, gdy chcesz zaakceptować tablicę o dowolnym rozmiarze jako parametr:
void g(std::vector& v) {...}

Aby odpowiedzieć na pytania, Zaloguj się lub Zarejestruj się