Является ли char * arrayName [] [] в C / C ++ указателем на указатель на указатель ИЛИ указателем на указатель? - PullRequest
4 голосов
/ 13 октября 2010

Я понимал многомерные массивы как указатели на указатели, но, возможно, я ошибаюсь?

Например, я, хотя:

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 ]

7 голосов
/ 13 октября 2010

Правило для C следующее:

6.3.2.1 L-значения, массивы и обозначения функций
...
3 Кромекогда это операнд оператора sizeof или унарный оператор & , или строковый литерал, используемый для инициализации массива, выражение, имеющее тип '' массив типа ''преобразуется в выражение с типом '' указатель на тип '', которое указывает на начальный элемент объекта массива и не является lvalue.Если объект массива имеет класс хранения регистров, поведение не определено.

Язык для C ++ немного отличается:

4.2 Преобразование массива в указатель [conv.array]

1 Значение l или значение типа «массив NT» или «массив неизвестных границ 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»,или 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 указывает на .

5 голосов
/ 13 октября 2010

Вам необходимо обратиться к «Правое левое правило» . В качестве альтернативы вы можете расшифровать большинство объявлений C-ish на здесь

Итак,

char *p[2][3] анализируется как

p - это массив из 2 элементов, где каждый элемент - это массив из 3 элементов, так что каждый элемент является указателем на символ. ([] Связывается сильнее *)

char (*p)[2][3] анализируется как

"p - указатель на массив из 2 элементов, где каждый элемент является массивом из 3 элементов." (скобка связывает сильнейшего)

5 голосов
/ 13 октября 2010

Я понимал многомерные массивы как указатели на указатели, но, возможно, я ошибаюсь?

Да, вы не правы. Существует разница между массивом и указателем. Массив может распасться на указатель, но указатель не несет состояния о размере или конфигурации массива, на который он указывает. Не путайте это автоматическое затухание с идеей, что массивы и указатели одинаковы - они не совпадают.

A 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; <- Вот почему броски плохие. Компилятор говорил вам, что то, что вы действительно хотите сделать с <code>dic, не имеет никакого смысла. Но поскольку вы можете привести указатель к любому другому указателю, приведение завершится успешно, даже если попытка прочитать данные таким образом приведет к неопределенному поведению.

2 голосов
/ 13 октября 2010

Одно из моих правил запоминания для комбинаций * и [] - это подпись 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.,

- Альф

2 голосов
/ 13 октября 2010

Не рассматривал слишком много деталей, но я думаю, что автор полагается на то, что выложил 2-мерный массив строк так:

ключ, значение, ключ, значение, ключ, значение в непрерывной памяти. Затем обход этого массива в виде 1d массива строк p = (char **)dic;

Это одна из красот и потенциальных проблем с C - у него много низкого уровня мощности, но вы должны защитить себя приличным кодом для предотвращения побочных эффектов.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...