Учебники

14) Указатели на С

Что такое указатель?

POINTER — это переменная, в которой хранится адрес другой переменной. Указатель также можно использовать для ссылки на другую функцию указателя. Указатель можно увеличивать / уменьшать, т. Е. Указывать на следующую / предыдущую ячейку памяти. Назначение указателя — сэкономить место в памяти и ускорить выполнение.

Как работает указатель?

Если мы объявим переменную v типа int, v фактически сохранит значение.

V теперь равен нулю.

Однако каждая переменная, кроме значения, также имеет свой адрес (или, проще говоря, там, где она находится в памяти). Адрес можно получить, поместив амперсанд (&) перед именем переменной.

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

Давайте попробуем это на практике.

Выход этой программы -480613588.

Теперь, что такое указатель? Вместо сохранения значения указатель будет хранить адрес переменной.

Int * y = & v;

ПЕРЕМЕННЫЕ

УКАЗАТЕЛЬ

Значение сохраняется в имени хранение / адрес памяти

Переменная , что указывает на адрес хранения / памяти другой переменной

Объявление указателя

Как и переменные, указатели должны быть объявлены до того, как они могут быть использованы в вашей программе. Указатели могут быть названы как угодно, если они подчиняются правилам именования Си. Объявление указателя имеет следующую форму.

data_type * pointer_variable_name;

Вот,

  • data_type является базовым типом указателя типов переменных C и указывает тип переменной, на которую указывает указатель.
  • Звездочка (*: та же, что и для умножения звездочка), являющаяся оператором косвенности, объявляет указатель.

Давайте посмотрим некоторые правильные объявления указателя

int    *ptr_thing;            /* pointer to an integer */ 
int *ptr1,thing;/* ptr1 is a pointer to type integer and thing is an integer variable */
double    *ptr2;    /* pointer to a double */
float    *ptr3;      /* pointer to a float */
char    *ch1 ;       /* pointer to a character */
float  *ptr, variable;/*ptr is a pointer to type float and variable is an ordinary float variable */

Инициализировать указатель

После объявления указателя мы инициализируем его как стандартные переменные с адресом переменной. Если указатели не инициализированы и не используются в программе, результаты непредсказуемы и могут привести к катастрофическим последствиям.

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

 pointer = &variable; 

Простая программа для иллюстрации указателя приведена ниже:

#include <stdio.h>
int main()
{
   int a=10;    //variable declaration
   int *p;      //pointer variable declaration
   p=&a;        //store address of variable a in pointer p
   printf("Address stored in a variable p is:%x\n",p);  //accessing the address
   printf("Value stored in a variable p is:%d\n",*p);   //accessing the value
   return 0;
}

Вывод:

Address stored in a variable p is:60ff08
Value stored in a variable p is:10
оператор Смысл
* Служит 2 цели

  1. Объявление указателя
  2. Возвращает значение указанной переменной
& Служит только 1 цели

  • Возвращает адрес переменной

Типы указателя

Нулевой указатель

Мы можем создать нулевой указатель, назначив нулевое значение во время объявления указателя. Этот метод полезен, когда у вас нет адреса, назначенного указателю. Пустой указатель всегда содержит значение 0.

Следующая программа иллюстрирует использование нулевого указателя:

#include <stdio.h>
int main()
{
	int *p = NULL; 	//null pointer
	printf(“The value inside variable p is:\n%x”,p);
	return 0;
}

Вывод:

The value inside variable p is:
0

Пустой указатель

В C-программировании указатель void также называется универсальным указателем. У него нет стандартного типа данных. Указатель void создается с использованием ключевого слова void. Может использоваться для хранения адреса любой переменной.

Следующая программа иллюстрирует использование пустого указателя:

#include <stdio.h>
int main()
{
void *p = NULL; 	//void pointer
printf("The size of pointer is:%d\n",sizeof(p));
return 0;
}

Вывод:

The size of pointer is:4

Дикий указатель

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

Следующая программа иллюстрирует использование дикого указателя:

#include <stdio.h>
int main()
{
int *p; 	//wild pointer
printf("\n%d",*p);
return 0;
}

Вывод

timeout: the monitored command dumped core
sh: line 1: 95298 Segmentation fault      timeout 10s main

Другие типы указателей в ‘c’ следующие:

  • Свисающий указатель
  • Сложный указатель
  • Ближний указатель
  • Дальний указатель
  • Огромный указатель

Указатели прямого и косвенного доступа

В C есть два эквивалентных способа доступа и управления переменным содержимым

  • Прямой доступ: мы используем непосредственно имя переменной
  • Косвенный доступ: мы используем указатель на переменную

Давайте разберемся с помощью программы ниже

#include <stdio.h>
/* Declare and initialize an int variable */
int var = 1;
/* Declare a pointer to int */
int *ptr;
int main( void )
{
/* Initialize ptr to point to var */
ptr = &var;
/* Access var directly and indirectly */
printf("\nDirect access, var = %d", var);
printf("\nIndirect access, var = %d", *ptr);
/* Display the address of var two ways */
printf("\n\nThe address of var = %d", &var);
printf("\nThe address of var = %d\n", ptr);
/*change the content of var through the pointer*/
*ptr=48;
printf("\nIndirect access, var = %d", *ptr);
return 0;}

