Если Вы не первый год программируете на языке C, то наверное давно заметили, что имена массивов и имена указателей можно использовать примерно одинаково. Например, у нас есть массив char Array[10] и указатель char *ptr, и для обоих имен Array и ptr допустимы одинаковые операции:
char Array[10];
char *ptr1;
char *ptr2;
ptr = Array;
//Обнуление блока памяти через имя массива
// и имя указателя работает одинаково:
memset(Array,0,sizeof(Array));
memset(ptr,0,10);
Array[0] = 'A'; // и так можно...
ptr1[1] = 'B'; // и так тоже можно, теперь в массиве
// Array содержится строка "AB"...
//Указателю можно присвоить как адрес массива,
// так и значение указателя:
ptr2 = Array+1;
ptr2 = ptr+2;
Возникает логичный вопрос: может быть, и массив, и указатель это одно и то же? Но почему тогда нельзя объявить массив, а потом в заголовочном файле объявить это же имя через extern как указатель?
char Array[10];
...
extern char *Array; //так нельзя, компилятор выдаст ошибку,
extern char Array[]; //а так можно, проблем нет.
Очевидно, указатель и массив все-таки чем-то отличаются друг от друга. Ниже в таблице приведены эти отличия.
Указатель |
Массив |
1. Указатель это некоторое место в памяти, которое содержит адрес какой-то другой области памяти. |
1. Массив это одиночный, заранее выделенный кусок памяти, где однотипные элементы непрерывно следуют друг за другом. Массив имеет изначально фиксированный размер и фиксированное размещение. |
2. Несмотря на то, что указатель можно инициализировать при определении, нельзя при определении указателя проинициализировать память, на которую указывает указатель. |
2. Содержимое массива, на которое указывает его имя, может быть проинициализировано в момент определения массива. Например int num[] = {2, 4, 5}; |
3. По своей природе указатель динамичен. Часто он используется для указания на выделенный блок памяти, у которой может быть впоследствии изменен размер или которая может быть позже освобождена. Также указатели используются для массивов, имя которых передается в функцию в качестве параметра (см. пример вызова memset с массивом Array). |
3. Массив по своей природе статичен. Т. е. память, которая выделена под массив, не может изменить свой размер, и не может быть впоследствии освобождена. |
4. Двоичный код, который генерируется для работы с указателями, отличается от кода, который генерируется при работе с массивами. |
4. Двоичный код, который генерируется для работы с указателями, отличается от кода, который генерируется при работе с массивами. |
[Явные отличия указателя и массива]
Ниже несколько примеров, которые демонстрируют отличия указателя и массива.
#include < stdio.h >
int main()
{
int arr[] = {10, 20, 30, 40, 50, 60};
int *ptr = arr;
// будет выведено sizof(int) * (количество элементов в массиве arr[])
printf("Размер arr[] равен %d\n", sizeof(arr));
// выведенный размер указателя будет одинаковым для всех типов
// указателей (char *, void *, и т. д.)
printf("Размер ptr равен %d", sizeof(ptr));
return 0;
}
Этот пример выведет следующее:
Размер arr[] равен 24
Размер ptr равен 4
Также нельзя назначить любой адрес переменной массива:
#include < stdio.h >
int main()
{
int arr[] = {10, 20}, x = 10;
int *ptr = &x; // эта строка скомпилируется нормально
arr = &x; // в этом месте компилятор выдаст ошибку
return 0;
}
Компилятор выведет примерно следующее:
Compiler Error: incompatible types when assigning to
type 'int[2]' from type 'int *'
Таким образом, можно сделать вывод, что массив определяется как постоянный указатель, у которого не может быть изменен адрес (char const *Array).
[Чем похожи указатель и массив]
Хотя массив и указатель - разные сущности языка C, некоторые их свойства выглядят одинаково.
1. Имя массива дает адрес первого элемента массива. Этот адрес можно присваивать указателю. Пример:
#include < stdio.h >
int main()
{
int arr[] = {10, 20, 30, 40, 50, 60};
int *ptr = arr; // Значению указателя будет присвоен адрес массива
printf("Значение первого элемента равно %d", *ptr);
return 0;
}
Если пример запустить, то он выведет следующее:
Значение первого элемента равно 10
2. К элементам массива можно получить доступ с помощью арифметики указателей. Компилятор использует эту арифметику для доступа к элементу массива. Например, выражение наподобие "arr[i]" обрабатывается компилятором как *(arr + i). Так что выражения наподобие *(arr + i) работают для массива так же, как и выражения наподобие ptr[i] для указателя.
#include < stdio.h >
int main()
{
int arr[] = {10, 20, 30, 40, 50, 60};
int *ptr = arr;
printf("arr[2] = %d\n", arr[2]);
printf("*(ptr + 2) = %d\n", *(arr + 2));
printf("ptr[2] = %d\n", ptr[2]);
printf("*(ptr + 2) = %d\n", *(ptr + 2));
return 0;
}
Этот код выведет:
arr[2] = 30
*(ptr + 2) = 30
ptr[2] = 30
*(ptr + 2) = 30
3. Если массив передается в параметре функции, то он всегда передается как указатель, даже если указать имя массива с квадратными скобками. Пример:
#include < stdio.h >
int fun(int ptr[])
{
int x = 10;
// Будет выведен размер указателя
printf("sizeof(ptr) = %d\n", sizeof(ptr));
// Это возможно, потому что ptr это указатель, а не массив.
ptr = &x;
printf("*ptr = %d ", *ptr);
return 0;
}int main()
{
int arr[] = {10, 20, 30, 40, 50, 60};
fun(arr);
return 0;
}
Код выведет:
sizeof(ptr) = 4
*ptr = 10
[Ссылки]
1. Difference between pointer and array in C? site:geeksforgeeks.org. |