- •Материалы для подоговки к экзамену по дисциплине «основы программировния»
- •Тема №1 Основные понятия
- •Способы записи алгоритма
- •Тема №2 Переменные
- •Типы переменных
- •Типы с плавающей точкой
- •Переполнение переменных
- •Постфиксное обозначение типа
- •Шестнадцатеричный и восьмеричный формат
- •Экспоненциальная форма представления чисел
- •Объявление переменных
- •Начальное значение переменной
- •Область видимости переменной
- •Тема №3 Оператор присваивания
- •Ввод-вывод Форматированный вывод
- •Форматированный ввод
- •Непечатные символы
- •Тема №4 Ветвления и логические операторы
- •Оператор Switch
- •Логические операторы
- •Логическое отрицание
- •Логическое и
- •Логическое или
- •Пример: закон де-Моргана.
- •Порядок выполнения логических операторов
- •Тема №5 Циклы
- •Цикл с предусловием
- •Циклы с постусловием
- •Цикл for
- •Вложенные циклы
- •Тема №6 Одномерные массивы
- •Начальная инициализация массива.
- •Размер массива
- •Переполнение массива
- •Пример с одномерным массивом
- •Многомерные статические массивы
- •Пример с многомерным массивом
- •Тема №7 Строки в си. Введение
- •Чтение строк
- •Указатели
- •Определение
- •Арифметика указателей
- •Указатель на указатель
- •Указатели и приведение типов
- •Null pointer - нулевой указатель
- •Пример работы с указателями
- •Тема №8 Указатели и массивы
- •Многомерные массивы и указатели на многомерные массивы
- •Определение макросов
- •Тема №9 Тернарный оператор и оператор запятая
- •Оператор запятая.
- •Сегментация приложения на си
- •Тема №10 Динамическое выделение памяти
- •Освобождение памяти с помощью free
- •Работа с двумерными и многомерными массивами
- •Тема №11 Параметры командной строки
- •Функции
- •Параметры и аргументы
- •Передача аргументов По значению
- •По указателю (ссылке)
- •Передача массива в качестве аргумента
- •Тема №12 Битовые операции
- •Операции побитового сдвига
- •Примеры
- •Вопросы к экзамену по дисциплине «Основы программирования»
Чтение строк
Для того, чтобы запросить у пользователя строку, необходимо создать буфер. Размер буфера должен быть выбран заранее, так, чтобы введённое слово в нём поместилось. При считывании строк есть опасность того, что пользователь введёт данных больше, чем позволяет буфер. Эти данные будут считаны и помещены в память, и затрут собой чужие значения. Таким образом можно провести атаку, записав нужные байты, в которых, к примеру, стоит переход на участок кода с вредоносной программой, или логгирование данных.
#include <conio.h>
#include <stdio.h>
void main() {
char buffer[20];
scanf("%19s", buffer);
printf("%s", buffer);
getch();
}
В данном случае количество введённых символов ограничено 19, а размер буфера на 1 больше, так как необходимо хранить терминальный символ. Напишем простую программу, которая запрашивает у пользователя строку и возвращает её длину.
#include <conio.h>
#include <stdio.h>
void main() {
char buffer[128];
unsigned len = 0;
scanf("%127s", buffer);
while (buffer[len] != '\0') {
len++;
}
printf("length(%s) == %d", buffer, len);
getch();
}
Так как числовое значение символа '\0' равно нулю, то можно записать
while (buffer[len] != 0) {
len++;
}
Или, ещё короче
while (buffer[len]) {
len++;
}
Теперь напишем программу, которая запрашивает у пользователя два слова и сравнивает их.
#include <conio.h>
#include <stdio.h>
/*
Результатом сравнения будет число
0 если слова равны
1 если первое слово больше второго в лексикографическом порядке
2 если второе слово больше
*/
void main() {
char firstWord[128]; //Первое слово
char secondWord[128]; //Второе слово
unsigned i; //Счётчик
int cmpResult = 0; //Результат сравнения
scanf("%127s", firstWord);
scanf("%127s", secondWord);
for (i = 0; i < 128; i++) {
if (firstWord[i] > secondWord[i]) {
//Больше даже если второе слово уже закончилось, потому что
//тогда оно заканчивается нулём
cmpResult = 1;
break;
} else if (firstWord[i] < secondWord[i]) {
cmpResult = -1;
break;
}
}
printf("%d", cmpResult);
getch();
}
Так как каждая буква имеет числовое значение, то их можно сравнивать между собой как числа. Кроме того, обычно (но не всегда!) буквы в таблицах кодировок расположены по алфавиту. Поэтому сортировка по числовому значению также будет и сортировкой по алфавиту.
Указатели
Это, пожалуй, самая сложная и самая важная тема во всём курсе. Без понимания указателей дальнейшее изучении си будет бессмысленным. Указатели – очень простая концепция, очень логичная, но требующая внимания к деталям.
Определение
Указатель – это переменная, которая хранит адрес области памяти. Указатель, как и переменная, имеет тип. Синтаксис определения указателей
<тип> *<имя>;
Например
float *a;
long long *b;
Два основных оператора для работы с указателями – это оператор & взятия адреса, и оператор * разыменования. Рассмотрим простой пример.
#include <conio.h>
#include <stdio.h>
void main() {
int A = 100;
int *p;
//Получаем адрес переменной A
p = &A;
//Выводим адрес переменной A
printf("%p\n", p);
//Выводим содержимое переменной A
printf("%d\n", *p);
//Меняем содержимое переменной A
*p = 200;
printf("%d\n", A);
printf("%d", *p);
getch();
}
Рассмотрим код внимательно, ещё раз
int A = 100;
Была объявлена переменная с именем A. Она располагается по какому-то адресу в памяти. По этому адресу хранится значение 100.
int *p;
Создали указатель типа int.
p = &A;
Теперь переменная p хранит адрес переменной A. Используя оператор * мы получаем доступ до содержимого переменной A.
Чтобы изменить содержимое, пишем
*p = 200;
После этого значение A также изменено, так как она указывает на ту же область памяти. Ничего сложного.
Теперь другой важный пример.
#include <conio.h>
#include <stdio.h>
void main() {
int A = 100;
int *a = &A;
double B = 2.3;
double *b = &B;
printf("%d\n", sizeof(A));
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(B));
printf("%d\n", sizeof(b));
getch();
}
Будет выведено
4
4
8
4
Несмотря на то, что переменные имеют разный тип и размер, указатели на них имеют один размер. Действительно, если указатели хранят адреса, то они должны быть целочисленного типа. Так и есть, указатель сам по себе хранится в переменной типа size_t (а также ptrdiff_t), это тип, который ведёт себя как целочисленный, однако его размер зависит от разрядности системы. В большинстве случаев разницы между ними нет. Зачем тогда указателю нужен тип?
