Является ли имя массива указателем?

Является ли имя массива указателем в C? Если нет, в чем разница между именем массива и переменной-указателем?

Ответы

Ответ 1

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

Вот массив:

int a[7];

a содержит пробел для семи целых чисел, и вы можете поместить значение в одно из них с присваиванием, например так:

a[3] = 9;

Вот указатель:

int *p;

p не содержит пробелов для целых чисел, но может указывать на пробел для целых чисел. Мы можем, например, установить его так, чтобы он указывал на одно из мест в массиве a, например на первое:

p = &a[0];

Что может сбить с толку, так это то, что вы также можете написать это:

p = a;

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

Теперь вы можете использовать p аналогично массиву:

p[3] = 17;

Причина этого заключается в том, что оператор разыменования массива в C, [ ], определяется в терминах указателей. x[y] означает: начните с указателя x, y элементы y вперед после того, на что указывает указатель, и затем возьмите все, что там есть. Используя арифметический синтаксис указателя, x[y] также можно записать как *(x+y).

Чтобы это работало с обычным массивом, таким как наше a, имя a в a[3] должно быть сначала преобразовано в указатель (на первый элемент в a). Затем мы продвигаемся на 3 элемента вперед и берем все, что есть. Другими словами: возьмите элемент в позицию 3 в массиве. (Который является четвертым элементом в массиве, поскольку первый из них пронумерован 0.)

Итак, в итоге, имена массивов в программе на C (в большинстве случаев) преобразуются в указатели. Единственное исключение - когда мы используем оператор sizeof для массива. Если a в этом контексте a был преобразован в указатель, sizeof a дал бы размер указателя, а не фактического массива, что было бы довольно бесполезно, так что в этом случае a означает сам массив.

Ответ 2

Когда массив используется как значение, его имя представляет адрес первого элемента.
Когда массив не используется в качестве значения, его имя представляет весь массив.

int arr[7];

/* arr used as value */
foo(arr);
int x = *(arr + 1); /* same as arr[1] */

/* arr not used as value */
size_t bytes = sizeof arr;
void *q = &arr; /* void pointers are compatible with pointers to any object */

Ответ 3

Если выражение большего размера (например, имя массива) появляется в более крупном выражении и оно не является операндом операторов & или sizeof, тогда тип выражения массива преобразуется из "N-элементный массив от T" до "указателя на T", а значение выражения - это адрес первого элемента в массиве.

Короче говоря, имя массива не является указателем, но в большинстве контекстов оно трактуется как указатель.

Edit

Отвечая на вопрос в комментарии:

Если я использую sizeof, я могу подсчитать размер только элементов массива? Затем массив "head" также занимает пространство с информацией о длине и указателе (а это означает, что для этого требуется больше места, чем обычный указатель)?

Когда вы создаете массив, единственным выделенным пространством является пространство для самих элементов; никакое хранение не материализуется для отдельного указателя или любых метаданных. Учитывая,

char a[10];

то, что вы получаете в памяти,

   +---+
a: |   | a[0]
   +---+ 
   |   | a[1]
   +---+
   |   | a[2]
   +---+
    ...
   +---+
   |   | a[9]
   +---+

Выражение a относится ко всему массиву, но нет объекта a отдельно от самих элементов массива. Таким образом, sizeof a дает размер (в байтах) всего массива. Выражение &a дает вам адрес массива, который совпадает с адресом первого элемента. Разница между &a и &a[0] является типом результата 1 - int (*)[10] в первом случае и int * во втором.

В случаях, когда вы хотите получить доступ к отдельным элементам - выражение a[i] определяется как результат *(a + i) - заданное значение адреса a, offset i элементов (а не байтов) из этого адрес и разыгрывать результат.

Проблема заключается в том, что a не является указателем или адресом - это весь объект массива. Таким образом, правило в C, когда всякий раз, когда компилятор видит выражение типа массива (например, a, которое имеет тип char [10]), и это выражение не является операндом операторов sizeof или унарных & тип этого выражения преобразуется ( "распадается" ) в тип указателя (char *), а значение выражения - это адрес первого элемента массива. Следовательно, выражение a имеет тот же тип и значение, что и выражение &a[0] (и по расширению выражение *a имеет тот же тип и значение, что и выражение a[0]).

C был получен из более раннего языка под названием B, а в B a был отдельным объектом-указателем из элементов массива a[0], a[1] и т.д. Ритчи хотел сохранить семантику массива B, но он не " t хотите испортить отдельный объект указателя. Поэтому он избавился от этого. Вместо этого компилятор будет преобразовывать выражения массива в выражения указателя во время перевода по мере необходимости.

Помните, что я сказал, что массивы не хранят метаданные об их размере. Как только выражение массива "распадается" на указатель, все, что у вас есть, является указателем на один элемент. Этот элемент может быть первым из последовательности элементов или может быть единственным объектом. Там нет способа узнать, основываясь на самом указателе.

