Является ли 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.,

Альф

Другие вопросы по тегам