Czym jest rozkład tablicy do wskaźnika?

Czym jest rozkład tablicy do wskaźnika? Czy jest jakiś związek ze wskaźnikami tablicy?

Author: Konrad Rudolph, 2009-09-22

9 answers

Mówi się, że tablice "rozpadają się" na wskaźniki. Tablica C++ zadeklarowana jako int numbers [5] nie może być ponownie wskazana, tzn. nie można powiedzieć numbers = 0x5a5aff23. Co ważniejsze, termin rozpad oznacza utratę typu i wymiaru; numbers rozpad na int* poprzez utratę informacji o wymiarze (count 5), A Typ nie jest już int [5]. Poszukaj tutaj przypadków, w których rozpad nie następuje .

Jeśli przekazujesz tablicę według wartości, tak naprawdę kopiujesz wskaźnik - wskaźnik do pierwszej tablicy element jest kopiowany do parametru (którego typ powinien być również wskaźnikiem typu elementu tablicy). Działa to ze względu na rozkładający się charakter tablicy; raz rozkładany, sizeof nie daje już pełnej wielkości tablicy, ponieważ zasadniczo staje się wskaźnikiem. Dlatego preferowane jest (między innymi) przechodzenie przez odniesienie lub wskaźnik.

Trzy sposoby przejścia w tablicy1:

void by_value(const T* array)   // const T array[] means the same
void by_pointer(const T (*array)[U])
void by_reference(const T (&array)[U])

Ostatnie dwa dadzą właściwe sizeof informacje, natomiast pierwsze nie od argumentu array został uszkodzony, aby przypisać go do parametru.

1 stała U powinna być znana w czasie kompilacji.

 301
Author: phoebus,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-05-23 11:55:18

Tablice są zasadniczo takie same jak wskaźniki W C/C++, ale nie do końca. Po przekonwertowaniu tablicy:

const int a[] = { 2, 3, 5, 7, 11 };

Do wskaźnika (który działa bez rzucania, a zatem może się zdarzyć nieoczekiwanie w niektórych przypadkach):

const int* p = a;

Tracisz zdolność operatora sizeof do liczenia elementów w tablicy:

assert( sizeof(p) != sizeof(a) );  // sizes are not equal
Ta utracona zdolność nazywana jest "rozpadem".

Aby uzyskać więcej informacji, zajrzyj do tego artykułu o rozkładach tablic.

 104
Author: system PAUSE,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-03-23 23:04:32

Oto, co mówi standard (C99 6.3.2.1 / 3-Inne operandy-Lvalues, tablice i desygnatory funkcji):

Z wyjątkiem sytuacji, gdy jest operandem operatora sizeof lub operatora uniary&, lub jest ciąg znaków używany do inicjalizacji tablicy, wyrażenie, które ma typ "array of type" jest konwertowane do wyrażenia z typem "wskaźnik do typu", które wskazuje na początkowy element obiekt array i nie jest lvalue.

Oznacza to, że prawie za każdym razem, gdy w wyrażeniu zostanie użyta nazwa tablicy, zostanie ona automatycznie przekonwertowana na wskaźnik do 1. pozycji w tablicy.

Zauważ, że nazwy funkcji działają w podobny sposób, ale Wskaźniki funkcji są używane znacznie rzadziej i w znacznie bardziej wyspecjalizowany sposób, że nie powoduje to prawie tak dużego zamieszania, jak automatyczna konwersja nazw tablic na wskaźniki.

Standard C++ (Konwersja Array-to-pointer 4.2) rozluźnia wymóg konwersji do (podkreślenia):

An wartość lvalue lub rvalue typu "array of N T "lub" array of unknown bound of t " Może zostać przekonwertowana na wartość R typu "wskaźnik do T."

Więc konwersja Nie musi przebiegać tak, jak zwykle w C(pozwala to na przeciążenie funkcji lub dopasowanie szablonów na typ tablicy).

Dlatego też w C należy unikać używania parametrów tablicy w prototypach funkcji/definicjach(moim zdaniem-nie jestem pewien czy jest jakaś ogólna zgoda). Oni powodują zamieszanie i są fikcją-użyj parametrów wskaźnika, a zamieszanie może nie zniknąć całkowicie, ale przynajmniej deklaracja parametrów nie kłamie.

 51
Author: Michael Burr,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-22 19:46:58

