Преговор - УПП, Седмица 3, 20.10.2023



Цикли, assert

Бележки от миналия път

  • второ:

    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

Цикли

  • Може да си го представите като

    if (условие) {
      тяло
    }
    else {
      спри
    }
    
    if (условие) {
      тяло
    }
    else {
      спри
    }
    ...
    
  • for цикъл

    for (инициализация; условие; инкрементация) {
      тяло
    }
    

    “инициализация” се изпълнява само веднъж, преди всичко останало в цикъла
    "условие” се изпълнява преди всяка итерация, като ако е false цикъла спира, докато ако е true продължава
    "инкрементация” се изпълнява след края на всяка итерация

  • или използвайки while цикъл:

    инициализация
    while (условие) {
      тяло
      инкрементация
    }
    

Ключови думи

Assert

Понякога е полезно да тестваме нашия код вътре в самия код. Тоест, искаме на конкретно място в кода да проверим нещо.

assert(condition);

Ако condition е true, нищо не се случва, обаче ако е false програмата моментално прекратява изпълнение.

Използва се повече като програмистки уред, тоест ако програмата спре заради assert, това е защото има някаква програмистка грешка.