Что делает следующий код?
int g[] = {9,8};
int (*j) = g;
По моему мнению, он создает указатель на массив из 2 целых чисел. Но тогда почему это работает:
int x = j[0];
и это не работает:
int x = (*j)[0];
Что делает следующий код?
int g[] = {9,8};
int (*j) = g;
По моему мнению, он создает указатель на массив из 2 целых чисел. Но тогда почему это работает:
int x = j[0];
и это не работает:
int x = (*j)[0];
Скобки в вашем примере являются излишними. Указателю не важно, присутствует ли массив - он знает только, что он указывает на int
int g[] = {9,8};
int (*j) = g;
также можно переписать как
int g[] = {9,8};
int *j = g;
который также можно было бы переписать как
int g[] = {9,8};
int *j = &g[0];
указатель на массив будет выглядеть как
int g[] = {9,8};
int (*j)[2] = &g;
//Dereference 'j' and access array element zero
int n = (*j)[0];
Там хорошо читайте на объявлениях указателей (и как их набросать) по этой ссылке здесь: http://www.codeproject.com/Articles/7042/How-to-interpret-complex-C-C-declarations
int g[] = {9,8};
Это объявляет объект типа int [2] и инициализирует его элементы {9,8}
int (*j) = g;
Это объявляет объект типа int * и инициализирует его указателем на первый элемент g.
Тот факт, что второе объявление инициализирует j чем-то другим, отличным от g, довольно странно. C и С++ просто имеют эти странные правила о массивах, и это один из них. Здесь выражение g
неявно преобразуется из lvalue, относящегося к объекту g, в r-значение типа int*
, которое указывает на первый элемент g.
Это преобразование происходит в нескольких местах. На самом деле это происходит, когда вы делаете g[0]
. Оператор индекса массива фактически не работает на массивах, только на указателях. Таким образом, оператор int x = j[0];
работает, потому что g[0]
выполняет то же самое неявное преобразование, которое было выполнено при инициализации j
.
Указатель на массив объявляется следующим образом
int (*k)[2];
и вы точно знаете, как это будет использоваться
int x = (*k)[0];
(обратите внимание, как "объявление следует использовать" , т.е. синтаксис объявления переменной типа имитирует синтаксис для использования переменной этого типа.)
Однако обычно не используется указатель на массив. Вся цель специальных правил вокруг массивов заключается в том, что вы можете использовать указатель на элемент массива, как если бы это был массив. Так что идиоматическому C вообще не важно, что массивы и указатели - это не одно и то же, и правила не позволяют вам делать что-либо полезное непосредственно с массивами. (например, вы не можете скопировать массив вроде: int g[2] = {1,2}; int h[2]; h = g;
)
Примеры:
void foo(int c[10]); // looks like we're taking an array by value.
// Wrong, the parameter type is 'adjusted' to be int*
int bar[3] = {1,2};
foo(bar); // compile error due to wrong types (int[3] vs. int[10])?
// No, compiles fine but you'll probably get undefined behavior at runtime
// if you want type checking, you can pass arrays by reference (or just use std::array):
void foo2(int (&c)[10]); // paramater type isn't 'adjusted'
foo2(bar); // compiler error, cannot convert int[3] to int (&)[10]
int baz()[10]; // returning an array by value?
// No, return types are prohibited from being an array.
int g[2] = {1,2};
int h[2] = g; // initializing the array? No, initializing an array requires {} syntax
h = g; // copying an array? No, assigning to arrays is prohibited
Поскольку массивы настолько несовместимы с другими типами на C и С++, вы должны просто избегать их. С++ имеет std::array
, что намного более согласованно, и вы должны использовать его, когда вам нужны массивы статического размера. Если вам нужны массивы с динамическим размером, ваш первый вариант - std::vector.
j[0];
разделяет указатель на int
, поэтому его тип int
.
(*j)[0]
не имеет типа. *j
разделяет указатель на int
, поэтому он возвращает int
, а (*j)[0]
пытается разыменовать int
. Это похоже на попытку int x = 8; x[0];
.