Является ли char* arrayName[][] в C / C++ указателем на указатель на указатель ИЛИ указателем на указатель?
Я понимал многомерные массивы как указатели на указатели, но, возможно, я ошибаюсь?
Например, я, хотя:
char * var
знак равно char var[]
char ** var
знак равно char* var[]
или же char var[][]
char *** var
знак равно char var[][][]
или же char* var[][]
или же char** var[]
Это неверно? Я был сбит с толку, потому что я видел char*[][], брошенный как char** в простом примере из учебника.
Я вставил пример ниже. Кто-нибудь может прояснить это для меня? Спасибо!
/* A simple dictionary. */
#include <stdio.h>
#include <string.h>
#include <ctype.h>
/* list of words and meanings */
char *dic[][40] = {
"atlas", "A volume of maps.",
"car", "A motorized vehicle.",
"telephone", "A communication device.",
"airplane", "A flying machine.",
"", "" /* null terminate the list */
};
int main(void)
{
char word[80], ch;
char **p;
do {
puts("\nEnter word: ");
scanf("%s", word);
p = (char **)dic;
/* find matching word and print its meaning */
do {
if(!strcmp(*p, word)) {
puts("Meaning:");
puts(*(p+1));
break;
}
if(!strcmp(*p, word)) break;
p = p + 2; /* advance through the list */
} while(*p);
if(!*p) puts("Word not in dictionary.");
printf("Another? (y/n): ");
scanf(" %c%*c", &ch);
} while(toupper(ch) != 'N');
return 0;
}
5 ответов
Правило для C следующее:
6.3.2.1 L-значения, массивы и обозначения функций
...
3 За исключением случаев, когда он является операндом оператора sizeof или унарного оператора &, или является строковым литералом, используемым для инициализации массива, выражение с типом '' массив типа '' преобразуется в выражение с указателем типа '' набрать '', который указывает на начальный элемент объекта массива и не является lvalue. Если объект массива имеет класс хранения регистров, поведение не определено.
Язык для C++ немного отличается:
4.2 Преобразование массива в указатель [conv.array]
1 Значение l или значение типа "массив N T" или "массив неизвестных границ T" может быть преобразовано в значение типа "указатель на T". Результатом является указатель на первый элемент массива.
...
8.3.4 Массивы [dcl.array]
...
7 Для многомерных массивов соблюдается непротиворечивое правило. Если E является n-мерным массивом ранга i × j ×... × k, то E, присутствующий в выражении, преобразуется в указатель на (n −1) -мерный массив с рангом j ×... × k, Если к этому указателю применяется оператор *, явный или неявный, как результат подписки, то результатом является указатель на (n −1) -мерный массив, который сам сразу преобразуется в указатель.
Таким образом, справедливо следующее:
Declaration Expression Type Decays to
----------- ---------- ---- ---------
T a[N] a T [N] T *
&a T (*)[N]
*a T
a[i] T
T a[M][N] a T [M][N] T (*)[N]
&a T (*)[M][N]
*a T [N] T *
a[i] T [N] T *
&a[i] T (*)[N]
*a[i] T
a[i][j] T
T a[M][N][O] a T [M][N][O] T (*)[M][N]
&a T (*)[M][N][O]
*a T [M][N] T (*)[N]
a[i] T [M][N] T (*)[N]
&a[i] T (*)[M][N]
*a[i] T [N] T *
a[i][j] T [N] T *
&a[i][j] T (*)[N]
*a[i][j] T
a[i][j][k] T
Шаблон должен быть четким для многомерных массивов.
Итак, давайте проанализируем ваш словарь:
/* list of words and meanings */
char *dic[][40] = {
"atlas", "A volume of maps.",
"car", "A motorized vehicle.",
"telephone", "A communication device.",
"airplane", "A flying machine.",
"", "" /* null terminate the list */
};
Это не настроит ваш словарь так, как вы хотите; Вы в основном настроили это как массив из 1 элемента из 40 указателей на символ. Если вам нужен массив пар строк, то объявление должно выглядеть так:
char *dic[][2] =
{
{"atlas", "A volume of maps"},
{"car", "A motorized vehicle"},
{"telephone", "A communication device"},
{"airplane" , "A flying machine"},
{NULL, NULL} // empty strings and NULLs are different things.
};
Тип dic
является "5-элементным массивом из 2-элементных массивов указателя на символ", или char *[5][2]
, Исходя из приведенных выше правил, выражение dic
должен распасться на char *(*)[2]
- указатель на 2-элементный массив указателей на char.
Функция для поиска в этом словаре будет выглядеть так:
char *definition(char *term, char *(*dictionary)[2]) // *NOT* char ***dictionary
{
while ((*dictionary)[0] != NULL && strcmp((*dictionary)[0], term) != 0)
dictionary++;
return (*dictionary)[1];
}
и вы бы назвали это из вашей основной функции, как
char *def = definition(term, dic);
Обратите внимание, что мы должны использовать круглые скобки вокруг *dictionary
выражение в функции. Оператор индекса массива []
имеет более высокий приоритет, чем оператор разыменования *
и мы не хотим подписываться на dictionary
напрямую, мы хотим вписать в массив, dictionary
указывает на.
Я понимал многомерные массивы как указатели на указатели, но, возможно, я ошибаюсь?
Да вы не правы Существует разница между массивом и указателем. Массив может распасться на указатель, но указатель не несет состояния о размере или конфигурации массива, на который он указывает. Не путайте этот автоматический распад с идеей, что массивы и указатели одинаковы - это не так.
char **
это указатель на блок памяти, содержащий символьные указатели, которые сами указывают на блоки памяти символов. char [][]
это один блок памяти, который содержит символы
Если у тебя есть char **
и получить к нему доступ, используя ptr[x][y]
компилятор изменяет это в *(*(ptr + x)+y)
, Если у тебя есть char [][]
, компилятор меняется arr[x][y]
в *(ptr + rowLength*y + x)
, (Примечание: я не на 110% положительно отношусь к порядку X и Y здесь, но это не имеет значения для пункта, который я здесь делаю) Обратите внимание, что, учитывая указатель, компилятор ничего не знает о размере или размеры массива, и не может определить фактический адрес, если вы рассматриваете указатель как многомерный массив.
char *dic[][40]
это массив массивов размера сорок, которые содержат символьные указатели. Поэтому он не соответствует вашему назначению там вообще.
p = (char **)dic;
<- Вот почему броски плохие. Компилятор говорил вам, что вы действительно хотите сделать, чтобы dic
не имеет никакого смысла. Но так как вы можете привести указатель к любому другому указателю, приведение завершится успешно, даже если попытка прочитать данные таким образом приведет к неопределенному поведению.
Вы должны обратиться к "правому левому правилу". В качестве альтернативы вы можете расшифровать большинство объявлений C-ish здесь
Так,
char *p[2][3]
анализируется как
p является массивом из 2 элементов, где каждый элемент является массивом из 3 элементов, так что каждый элемент является указателем на символ.([] связывается сильнее *)
char (*p)[2][3]
анализируется как
"p является указателем на массив из 2 элементов, где каждый элемент является массивом из 3 элементов". (скобка связывает сильнейшего)
Не рассматривал слишком подробно, но я думаю, что автор полагается на то, чтобы выложить 2d массив строк следующим образом:
ключ, значение, ключ, значение, ключ, значение в непрерывной памяти. Затем обойти этот массив как 1d массив строк p = (char **)dic;
Это одна из красот и потенциальных проблем с C - у него много низкого уровня мощности, но вы должны защитить себя приличным кодом, чтобы предотвратить побочные эффекты.
Одно из моих правил запоминания для комбинаций *
а также []
это подпись main
, Работает!:-)
Ваш dic
массив из 40 элементов, каждый элемент которого является указателем на char
,
#include <iostream>
#include <typeinfo>
using namespace std;
template< class Type, unsigned N >
void tellMeAbout( Type const (&)[N] )
{
cout << "Well, it's an array of " << typeid( Type ).name() << ".\n";
}
int main()
{
char *dic[][40] = { 0 };
tellMeAbout( dic );
}
Используя Visual C++, я получаю...
Ну, это массив символов * [40].
Ура & hth.,
Альф