Когда вы передаете выражение массива функции, вся функция получает указатель на первый элемент - он не знает, насколько велик массив (поэтому функция gets была такой угрозой и в конечном итоге удалены из библиотеки). Чтобы функция знала, сколько элементов имеет массив, вы должны либо использовать значение дозорного (например, 0-терминатор в строках C), либо вы должны передать количество элементов в качестве отдельного параметра.


  • Который * может * влиять на то, как интерпретируется значение адреса, зависит от машины.

Ответ 4

Массив, объявленный таким образом

int a[10];

выделяет память для 10 int s. Вы не можете изменить a, но вы можете сделать арифметику указателя с помощью a.

Указатель, подобный этому, выделяет память только для указателя p:

int *p;

Он не выделяет int s. Вы можете изменить его:

p = a;

и используйте индексы массива, как вы можете, с помощью:

p[2] = 5;
a[2] = 5;    // same
*(p+2) = 5;  // same effect
*(a+2) = 5;  // same effect

Ответ 5

Имя массива само по себе дает ячейку памяти, поэтому вы можете обрабатывать имя массива как указатель:

int a[7];

a[0] = 1976;
a[1] = 1984;

printf("memory location of a: %p", a);

printf("value at memory location %p is %d", a, *a);

И другие полезные свойства, которые вы можете сделать для указателя (например, добавление/вычитание смещения), вы также можете сделать с массивом:

printf("value at memory location %p is %d", a + 1, *(a + 1));

Язык, если C не отображал массив как только своего рода "указатель" (педантично это просто место памяти. Он не может указывать на произвольное местоположение в памяти и не может быть управляемый программистом). Нам всегда нужно это кодировать:

printf("value at memory location %p is %d", &a[1], a[1]);

Ответ 6

Я думаю, что этот пример проливает свет на проблему:

#include <stdio.h>
int main()
{
        int a[3] = {9, 10, 11};
        int **b = &a;

        printf("a == &a: %d\n", a == b);
        return 0;
}

Он компилирует штраф (с 2 предупреждениями) в gcc 4.9.2 и печатает следующее:

a == &a: 1

oops: -)

Итак, вывод - нет, массив не является указателем, он не хранится в памяти (даже не только для чтения) в качестве указателя, даже если это похоже на то, что вы можете получить его адрес с помощью и оператора. Но - oops - этот оператор не работает:-)), в любом случае вы были предупреждены:

p.c: In function ‘main’:
pp.c:6:12: warning: initialization from incompatible pointer type
  int **b = &a;
            ^
p.c:8:28: warning: comparison of distinct pointer types lacks a cast
  printf("a == &a: %d\n", a == b);

С++ отказывается от любых попыток с ошибками во время компиляции.

Edit:

Вот что я хотел продемонстрировать:

#include <stdio.h>
int main()
{
    int a[3] = {9, 10, 11};
    void *c = a;

    void *b = &a;
    void *d = &c;

    printf("a == &a: %d\n", a == b);
    printf("c == &c: %d\n", c == d);
    return 0;
}

Даже если c и a "указывают" на одну и ту же память, вы можете получить адрес указателя c, но вы не можете получить адрес указателя a.

Ответ 7

Имя массива ведет себя как указатель и указывает на первый элемент массива. Пример:

int a[]={1,2,3};
printf("%p\n",a);     //result is similar to 0x7fff6fe40bc0
printf("%p\n",&a[0]); //result is similar to 0x7fff6fe40bc0

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

0x7fff6fe40bc0

Ответ 8

Массив - это совокупность ценных и непрерывных элементов в памяти. В C имя массива - это индекс для первого элемента, и, применяя смещение, вы можете получить доступ к остальным элементам. "Индекс для первого элемента" действительно является указателем на направление памяти.

Разница с указателями-указателями заключается в том, что вы не можете изменить местоположение, на которое указывает имя массива, поэтому оно похоже на указатель const (похоже, не то же самое. См. комментарий Mark). Но также, что вам не нужно разыгрывать имя массива, чтобы получить значение, если вы используете арифметику указателя:

char array = "hello wordl";
char* ptr = array;

char c = array[2]; //array[2] holds the character 'l'
char *c1 = ptr[2]; //ptr[2] holds a memory direction that holds the character 'l'

Таким образом, ответ будет "да".

Ответ 9

Имя массива - это адрес 1-го элемента массива. Поэтому да имя массива - это указатель на const.

Ответ 10

Прежде всего, за K & R, ответ да.

На концептуальном уровне имя массива является ptr, они абсолютно одинаковы, CONCEPTUALLY.

Как компилятор реализует массив до компилятора. Это аргумент между теми, кто говорит, что они не одно и то же, и те, кто говорит, что они.

Компилятор может реализовать int a[5] как указатель a в неназванный кусок хранилища, который может содержать 5 целых чисел. Но они этого не делают. Писателю-компилятору проще генерировать код для простого массива, а затем подталкивать указатель a.

В любом случае, концептуально область данных, выделенная оператором int a[5];, может ссылаться на ptr a или на нее может ссылаться указатель, созданный оператором &a[0].