После компиляции программы без каких-либо ошибок, результат:

Direct access, var = 1
Indirect access, var = 1

The address of var = 4202496
The address of var = 4202496

Indirect access, var = 48

Арифметика указателей

Операции с указателями сведены на следующем рисунке

Операции с указателями

Приоритетная операция (приоритет)

При работе с указателями мы должны соблюдать следующие правила приоритета:

  • Операторы * и & имеют тот же приоритет, что и унарные операторы (отрицание !, увеличение ++, уменьшение—).
  • В том же выражении унарные операторы *, &,!, ++, — оцениваются справа налево.

Если указатель P указывает на переменную X, тогда * P можно использовать везде, где можно записать X.

Следующие выражения эквивалентны:

int X = 10

int * P = & Y;

Для приведенного выше кода ниже выражения являются истинными

выражение Эквивалентное выражение
Y = * P + 1

* P = * P + 10

* P + = 2

++ * P

(* P) ​​++

Y = X + 1

X = X + 10

X + = 2

++ X

X ++

В последнем случае необходимы скобки: поскольку унарные операторы * и ++ вычисляются справа налево, без скобок будет увеличиваться указатель P, а не объект, на который указывает P.

В таблице ниже приведены основные арифметические операции, которые можно использовать при работе с указателями.

операция объяснение
присваивание int * P1, * P2 P1 = P2; P1 и P2 указывают на одну и ту же целочисленную переменную
Увеличение и уменьшение Int * P1; P1 ++; P1—;
Добавление смещения (константа) Это позволяет указателю перемещать N элементов в таблице. Указатель будет увеличен или уменьшен в N раз на количество байтов типа переменной. P1 + 5;

Указатели и массивы

Традиционно мы обращаемся к элементам массива, используя его индекс, но этот метод можно устранить с помощью указателей. Указатели облегчают доступ к каждому элементу массива.

#include <stdio.h>
int main()
{
    int a[5]={1,2,3,4,5};   //array initialization
    int *p;     //pointer declaration
               /*the ptr points to the first element of the array*/

    p=a; /*We can also type simply ptr==&a[0] */
    
    printf("Printing the array elements using pointer\n");
    for(int i=0;i<5;i++)    //loop for traversing array elements
    {
        	printf("\n%x",*p);  //printing array elements
        	p++;    //incrementing to the next element, you can also write p=p+1
    }
    return 0;
}

Вывод

1
2
3
4
5

Добавление определенного числа к указателю переместит местоположение указателя на значение, полученное с помощью операции сложения. Предположим, что p является указателем, который в данный момент указывает на ячейку памяти 0, если мы выполним следующую операцию сложения, p + 1, то она будет выполняться следующим образом:

Добавление / увеличение указателя

Поскольку p в настоящее время указывает на местоположение 0 после добавления 1, значение станет 1, и, следовательно, указатель будет указывать на ячейку памяти 1.

Указатели и Строки

Строка — это массив объектов char, заканчивающийся нулевым символом ‘\ 0’. Мы можем манипулировать строками, используя указатели. Вот пример, который объясняет этот раздел

#include <stdio.h>
#include <string.h>
int main()
{
char str[]="Hello Guru99!";
char *p;
p=str;
printf("First character is:%c\n",*p);
p =p+1;
printf("Next character is:%c\n",*p);
printf("Printing all the characters in a string\n");
p=str;  //reset the pointer
for(int i=0;i<strlen(str);i++)
{
printf("%c\n",*p);
p++;
}
return 0;
}

Вывод

First character is:H
Next character is:e
Printing all the characters in a string
H
e
l
l
o

G
u
r
u
9
9
!

Другой способ работы со строками — использовать массив указателей, как в следующей программе:

#include <stdio.h>
int main(){
char *materials[ ] = {  "iron",  "copper",  "gold"};
printf("Please remember these materials :\n");
int i ;
for (i = 0; i < 3; i++) {
  printf("%s\n", materials[ i ]);}
  return 0;}

Вывод:

Please remember these materials:
iron
copper
gold

Преимущества указателей

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

Недостатки указателей

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

Резюме

  • Указатель — это не что иное, как область памяти, где хранятся данные.
  • Указатель используется для доступа к ячейке памяти.
  • Существуют различные типы указателей, такие как пустой указатель, дикий указатель, пустой указатель и другие типы указателей.
  • Указатели могут использоваться с массивом и строкой для более эффективного доступа к элементам.
  • Мы можем создать указатели на функции для динамического вызова функции.
  • Арифметические операции могут быть выполнены с указателем, который известен как арифметика указателя.
  • Указатели также могут указывать на функцию, которая облегчает вызов различных функций в случае определения массива указателей.
  • Если вы хотите использовать другой тип данных переменной, вы можете использовать указатель типа void.