Ответ 1
Я думаю, что вывод из всего этого обсуждения никогда. Никто здесь действительно не продемонстрировал использование этой конструкции, где что-то еще не работало бы более понятным образом.
Я могу заявить:
int (*ap)[N];
Итак, ap
является указателем на массив int размера N. Почему это когда-либо полезно? Если я передам его функции, какую полезную вещь он может сделать с этим, что он не мог бы сделать с нормальным указателем на содержимое массива?
C Часто задаваемые вопросы:
2.12: Как объявить указатель на массив?
Обычно вы не хотите.
Я думаю, что вывод из всего этого обсуждения никогда. Никто здесь действительно не продемонстрировал использование этой конструкции, где что-то еще не работало бы более понятным образом.
Указатель на массив может использоваться для динамического выделения многомерного массива N, где известны размеры N-1. Ниже создается массив Nx3.
int (*ap)[3];
ap = malloc(N * sizeof(*ap));
/* can now access ap[0][0] - ap[N-1][2] */
@Adam E/Cruachan, это не то же самое, что указатель на указатель. ap - это единственный указатель на блок памяти, содержащий три последовательных целых числа. ap ++ будет продвигать адрес указателя к следующему блоку из трех целых чисел. для int **pp;
, pp указывает на целочисленный указатель, каждый из которых может указывать на целые числа в памяти.
+-----+ +------+ +-----+
ap ---> | int | vs. pp ---> | int* | -> | int |
| int | +------+ +-----+
| int | pp+1 -> | int* | -\
+-----+ +------+ \ +-----+
ap+1 -> | int | : : -> | int |
| int | +-----+
| int |
+-----+
: :
Если вы увеличиваете указатель, он будет указывать на начало следующей группы из N элементов.
Это не большое дело, и это зависит от разработчика.
Это не полезно, правда. Но иногда используются указатели на массивы, например. в Microsoft Windows API - я видел там много всего этого.
Бывают ситуации, когда вы хотите передать местоположение памяти в программах. например, API Windows может ожидать, что вы передадите указатель на структуру данных или массив, где, как вы программируете на каком-либо другом языке, скажем, С#. Windows API не заботится о том, как целевой язык обрабатывает массив, для Windows API это просто поток байтов в памяти, и он заполнит его, отправьте его вам. во избежание ошибки пропуска типа кросс-языка в некоторых случаях мы используем указатель на массив, а не неявное имя массива в качестве указателя. Более того, не гарантируется, что имя неявного массива является длинным указателем, некоторые компиляторы могут оптимизировать его как относительное значение с сегментом. указатель на массив гарантирует, что он имеет размер регистра машинного регистра, и вы можете указать местоположение в любом месте доступной ОЗУ.
Как правило, единственный раз, когда вы увидите указатель на массив (T (*a)[N]
), является параметром функции, где a
означает 2d-массив:
void foo(int (*a)[N], size_t count)
{
size_t i;
for (i = 0; i < count; i++)
a[i][j] = ...;
...
}
void bar(void)
{
int arr[M][N];
foo(arr, M);
}
Обратите внимание, что для объявления параметра функции int a[][N]
эквивалентно int (*a)[N]
, но это верно только для объявлений параметров функции:
void foo (int a[][N], size_t count) {...}
Указатели на массивы обычно не так полезны, как указатели на базовый тип, так как вам нужно знать размер массива, чтобы правильно объявить указатель на него (указатель на 10-элементный массив int отличается от типа указатель на 20-элементный массив int). Лично я не нашел для них много пользы в 20-летнем программировании.
Помните, что в большинстве контекстов выражение массива (например, arr
выше) будет иметь свой тип, неявно преобразованный из "N-element array of T" в "указатель на T" (за исключением случаев, когда выражение массива является операндом из sizeof
или &
, или массив является строковым литералом, который используется в качестве инициализатора в объявлении). В этом случае тип arr
в вызове foo неявно преобразуется из "массива M-элементов массива N-элементов из int" в "указатель на N-элементный массив из int".
Учитывая объявление T a[M][N]
, все следующие выражения будут оцениваться в том же месте (адрес первого элемента в массиве), но типы будут разными, как показано ниже:
Expression Type Implicitly converted to ---------- ---- ----------------------- a T [M][N] T (*)[N] a[0] T [N] T * &a T (*)[M][N] &a[0] T (*)[N] &a[0][0] T *
Это, вероятно, будет перенесено в субъективное/аргументированное, но...
По-моему, указатели на массивы на языке, потому что они попали на язык. Существуют указатели на каждый другой декларируемый тип данных, так что они тоже здесь. Я никогда не видел, чтобы кто-то получал от них действительно полезную работу. Гипотетически, они позволяют прототипу, который требует массив, а не указатель на элемент, но...
Мне кажется бесполезным делать указатель на массив. В C массив уже является указателем на блок этого типа данных.
int (*ap)[N];
int **ipp;
- оба типа данных (указатель на указатель на Integer). Единственное отличие в том, что существует пространство для N целых чисел, выделенных для ap.
Нет необходимости передавать массив указателем на функцию, например, с целью изменения содержимого массива внутри этой функции, поскольку он уже является указателем. Как правило, я бы сказал, что это не нужно и просто создает дополнительную необходимость разыменовывать указатель, чтобы получить данные в массиве. Но я уверен, что там есть программа или алгоритм, который мог бы найти для нее законное использование.
Следующий код является частью моей статьи: Указатели и массивы в C С++
Вы можете проверить это @http://pointersandarrays.blogspot.com/
Указатели и 2D-массивы
Следующий фрагмент кода иллюстрирует, как объявить и получить доступ к 2D-массиву. Под 2D-массивом находится одномерный массив. Вы будете уверены в этом после игры со следующим фрагментом кода.
Фрагмент кода # 4
#include<iostream&rt;
using namespace std;
int main()
{
cout<< "Understanding Pointers and 2 D Arrays"<<endl;
cout<< "----------------------\n"<<endl;
//Declaration of a 2D Array.
int tab[3][5];
//Total space required : 3*5 * sizeof(int) = 15 * sizeof(int)
//Funda : Since the amount of required space is known by compiler, contiguous 15 memory cells are allocated here.
//Hence the case is similar to a 1 D Array of size 15. Lets try this out!
//Array initialization using array name
for(int i=0; i<3;i++)
for(int j=0; j<5;j++)
tab[i][j]=i+2*j;
//Print array using array name
cout << "\nPrint array using array name ..."<<endl;
for(int i=0; i<3;i++)
{
for(int j=0; j<5;j++)
printf("%2d ",tab[i][j] );
printf("\n");
}
//Print array using a pointer. Proof of 1 D array being allocated
cout << "\nPrint array using a pointer. Proof of 1 D array being allocated ..." << endl;
int *tptr;
tptr = &tab[0][0]; // pointer tptr points at first element of the array.
for(int i=0; i<15;i++)
printf("%d ",*(tptr+i) );
tptr = &tab[0][0];
cout << "\nNotice that array is printed row by row in a linear fashion."<<endl;
return 0;
}
Выход №4:
Understanding Pointers and 2D Arrays
Print array using array name ...
0 2 4 6 8
1 3 5 7 9
2 4 6 8 10
Print array using a pointer. Proof of 1 D array being allocated ...
0 2 4 6 8 1 3 5 7 9 2 4 6 8 10
Notice that array is printed row by row in a linear fashion.
код >