int a; // Объявление переменной a типа int
int *p; // Объявление указателя p на int
*p = 5; // Ошибка! Указатель указывает на случайную память
p = &a; // Присваиваем указателю адрес переменной a
*p = 5; // Верно (теперь a = 5 и *p = 5)
#include <stdio.h> //printf()
#include <stdio.h> //printf()
int main(){
int a = 7; // Целочисленная переменная a
int *p = &a; // Указатель p на целочисленную переменную a
printf("*p = %d \n", *p); // Разыменование указателя (*p = 7)
printf("&a = %p \n", &a); // Оператор взятия адреса & (&a = 000000000061FE1C)
printf("p = %p \n", p); // Указатель - это адрес (p = 000000000061FE1C)
printf("&p = %p \n", &p); // Указатель тоже размещен в памяти и имеет адрес (&p = 000000000061FE10)
}
Существует мнемоническое правило, позволяющее легко запомнить, к чему относится const. Надо провести черту через *, если const слева, то оно относится к значению данных; если справа — к значению указателя.
Ключевое слово const может стоять и до типа (например int) и после (главное чтобы до *, если после - это уже другое поведение указателя) - разницы никакой
const int * p = &a; // Можно p = &b но нельзя *p = b (аналогично int const * p = &a;)
int const * p = &a; // Аналогично предыдущей записи
int * const p = &a; // Можно *p = b но нельзя p = &b
const int * const p = &a; // Нельзя *p = b и нельзя p = &b (аналогично int const * const p = &a;)
#include <stdio.h> // prinf()
#include <string.h> // strlen()
int my_print_1(const char *str, int num){ // Функция 1
printf("str = %s, num = %d\n", str, num); // Печатаем переданные параметры
return strlen(str); // Возвращаем длину строки
}
int my_print_2(const char *str, int num){ // Функция 2
printf("text = %s, digit = %d\n", str, num); // Печатаем переданные параметры
return strlen(str); // Возвращаем длину строки
}
int main(){
int (*p_func)(const char *, int); // Указатель на функцию
p_func = my_print_1; // Присваиваем указателю адрес функции
int size = p_func("Hi Victor!", 123); // "str = Hi Victor!, num = 123" Вызываем my_print_1() через указатель
printf("my_print_return = %d \n", size); // "my_print_return = 10" Выводим длину строки
int (*func_ar[2])(const char *, int) = {my_print_1, my_print_2}; // Массив указателей на функции
int num_1 = func_ar[0]("my_print_1 text", 10); // "str = my_print_1 text, num = 10" Вызываем my_print_1() через указатель
int num_2 = func_ar[1]("my_print_2 strint", 20); // "text = my_print_2 strint, digit = 20" Вызываем my_print_2() через указатель
printf("func_ar[0] return = %d \n", num_1); // "func_ar[0] return = 15" Выводим длину строки
printf("func_ar[1] return = %d \n", num_2); // "func_ar[1] return = 17" Выводим длину строки
}
3) Можно создавать динамические массивы указателей на функции
int (**func_ar)(const char *, int) = malloc(2 * sizeof(&my_print_1)); // Динамический массив для 2х указателей на функцию
free(func_ar); // Освобождение памяти
При выходе из функции, выделенная в блоке память под локальные переменные освобождается.
Можно завести статическую переменную и вернуть ее адрес. Минус такого подхода что можно только один экземпляр указателя иметь, так как при повторном вызове функции новая статическая переменная не будет создаваться.
Если нужно иметь много экземпляров указателей, например, при инициализации указателя на структуру, которая потом будет передаваться в функцию. Нужно выделять память через malloc(), в этом случае память будет освобождаться не при выходе из блока а вручную (через free())
#include <stdio.h> // printf()
#include <stdlib.h> // malloc(), free()
struct st_array{ // Структура массива
int *ar; // Указатель на массив
int size; // Размер массива
};
struct st_array * ar_create(int size, int initial_value){ // Возвращает структура с массивом size элементов со значениями от initial_value
struct st_array *st = malloc(sizeof(struct st_array)); // Создаем указатель на структуру и выделяем под нее память
st->size = size; // Размер массива
st->ar = malloc(size * sizeof (initial_value)); // Выделяем память под массив
for(int i = 0; i < size; i++){ // Заполняем массив
st->ar[i] = initial_value + i;
}
return st; // Возвращаем структуру массива
}
void print_array(struct st_array *st){ // Выводим значения массива
for(int i = 0; i < st->size; i++){
printf("%d ", st->ar[i]);
}
printf("\n");
}
void delete_ar(struct st_array *st){ // Освобождение памяти структуры массива
free(st->ar); // Освобождаем память занятую массивом
free(st); // Освобождаем память занятую структурой
}
int main(){
struct st_array *st1 = ar_create(10, 5); // Создаем структуру массива
struct st_array *st2 = ar_create(5, 1); // Создаем структуру массива
struct st_array *st3 = ar_create(13, 7); // Создаем структуру массива
print_array(st1); // "5 6 7 8 9 10 11 12 13 14" Печатаем значения массива
print_array(st2); // "1 2 3 4 5"
print_array(st3); // "7 8 9 10 11 12 13 14 15 16 17 18 19"
delete_ar(st1); // Освобождаем память
delete_ar(st2); // Освобождаем память
delete_ar(st3); // Освобождаем память
}