Преговор - УПП, Седмица 3, 20.10.2023
Цикли, assert
Бележки от миналия път
#- Видях две груби грешки в предадените решения, първо:
Грешно е, понежеbool b; if (condition) b = true;
condition
сам по себе си е от тип bool, с тозиif
буквално не правите нищо. Правилния вариант е:
Аналогично за:bool b = condition;
правилното е:bool b; if (!condition) b = true;
bool b = !condition;
- второ:
a >= 48 && a <= 57
Всяка буква действително съответства на определено число, дефинирано от ASCII стандарта. Наистина '0' е 48 и '9' е 57, обаче от вас не се очаква да помните ASCII таблицата! Също, ако не знаеш наизуст кои буква са тези числа, този израз е много неясен.
Правилния вариант е просто да направите сравнения с буквите, все пак C++ ги "конвертира" (много грубо казано) към съответните ASCII стойности:
a >= '0' && a <= '9'
С/без загуба на данни
#Получи се малко разногласие между това, което Ивана ви е казала и това което аз ви казах. В края на деня, конвертиране от int към unsigned int не губи информация!
Цитирайки 4то издание на "The C++ Programming Language" глава 10.5:
A conversion is value-preserving if you can convert a value and then convert the result back to it's original type and get the original valueТоест, конвертирания при които не губиш битове, нямаш загуба на данни.
2s compliment
#Интуитивно обяснение на 2s compliment. Да приемем, че работим с тип данни, при който имаме само 3 бита.
Как бихме ги използвали, за да представим отрицателните числа?
Най-лесния начин е да погледнем най-левия (старшия) бит, ако е 0 значи е положително, ако е 1 значи е отрицателно, тоест 010
е 2, докато 110
е -2.
Ако разпишем всичките ни числа:
111 -3
110 -2
101 -1
100 -0
011 3
010 2
001 1
000 0
Това работи, обаче имаме един гигантски проблем: събирането не работи! От математика знаем, че "2 - 1 = 2 + (-1)", тоест би трябвало да можем да съберем побитово 2 и -1 и да получим 1. Ама,
2 + -1 = 010 + 101 = 111 = -3
Също, знаем, че "(-1) + (-1) = -2", обаче
-1 + -1 = 101 + 101 = 1010 имаме overflow, това се съкращава на 010 = 2
Излиза, че с тази схема не можем да събираме отрицателни числа както събираме положителни (или както събираме побитово).
Да не говорим, че ние имаме -0, което не ни трябва много.
011 3
010 2
001 1
Сега, искаме -1 да бъде такова число, различно от досега дефинираните (тоест което има единица в старшия си бит) така че "1 + -1 = 0".
Лесно се досещаме, че ако добавим към "001" числото "111", тогава тяхната сума ще предизвика overflow и резултата ще бъде 0:
001 + 111 = 1000 overflow, става 000
Нека тогава "-1 = 111".
Правейки същото за "2 + -2", намираме че
010 + 110 = 1000 overflow, става 000
Нека тогава "-2 = 110".
За "3 + -3" се досещаме, че "-3 = 101".
За сега имаме:
111 -1
110 -2
101 -3
100 x
011 3
010 2
001 1
000 0
100 не сме го използвали, обаче намерихме отрицателното число за всяко положително.
Да видим как работим с него, побитово "100 + 001 = 101", тоест "x + 1 = -3", следователно x ще бъде -4.
Това е идеята на "complements", дефинираме отрицателни числа, така че да използваме едно и също събиране между положителни и отрицателни.
Още от много старите "калкулатори" (сумиращи машини), изваждането се е свеждало до събиране на положително и отрицателно число.
И това е причината защо ~ 2
връща -3
:
~ 00000000000000000000000000000010
──────────────────────────────────
11111111111111111111111111111101
И затова при >>
със знаков тип имаме аритметическо отместване:
11111111111111111111111111111101 >> 1
─────────────────────────────────────
11111111111111111111111111111110
Цикли
#while
цикъл
"условие" е булева стойност, като ако еwhile (условие) { тяло }
false
цикъла спира, докато ако еtrue
"тяло" се изпълнява и после пак се проверява условие
if (условие) {
тяло
}
else {
спри
}
if (условие) {
тяло
}
else {
спри
}
...
do-while
цикъл, катоwhile
, но "тяло" се изпълнява поне един път
Нашият аналог е:do { тяло } while (условие);
тяло if (условие) { тяло } else { спри } if (условие) { ...
for
цикъл
"инициализация" се изпълнява само веднъж, преди всичко останало в цикълаfor (инициализация; условие; инкрементация) { тяло }
"условие" се изпълнява преди всяка итерация, като ако еfalse
цикъла спира, докато ако еtrue
продължава
"инкрементация" се изпълнява след края на всяка итерация
- Може да си представяте, че това на практика извършва:
инициализация if (условие) { тяло } else { спри } инкрементация if (условие) { тяло } else { спри } ...
- или използвайки
while
цикъл:инициализация while (условие) { тяло инкрементация }
Ключови думи
#break
: моментално прекратява изпълнението на цикълаПри
for
цикли инкрементацията не се изпълнява следbreak
continue
: продължи след края на сегашната итерация; тоест изпълнението на тяло се прекратява и започваме нова итерацияПри
for
цикли директно отива на инкрементация
Assert
#Понякога е полезно да тестваме нашия код вътре в самия код. Тоест, искаме на конкретно място в кода да проверим нещо.
assert(condition);
Ако condition
е true
, нищо не се случва, обаче ако е false
програмата моментално прекратява изпълнение.
Използва се повече като програмистки уред, тоест ако програмата спре заради assert
, това е защото има някаква програмистка грешка.