"Decay" odnosi się do niejawnej konwersji wyrażenia z typu tablicy na typ wskaźnika. W większości kontekstów, gdy kompilator widzi wyrażenie tablicowe, konwertuje typ wyrażenia z "tablicy N-elementów T" na "wskaźnik do T" i ustawia wartość wyrażenia na adres pierwszego elementu tablicy. Wyjątki od tej reguły są takie, gdy tablica jest operandem operatorów sizeof lub &, lub tablica jest literałem łańcuchowym używanym jako inicjalizacja w deklaracji.

Przyjmij następujący kod:

char a[80];
strcpy(a, "This is a test");

Wyrażenie a jest typu "80-element array of char", a wyrażenie" This is a test "jest typu" 16-element array of char " (w C; w C++ literały łańcuchowe są tablicami const char). Jednak w wywołaniu strcpy() żadne z wyrażeń nie jest operandem sizeof lub &, więc ich typy są domyślnie konwertowane na "wskaźnik na znak", a ich wartości są ustawiane na adres pierwszego elementu w każdym z nich. To, co strcpy() otrzymuje nie są tablicami, ale wskaźnikami, jak widać w prototypie:

char *strcpy(char *dest, const char *src);

To nie to samo co wskaźnik tablicy. Na przykład:

char a[80];
char *ptr_to_first_element = a;
char (*ptr_to_array)[80] = &a;

Zarówno ptr_to_first_element, jak i ptr_to_array mają tę samą } wartość; adres bazowy a. są to jednak różne typy i są traktowane inaczej, jak pokazano poniżej:

a[i] == ptr_to_first_element[i] == (*ptr_to_array)[i] != *ptr_to_array[i] != ptr_to_array[i]

Pamiętaj, że wyrażenie {[15] } jest interpretowane jako *(a+i) (co działa tylko wtedy, gdy typ tablicy jest przekonwertowany na typ wskaźnika), więc zarówno a[i] i ptr_to_first_element[i] działają tak samo. Wyrażenie (*ptr_to_array)[i] jest interpretowane jako *(*a+i). Wyrażenia *ptr_to_array[i] i ptr_to_array[i] mogą prowadzić do ostrzeżeń kompilatora lub błędów w zależności od kontekstu; na pewno zrobią coś złego, jeśli oczekujesz, że będą oceniać do a[i].

sizeof a == sizeof *ptr_to_array == 80

Ponownie, gdy tablica jest operandem sizeof, nie jest konwertowana na typ wskaźnika.

sizeof *ptr_to_first_element == sizeof (char) == 1
sizeof ptr_to_first_element == sizeof (char *) == whatever the pointer size
                                                  is on your platform

ptr_to_first_element jest prostym wskaźnikiem do znaku.

 33
Author: John Bode,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-22 20:21:27

tablice w C nie mają wartości.

Wszędzie tam, gdzie oczekiwana jest wartość obiektu, ale obiekt jest tablicą, używany jest adres pierwszego elementu, z typem pointer to (type of array elements).

W funkcji wszystkie parametry są przekazywane przez wartość(tablice nie są wyjątkiem). Kiedy przekazujesz tablicę w funkcji, " rozkłada się ona na wskaźnik "(sic); kiedy porównujesz tablicę do czegoś innego, ponownie" rozkłada się na wskaźnik " (sic); ...

void foo(int arr[]);

Funkcja foo oczekuje wartości tablica. Ale w C tablice nie mają wartości! Tak więc foo pobiera adres pierwszego elementu tablicy.

int arr[5];
int *ip = &(arr[1]);
if (arr == ip) { /* something; */ }

W porównaniu powyżej, arr nie ma wartości, więc staje się wskaźnikiem. Staje się wskaźnikiem do int. Wskaźnik ten można porównać ze zmienną ip.

W składni indeksowania tablicy, do której jesteś przyzwyczajony, arr jest 'rozkładany do wskaźnika'

arr[42];
/* same as *(arr + 42); */
/* same as *(&(arr[0]) + 42); */

Tablica nie rozpada się na wskaźnik tylko wtedy, gdy jest operandem Operator sizeof, lub operator & ('adres' Operatora), lub jako ciąg znaków używany do inicjalizacji tablicy znaków.

 16
Author: pmg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-22 17:55:23

To kiedy tablica gnije i jest wskazywana; -)

Właściwie, chodzi o to, że jeśli chcesz przekazać tablicę gdzieś, ale wskaźnik jest przekazywany zamiast tego (bo kto do diabła przekazałby całą tablicę za Ciebie), Ludzie mówią, że słaba tablica rozpadła się na wskaźnik.

 9
Author: Michael Krelin - hacker,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2009-09-22 17:28:58

Rozkładanie tablicy oznacza, że gdy tablica jest przekazywana jako parametr do funkcji, jest traktowana identycznie jak ("rozkłada się do") wskaźnik.

