Невозможно привести массив к указателю - PullRequest
6 голосов
/ 12 декабря 2011

У меня есть следующий источник:

#include <iostream>
using namespace std;

void main(int j)
{
    char arr[10][10];
    char** ptr;
    ptr = arr;
}

при компиляции с использованием VS2010 я получаю эту ошибку:

error : a value of type "char (*)[10]" cannot be assigned to an entity of type "char **"

Я думал, что массивы в C ++ были просто указателями. Таким образом, char[][] также может быть char**. Что я делаю не так?

Ответы [ 8 ]

12 голосов
/ 12 декабря 2011

Массивы не являются указателями.

Массив распадается на указатель в большинстве случаев, но это не рекурсивно. Таким образом, T[] распадается до T *, но T[][] не распадается до T**.

Предлагаю прочитать всю главу C FAQ , посвященную массивам и указателям ; в частности, раздел о 2D массивах и указателях на указатели .

7 голосов
/ 06 июля 2013

Существующие ответы, хотя и правильные, не дают четкого объяснения тому, что существует фундаментальная причина (кроме языковых правил), почему вы не можете привести char [10][10] к char **.Даже если вы заставите актеров произнести что-то вроде

char arr[2][2];
char ** ptr = (char **)arr;

, на самом деле это не сработает.

Причина в том, что в C и C ++ двумерный массив размещается в памяти как массив массивов.То есть, в C двумерный массив размещается в памяти как одно выделение,

arr -> arr[0][0]
       arr[0][1]
       arr[1][0]
       arr[1][1]

Вы заметите, что arr указывает не на char *, а на arr[0][0] который является char;следовательно, хотя arr может быть приведено к char *, оно не может быть приведено к char **.

Правильное принудительное приведение будет

char arr[2][2];
char * ptr = (char *)arr;

Если вы не хотите принудительно применять приведение (всегда хорошая идея, если это возможно!), Вы скажете

char arr[2][2];
char * ptr = arr[0];

или, чтобы сделать результат более понятным,

char arr[2][2];
char * ptr = &arr[0][0];

И теперь у вас есть (фактически) указатель на массив из 4 символов.[Proviso: Я не могу найти ничего в стандарте C, который запрещает реализации добавлять отступы между двумя строками массива, но я не верю, что это делают какие-либо реальные реализации, и обычная практика кодирования зависит от предположениячто такого дополнения не будет.]

Если вам действительно нужно преобразовать arr в char **, вам придется явно создать массив указателей:

char arr[2][2]
char * arr_ptrs[2];
char ** ptr;
arr_ptrs[0] = arr[0];
arr_ptrs[1] = arr[1];
ptr = arr_ptrs;

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

В Java, для сравнения, двумерный массив всегда является массивом указателей на массивы, так что массив

char arr[][] = { {'a', 'b'}, {'c', 'd'} };

размещается в памяти как три отдельных выделения, впроизвольный порядок и не обязательно соседний,

arr -> arr[0]
       arr[1]

arr[0] -> arr[0][0]
          arr[0][1]

arr[1] -> arr[1][0]
          arr[1][1]

Вы сразу заметите, что для этого требуется больше памяткичем эквивалентный массив C, и медленнее оценивать во время выполнения.С другой стороны, он позволяет разным длинам строк массива.

4 голосов
/ 12 декабря 2011

Типы char[10][10] и char** и char (*)[10] - это разные типы.Тем не менее, первый не может преобразовать во второй, он может преобразовать в третий.

Поэтому попробуйте это:

char arr[10][10];
char (*ptr)[10];
ptr = arr; //ok

Это будет работать, потому что, как я уже сказал, объект типа char[10][10] может преобразовываться в объект типа char (*)[10].Это совместимые типы.

1 голос
/ 12 декабря 2011

Я думал, что массивы в C ++ были просто указателями.

Нет, массив - это набор объектов, расположенных непрерывно в памяти. В некоторых случаях они могут быть преобразованы в указатель на первый элемент.

Так что char[][] также может быть char**

Нет. Он может быть преобразован в указатель на первый одномерный массив (тип char (*)[10], указанный в сообщении об ошибке); но этот массив не является указателем, поэтому он не может быть преобразован в указатель-указатель.

0 голосов
/ 05 августа 2012

Когда вы приведете ar [10] [10] к указателю, вы получите массив указателей, как указано выше * ar [10], а не ** ar.

0 голосов
/ 12 декабря 2011

Массивы не являются указателями (хотя я заметил, что многие книги заставляют вас думать об этом). Это нечто совершенно другое. Указатель - это адрес памяти, а массив - это непрерывный набор некоторых данных.

В некоторых случаях массив может распадаться на указатель на свой первый элемент. Затем вы можете использовать арифметику указателей для итерации по непрерывной памяти. Примером этого случая может быть передача массива в функцию в качестве параметра.

То, что вы, вероятно, хотите сделать здесь, выглядит примерно так:

char arr[10];
char * i = &arr[0];

Очевидно, вам нужно использовать 2D-массивы и символы ** в вашем случае. Я оставлю это вам, чтобы выяснить:)

0 голосов
/ 12 декабря 2011

Массивы - это НЕ просто указатели - массивы - это массивы. Однако массивы не являются первоклассными типами, поэтому их нельзя использовать во многих местах. То, что вызывает ваше замешательство, заключается в том, что имена массивов МОЖНО неявно преобразовывать в указатели на первый элемент массива, что означает, что вы можете использовать массив во многих местах, где вам нужен указатель, и он «просто работает». Это, однако, не одно из тех мест.

0 голосов
/ 12 декабря 2011

Ошибка точно говорит вам, что неправильно, двухмерный массив может быть назначен указателю на массив, а не двойному указателю. Итак, что вам нужно:

char (*ptr)[10] = arr; 

Что я делаю не так?

Перво-наперво
Массивы не являются указателями !! , но иногда они действуют как указатели.

Правило:

Выражение с типом массива (которое может быть именем массива) преобразуется в указатель всякий раз, когда тип массива недопустим, но тип указателя является.

Итак, если у вас есть одномерный массив:

char arr[10];

Затем arr распадается на адрес нулевого элемента, который имеет тип char *. Отсюда:

char *ptr = arr;

Но если у вас есть двумерный массив, который по сути является массивом массивов.

 char arr[10][10];

Затем arr распадается на указатель на массив из 10 символов.

Итак, чтобы присвоить arr чему-то, вам понадобится что-то, соответствующее типу, который является указателем на массив символов.
Отсюда:

char (*ptr)[10] = arr; 
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...