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



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

535   9  

9 ответов:

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

вот массив:

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 элементы вперед после того, на что указывает указатель, а затем взять все, что есть. Использование указателя арифметический синтаксис,x[y] также можно записать как *(x+y).

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

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

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

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 */

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

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

Edit

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

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

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

char a[10];

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

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

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

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

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

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

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

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


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

массив объявлен так

int a[10];

выделяет память на 10 intы. Вы не можете изменить 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

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

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]);

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

#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

упс :-)

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

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);

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

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 указатель.

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

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

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

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

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'

Так что ответ вроде "Да".

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

Comments

    Ничего не найдено.