void do_something(int *array) {
  // We don't know how big array is here, because it's decayed to a pointer.
  printf("%i\n", sizeof(array));  // always prints 4 on a 32-bit machine
}

int main (int argc, char **argv) {
    int a[10];
    int b[20];
    int *c;
    printf("%zu\n", sizeof(a)); //prints 40 on a 32-bit machine
    printf("%zu\n", sizeof(b)); //prints 80 on a 32-bit machine
    printf("%zu\n", sizeof(c)); //prints 4 on a 32-bit machine
    do_something(a);
    do_something(b);
    do_something(c);
}
Istnieją dwie komplikacje lub wyjątki od powyższego.

Po pierwsze, gdy mamy do czynienia z tablicami wielowymiarowymi w C i C++, traci się tylko pierwszy wymiar. Dzieje się tak dlatego, że tablice są rozmieszczone w pamięci, więc kompilator musi znać wszystkie oprócz pierwszego wymiaru, aby móc obliczyć przesunięcia w tym bloku pamięć.

void do_something(int array[][10])
{
    // We don't know how big the first dimension is.
}

int main(int argc, char *argv[]) {
    int a[5][10];
    int b[20][10];
    do_something(a);
    do_something(b);
    return 0;
}

Po drugie, w C++, można użyć szablonów, aby wydedukować rozmiar tablic. Microsoft używa tego do wersji C++ bezpiecznych funkcji CRT, takich jak strcpy_s , i możesz użyć podobnej sztuczki, aby niezawodnie uzyskać liczbę elementów w tablicy.

 4
Author: Josh Kelley,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-05-21 08:49:27

TL;dr: kiedy używasz tablicy, którą zdefiniowałeś, będziesz używał wskaźnika do jej pierwszego elementu.

Tak więc:

  • kiedy piszesz arr[idx] tak naprawdę mówisz *(arr + idx).
  • Funkcje nigdy tak naprawdę nie przyjmują tablic jako parametrów, tylko wskaźniki, nawet jeśli podasz parametr tablicy.

Rodzaj wyjątków od tej reguły:

  • można przekazywać tablice o stałej długości do funkcji w obrębie struct.
  • sizeof() daje rozmiar wzięty przez tablica, a nie wielkość wskaźnika.
 0
Author: einpoklum,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-03-13 09:59:08

Mogę być tak odważny, że myślę, że istnieją cztery (4) sposoby przekazywania tablicy jako argumentu funkcji. Również tutaj jest krótki, ale działający kod dla Twojej lektury.

#include <iostream>
#include <string>
#include <vector>
#include <cassert>

using namespace std;

// test data
// notice native array init with no copy aka "="
// not possible in C
 const char* specimen[]{ __TIME__, __DATE__, __TIMESTAMP__ };

// ONE
// simple, dangerous and useless
template<typename T>
void as_pointer(const T* array) { 
    // a pointer
    assert(array != nullptr); 
} ;

// TWO
// for above const T array[] means the same
// but and also , minimum array size indication might be given too
// this also does not stop the array decay into T *
// thus size information is lost
template<typename T>
void by_value_no_size(const T array[0xFF]) { 
    // decayed to a pointer
    assert( array != nullptr ); 
}

// THREE
// size information is preserved
// but pointer is asked for
template<typename T, size_t N>
void pointer_to_array(const T (*array)[N])
{
   // dealing with native pointer 
    assert( array != nullptr ); 
}

// FOUR
// no C equivalent
// array by reference
// size is preserved
template<typename T, size_t N>
void reference_to_array(const T (&array)[N])
{
    // array is not a pointer here
    // it is (almost) a container
    // most of the std:: lib algorithms 
    // do work on array reference, for example
    // range for requires std::begin() and std::end()
    // on the type passed as range to iterate over
    for (auto && elem : array )
    {
        cout << endl << elem ;
    }
}

int main()
{
     // ONE
     as_pointer(specimen);
     // TWO
     by_value_no_size(specimen);
     // THREE
     pointer_to_array(&specimen);
     // FOUR
     reference_to_array( specimen ) ;
}

Mogę również pomyśleć, że pokazuje to wyższość C++ vs C. przynajmniej w odniesieniu (kalambur zamierzony) przekazywania tablicy przez odniesienie.

Oczywiście istnieją bardzo rygorystyczne projekty bez alokacji sterty, bez wyjątków i bez std:: lib. Obsługa macierzy natywnych C++ jest kluczową cechą języka, można powiedz.

 0
Author: Chef Gladiator,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2019-11-09 07:44:18