- •Материалы для подоговки к экзамену по дисциплине «основы программировния»
- •Тема №1 Основные понятия
- •Способы записи алгоритма
- •Тема №2 Переменные
- •Типы переменных
- •Типы с плавающей точкой
- •Переполнение переменных
- •Постфиксное обозначение типа
- •Шестнадцатеричный и восьмеричный формат
- •Экспоненциальная форма представления чисел
- •Объявление переменных
- •Начальное значение переменной
- •Область видимости переменной
- •Тема №3 Оператор присваивания
- •Ввод-вывод Форматированный вывод
- •Форматированный ввод
- •Непечатные символы
- •Тема №4 Ветвления и логические операторы
- •Оператор Switch
- •Логические операторы
- •Логическое отрицание
- •Логическое и
- •Логическое или
- •Пример: закон де-Моргана.
- •Порядок выполнения логических операторов
- •Тема №5 Циклы
- •Цикл с предусловием
- •Циклы с постусловием
- •Цикл for
- •Вложенные циклы
- •Тема №6 Одномерные массивы
- •Начальная инициализация массива.
- •Размер массива
- •Переполнение массива
- •Пример с одномерным массивом
- •Многомерные статические массивы
- •Пример с многомерным массивом
- •Тема №7 Строки в си. Введение
- •Чтение строк
- •Указатели
- •Определение
- •Арифметика указателей
- •Указатель на указатель
- •Указатели и приведение типов
- •Null pointer - нулевой указатель
- •Пример работы с указателями
- •Тема №8 Указатели и массивы
- •Многомерные массивы и указатели на многомерные массивы
- •Определение макросов
- •Тема №9 Тернарный оператор и оператор запятая
- •Оператор запятая.
- •Сегментация приложения на си
- •Тема №10 Динамическое выделение памяти
- •Освобождение памяти с помощью free
- •Работа с двумерными и многомерными массивами
- •Тема №11 Параметры командной строки
- •Функции
- •Параметры и аргументы
- •Передача аргументов По значению
- •По указателю (ссылке)
- •Передача массива в качестве аргумента
- •Тема №12 Битовые операции
- •Операции побитового сдвига
- •Примеры
- •Вопросы к экзамену по дисциплине «Основы программирования»
Указатели и приведение типов
Так как указатель хранит адрес, можно кастовать его до другого типа. Это может понадобиться, например, если мы хотим взять часть переменной, или если мы знаем, что переменная хранит нужный нам тип.
#include <conio.h>
#include <stdio.h>
#define SIZE 10
void main() {
int A = 10;
int *intPtr;
char *charPtr;
intPtr = &A;
printf("%d\n", *intPtr);
printf("--------------------\n");
charPtr = (char*)intPtr;
printf("%d ", *charPtr);
charPtr++;
printf("%d ", *charPtr);
charPtr++;
printf("%d ", *charPtr);
charPtr++;
printf("%d ", *charPtr);
getch();
}
В этом примере мы пользуемся тем, что размер типа int равен 4 байта, а char 1 байт. За счёт этого, получив адрес первого байта, можно пройти по остальным байтам числа и вывести их содержимое.
Null pointer - нулевой указатель
Указатель до инициализации хранит мусор, как и любая другая переменная. Но в то же время, этот "мусор" вполне может оказаться валидным адресом. Пусть, к примеру, у нас есть указатель. Каким образом узнать, инициализирован он или нет? В общем случае никак. Для решения этой проблемы был введён макрос NULL библиотеки stdlib.
Принято при определении указателя, если он не инициализируется конкретным значением, делать его равным NULL.
int *ptr = NULL;
По стандарту гарантировано, что в этом случае указатель равен NULL, и равен нулю, и может быть использован как булево значение false. Хотя в зависимости от реализации NULL может и не быть равным 0 (в смысле, не равен нулю в побитовом представлении, как например, int или float).
Это значит, что в данном случае
int *ptr = NULL;
if (ptr == 0) {
...
}
вполне корректная операция, а в случае
int a = 0;
if (a == NULL) {
...
}
поведение не определено. То есть указатель можно сравнивать с нулём, или с NULL, но нельзя NULL сравнивать с переменной целого типа или типа с плавающей точкой.
#include <stdlib.h>
#include <stdio.h>
#include <conio.h>
void main() {
int *a = NULL;
unsigned length, i;
printf("Enter length of array: ");
scanf("%d", &length);
if (length > 0) {
//При выделении памяти возвращается указатель.
//Если память не была выделена, то возвращается NULL
if ((a = (int*) malloc(length * sizeof(int))) != NULL) {
for (i = 0; i < length; i++) {
a[i] = i * i;
}
} else {
printf("Error: can't allocate memory");
}
}
//Если переменая была инициализирована, то очищаем её
if (a != NULL) {
free(a);
}
getch();
}
Пример работы с указателями
Так как размер типа char всегда равен 1 байт, то с его помощью можно реализовать операцию swap – обмена местами содержимого двух переменных.
#include <conio.h>
#include <conio.h>
#include <stdio.h>
void main() {
int length;
char *p1, *p2;
char tmp;
float a = 5.0f;
float b = 3.0f;
printf("a = %.3f\n", a);
printf("b = %.3f\n", b);
p1 = (char*) &a;
p2 = (char*) &b;
//Узнаём сколько байт перемещать
length = sizeof(float);
while (length--) {
//Обмениваем местами содержимое переменных побайтно
tmp = *p1;
*p1 = *p2;
*p2 = tmp;
//не забываем перемещаться вперёд
p1++;
p2++;
}
printf("a = %.3f\n", a);
printf("b = %.3f\n", b);
getch();
}
В этом примере можно поменять тип переменных a и b на double или любой другой (с соответствующим изменением вывода и вызова sizeof), всё равно мы будет обменивать местами байты двух переменных.
