- •Материалы для подоговки к экзамену по дисциплине «основы программировния»
- •Тема №1 Основные понятия
- •Способы записи алгоритма
- •Тема №2 Переменные
- •Типы переменных
- •Типы с плавающей точкой
- •Переполнение переменных
- •Постфиксное обозначение типа
- •Шестнадцатеричный и восьмеричный формат
- •Экспоненциальная форма представления чисел
- •Объявление переменных
- •Начальное значение переменной
- •Область видимости переменной
- •Тема №3 Оператор присваивания
- •Ввод-вывод Форматированный вывод
- •Форматированный ввод
- •Непечатные символы
- •Тема №4 Ветвления и логические операторы
- •Оператор Switch
- •Логические операторы
- •Логическое отрицание
- •Логическое и
- •Логическое или
- •Пример: закон де-Моргана.
- •Порядок выполнения логических операторов
- •Тема №5 Циклы
- •Цикл с предусловием
- •Циклы с постусловием
- •Цикл for
- •Вложенные циклы
- •Тема №6 Одномерные массивы
- •Начальная инициализация массива.
- •Размер массива
- •Переполнение массива
- •Пример с одномерным массивом
- •Многомерные статические массивы
- •Пример с многомерным массивом
- •Тема №7 Строки в си. Введение
- •Чтение строк
- •Указатели
- •Определение
- •Арифметика указателей
- •Указатель на указатель
- •Указатели и приведение типов
- •Null pointer - нулевой указатель
- •Пример работы с указателями
- •Тема №8 Указатели и массивы
- •Многомерные массивы и указатели на многомерные массивы
- •Определение макросов
- •Тема №9 Тернарный оператор и оператор запятая
- •Оператор запятая.
- •Сегментация приложения на си
- •Тема №10 Динамическое выделение памяти
- •Освобождение памяти с помощью free
- •Работа с двумерными и многомерными массивами
- •Тема №11 Параметры командной строки
- •Функции
- •Параметры и аргументы
- •Передача аргументов По значению
- •По указателю (ссылке)
- •Передача массива в качестве аргумента
- •Тема №12 Битовые операции
- •Операции побитового сдвига
- •Примеры
- •Вопросы к экзамену по дисциплине «Основы программирования»
Передача аргументов По значению
При передаче аргументов по значению происходит их копирование. Это значит, что любые изменения, которые функция производит над переменными, имеют место быть только внутри функции. Например
#include <conio.h>
#include <stdio.h>
void change(int a) {
a = 100;
printf("%d\n", a);
}
void main() {
int d = 200;
printf("%d\n", d);
change(d);
printf("%d", d);
getch();
}
Программы выведет
200
100
200
Понятно почему. Внутри функции мы работаем с переменной «а», которая является копией переменной «d». Мы изменяем локальную копию, но сама переменная d при этом не меняется. После выхода из функции локальная переменная будет уничтожена. Переменная d при этом никак не изменится.
По указателю (ссылке)
Каким образом тогда можно изменить переменную? Для этого нужно передать адрес этой переменной. Перепишем функцию, чтобы она принимала указатель типа int
#include <conio.h>
#include <stdio.h>
void change(int *a) {
*a = 100;
printf("%d\n", *a);
}
void main() {
int d = 200;
printf("%d\n", d);
change(&d);
printf("%d", d);
getch();
}
Вот теперь программа выводит
200
100
100
Здесь также была создана локальная переменная, но так как передан был адрес, то мы изменили значение переменной d, используя её адрес в оперативной памяти.
В программировании первый способ передачи параметров называют передачей по значению, второй – передачей по указателю (ссылке). Запомните простое правило: если вы хотите изменить переменную, необходимо передавать функции указатель на эту переменную. Следовательно, чтобы изменить указатель, необходимо передавать указатель на указатель и т.д. Например, напишем функцию, которая будет принимать размер массива типа int и создавать его. С первого взгляда, функция должна выглядеть как-то так:
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
void init(int *a, unsigned size) {
a = (int*) malloc(size * sizeof(int));
}
void main() {
int *a = NULL;
init(a, 100);
if (a == NULL) {
printf("ERROR");
} else {
printf("OKAY...");
free(a);
}
getch();
}
Но эта функция выведет ERROR. Мы передали адрес переменной. Внутри функции init была создана локальная переменная a, которая хранит адрес массива. После выхода из функции эта локальная переменная была уничтожена. Кроме того, что мы не смогли добиться нужного результата, у нас обнаружилась утечка памяти: была выделена память на куче, но уже не существует переменной, которая бы хранила адрес этого участка.
Для изменения объекта необходимо передавать указатель на него, в данном случае – указатель на указатель.
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
void init(int **a, unsigned size) {
*a = (int*) malloc(size * sizeof(int));
}
void main() {
int *a = NULL;
init(&a, 100);
if (a == NULL) {
printf("ERROR");
} else {
printf("OKAY...");
free(a);
}
getch();
}
Ещё подобный пример. Напишем функцию, которая принимает в качестве аргумента строку и возвращает указатель на область памяти, в которую скопирована эта строка.
#include <conio.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* initByString(const char *str) {
char *p = (char*) malloc(strlen(str) + 1);
strcpy(p, str);
return p;
}
void main() {
char *test = initByString("Hello World!");
printf("%s", test);
free(test);
getch();
}
В этом примере утечки памяти не происходит. Мы выделили память с помощью функции malloc, скопировали туда строку, а после этого вернули указатель. Локальные переменные были удалены, но переменная test хранит адрес участка памяти на куче, поэтому можно его удалить с помощью функции free.
