- •Ответы по дисциплине «Основы алгоритмизации и программирования»
- •Запись алгоритма Евклида на языке с
- •Int main() {
- •Эвристический алгоритм «ближайшего соседа»
- •Эвристический алгоритм «ближайших пар»
- •«Правильный» алгоритм поиска маршрута
- •Эволюция языка с bcpl → b → c → k&r c → ansi c → c99 → c1x
- •#Define имя текст_для_подстановки
- •123, 67543, 037, 07777, 0Xabf7, 0xffff, …
- •123456789L, 0xful (это просто число 15).
- •Определение символических констант в limits.H
- •Int lower, upper, step;
- •Int main() {
- •Int main() {
- •Int main() {
- •Всего операций: 47
- •If (условие) оператор
- •If (условие) оператор1 else оператор2
- •Int main() {
- •Int main() {
- •Int main() {
- •Int main() {
- •If (found)
- •Адресация памяти
- •Адреса объектов программы
- •Int fact(int n) {
- •О размерах участков памяти, выделяемых объектам
- •Правила адресной арифметики
- •Никакие другие операции к адресам неприменимы, т.Е. Адреса нельзя умножать, делить, складывать между собой и пр.
- •Имя массива – это константный указатель на его начало.
- •T X[] эквивалентно t *X
- •Int main() {
- •Void *calloc(size_t n, size_t r)
- •Void free(void *p)
- •Int main() {
- •Void *p;
- •Void swaps(char** a, char** b) {
- •Int main(void) {
- •Int main() {
- •Правило «право-лево»
- •Int pt_in_rect(struct point p, struct rect r) {
- •Int main() {
- •Int main() {
- •Int ival;
- •Void init(Vector*);
- •Void resize(Vector*, int);
- •Void push_back(Vector*, double);
- •Void push_s(Stack *st, double d) {
- •Void init_q(Queue *q) {
- •Void enqueue(Queue *q, double d) {
- •Int dequeue(Queue *q, double *d) {
- •Typedef struct Heap {Vector V;} Heap;
- •Void init_h(Heap *hp) {
- •Int Heap_Maximum(Heap *hp, double *z) {
- •Void Max_Heap_Insert(Heap *hp, double X){
- •Void Max_Heapify(Heap *hp, int I) {
- •Int l, r, largest;
- •Int Heap_Extract_Max(Heap *hp, double *z) {
- •Void Build_Max_Heap(Heap *hp) {
- •Void Insert_head_l1(List1 *l, double z) {
- •Void Insert_back_l1(List1 *l, double z) {
- •Int Extract_head_l1(List1 *l, double *z) {
- •Int Extract_back_l1(List1 *l, double *z) {
- •Void reverse_l1(List1 *l) {
- •Исходный код функции sort_l1
- •Void sort_l1(List1 *l) {
- •Void visit(List1* l) {
- •Void traverse(List1* l) {
- •Void Print_l1(List1 *l) {
- •Void Insert_l2(List2 *l, double z, int direction) {
- •Прямой обход (сверху вниз), при котором мы посещаем узел, а затем левое и правое поддеревья
- •Поперечный обход (слева направо), при котором мы посещаем левое поддерево, затем узел, а затем правое поддерево
- •Обратный обход (снизу вверх), при котором мы посещаем левое и правое поддеревья, а затем узел.
- •Простой метод сортировки массива
- •Задача о взвешивании монет
- •1) Очевидно, что на последнем шаге процедуры взвешивания мы должны иметь дело максимум с 3 монетами, чтобы в при любом исходе взвешивания получить результат.
- •2) Задача предпоследнего шага – отобрать группу из 3-х монет. Это можно сделать, если в нашем распоряжении будет не более 9 монет (3 группы по 3 монеты).
- •3) Наконец, если у нас будет от 10 до 27 монет, мы сможем отобрать из них не более 9
- •Void mov(int n, char a, char c, char b) {
- •Int main() {
Адресация памяти
Будем считать, что оперативная память компьютера представляет собой линейную последовательность пронумерованных ячеек памяти – байтов и каждая ячейка памяти имеет адрес.
Б удем также считать, что компьютер имеет 32-разрядную архитектуру и адреса записываются как 32-битные коды.
Наименьший адрес: 0x00000000, наибольший – 0xFFFFFFFF.
Адресуемый объем памяти – 232 байт = 4 Гбайт
Адреса объектов программы
Каждый объект программы занимает определенный участок оперативной памяти компьютера и, следовательно, может быть охарактеризован адресом начала этого участка, который и считается адресом данного объекта.
В языке С имеется специальная унарная операция & для получения адреса объекта d в оперативной памяти.
Функция printf имеет специальный формат %p для вывода адреса.
Вопрос №46. Размещение в памяти объектов программы. Размеры участков памяти, выделяемые объектам программы.
# include <stdio.h>
#include <stdlib.h>
int x;
short y;
char a[256], b[512];
Int fact(int n) {
return n ? n * fact(n-1) : 1;
}
main() {
printf("addr(x) = %p\n", &x);
printf("addr(y) = %p\n", &y);
printf("addr(a) = %p\n", &a);
printf("addr(b) = %p\n", &b);
printf("addr(fact) = %p\n", &fact);
system("PAUSE");
return 0;
}
О размерах участков памяти, выделяемых объектам
Результаты работы программы позволяют предположить, что память объектам программы выделяется блоками, кратными 16 байт.
Вопрос №47. Адреса массивов и функций.
Некоторые объекты программы не требуют применения к ним операции взятия адреса &, т.к. они сами и являются адресами.
К их числу относятся функции и массивы, т.е. значением имени функции и значением имени массива является адрес.
Доказательство этого:
Вопрос №48. Адресная арифметика. Правила адресной арифметики.
Можно предположить, что адреса являются обычными числами (?) и могут участвовать в арифметических операциях.
Правила адресной арифметики
К адресу можно прибавлять или из адреса можно вычитать целые значения. При этом результат операции зависит от типа переменной, адрес которой участвует в операции. Если для данного типа размер переменной равен n байт и к адресу прибавляется i, то величина адреса изменится на i*n.
Никакие другие операции к адресам неприменимы, т.Е. Адреса нельзя умножать, делить, складывать между собой и пр.
Исключением является операция вычитания адресов однотипных объектов, результатом которой является целое число, равное количеству объектов данного типа, которые могут разместиться на участке памяти между адресами-операндами.
Вопрос №49. Указатели. Нетипизированные указатели.
Адреса объектов программы можно сохранять в переменных специального вида – указателях.
Указатели образуют не один конкретный тип данных (хотя все указатели имеют один размер – 4 байта); типов указателей существует столько, сколько существует типов данных, т.е. для любого типа данных Т существует тип указателя Т*.
Примеры типов указателей: char*, int*, short*, long*, float*, double*, void*, …
Указатели типа void* называются нетипизированными указателями. При выполнении арифметических операций над такими указателями считается, что объекты, на которые они указывают, имеют размер 1 байт.
Вопрос №50. Операция доступа (ссылки) по указателю.
В языке С существует одноместная операция * , применяемая к значениям, являющимся адресами (указателями). Результатом этой операции является объект программы, расположенный по данному адресу. Эта операция называется операцией ссылки по указателю, или операцией разыменования.
Операции & и * являются взаимно-дополняющими и, если они расположены подряд, взаимно компенсируют друг друга:
Пусть дано определение: int x; Тогда &x – адрес x; *&x – сама переменная x; &*&x – адрес x и т.д.
Переменная типа указатель может в разное время принимать различные значения. Следовательно, через одну и туже переменную-указатель можно обращаться к разным переменным (в разное время):
int a=2, b=3, c=4;
int* p=&a;
*p=7;
p=&b; *p=8;
p=&c; *p=9;
printf(“a=%d, b=%d, c=%d\n”, a, b, c); //a=7, b=8, c=9
Вопрос №51. Указатели и массивы.
Массив - это одна из наиболее простых структур данных, представляющих собой линейную последовательность фиксированного количества однотипных элементов, расположенных в памяти друг за другом непрерывно и допускающих прямой доступ к любому элементу по его номеру (индексу).
Массив характеризуется именем, типом элементов и их количеством.
Пример определения массива: int x[50]
В языке С существует очень тесная связь между массивами и указателями, которая характеризуется простым утверждением: