Jump to content

Преговор - УПП, Седмица 2, 13.10.2023



Конвертиране на данни, побитови оператори, условни оператори

Конвертиране на данни

  • Неявно (скрито, експлицитно): компилаторът сам се досеща да промени типа
  •   int a = 5;
      double b = a;
    
  • Явно (изрично, експлицитно): казваме как типът да се конвертира
  •   double a = 5.7, b = 3.42;
      int m = (int)a + (int)b;
    
  • Със загуба на информация (от по-голям тип в по-малък)
  •   double a = 5.8371;
      int b = a;
    
  • Без загуба на информация (от по-малък тип в по-голям)
  •   int a = 1000;
      long b = a;
    

Побитови оператори

Първо ще ги погледнем чисто теоретично.

Внимавайте, те са различни от логическите оператори!

Означавам побитовите числа с водеща буква "b"! Тоест, числото b1001 е двоичното число 1001, което е десетичното число 9.

  • Отрицание/негация, ~: обръща 0 в 1 и 1 в 0
  •   ~ b0010 = b1101
    
  • Побитово "и", &: за два бита, ако и двете са 1, връща 1, иначе връща 0
  •   3 & 5 = 1
      b0011 & b0101 = b0001
    
  • Побитово "или", |: за два бита, ако и двете са 0, връща 0, иначе връща 1
  •   3 | 5 = 7
      b0011 | b0101 = b0111
    
  • Изключващо "или", ^: ако само един от двата бита е 1, връща 1, иначе връща 0
  •   3 ^ 5 = 6
      b0011 ^ b0101 = b0110
    
  • Побитово отместване на ляво, <<: отмества всеки бит с n позиции на ляво Винаги "запълва" с нули (няма разлика между логическо и аритметично ляво отместване)
  •   b1010 << 1 = b0100
      b0001 << 3 = b1000
    

    (приемаме, че типа данни съхранява само 4 бита данни)

  • Побитово отместване на дясно, >>: отмества всеки бит с n позиции на дясно При аритметичното отместване, дупките се запълват с предходната стойност на старшия бит При логическото отместване, винаги запълваме с 0 В C++ има един оператор, дали е аритметическо или логическо се определя от типа
  •   b1000 >> 1 = b0100 или пък b1100
      b1010 >> 3 = b0001 или пък b1111
    
Примери с код:
int a = 3, b = 5;

int n1 = ~ a; // -4
int n2 = ~ (unsigned int)a; // 4294967292

int i1 = 3 & 5; // 1
int i2 = 3 | 5; // 7
int i3 = 3 ^ 5; // 6

int l1 = b << 1; // 10
int l2 = b << 3; // 40

int r1 =  b >> 1; // 2
int r2 =  b >> 2; // 1
int r3 = -b >> 1; // -3
int r4 = (unsigned int)b  >> 1; // 2
int r5 = (unsigned int)-b >> 1; // 2147483645

Логически оператори

Много приличат на побитовите, обаче побитовите работят върху битове, докато логическите работят върху bool.

  • !: обръща true във false и обратно
  •    ! true  // false
       ! false // true
    
  • &&: "логическо и", ако и двете стойности са true, тогава и резултатът е true
  •   true  && true  // true
      false && true  // false
      false && false // false
    
  • ||: "логическо или", само ако и двете са false, тогава е false
  •   true  || true  // true
      false || true  // true
      false || false // false
    

Условни конструкции

  • if: приема булева стойност, ако е true, изпълнява кода в къдравите скоби, ако е false ги пропуска
  •   if (expression) {
          dosomething;
      }
      dootherthing;
    

    expression се конвертира към типа bool

    Ако в къдравите скоби имаме един израз (тоест е само един ред код, завършващ на ) можем да ги пропуснем.

      if (expression) dosomething;
      dootherthing;
    
  • if-else: работи като if, обаче след къдравите скоби добавяме {` else { ... }}.
  •   if (expression) {
          dothing1;
      }
      else {
          dothing2;
      }
      dootherthing;
    

    Ако expression е истина, dothing1 се изпълнява и dothing2 се пропуска изцяло. Аналогично, ако expression е лъжа, dothing1 се изпуска и dothing2 се изпълнява.

    Както с if, ако dothing2 е само един израз, можем да пропуснем къдравите скоби

      if (expression) {
          dothing1;
      }
      else dothing2;
      dootherthing;
    
  • благодарение на изпускането на къдрави скоби, можем да пишем
  •   if (expression1) {
          doSomething1;
      }
      else if (expression2) {
          doSometing2;
      }
      ...
      else {
          doSomethingN;
      }
    

    Това е еквивалентно на:

      if (expression1) doSomething1;
      else {
          if (expression2) doSomething2;
          else {
              ...
                  else doSomethingN;
          }
      }
    
  • expression винаги се конвертира до тип bool (ако е нужно) Никога не правете такива неща:
  •   bool a;
      if (expression) a = true;
      else a = false;
    
      bool a = false;
      if (expression) a = true;
    

    Просто правите bool a = expression

      bool a;
      if (expression) a = false;
      else a = true;
    

    Просто правите bool a = ! expression

  • Тернарен оператор: ако искате да имате такава условна конструкция като част от израз (който взема стойност) използвате
  •   (expression) ? valueA : valueB
    

    Ако expression е истина, връща се valueA, иначе се връща valueB

      int grade;
      std::cin >> grade;
      double a = (grade < 300) ? 2.0 : grade / 100.0;
    

    Има много нисък приоритет, в повечето случаи ще ви се наложи да го оградите в скоби.

      int grade;
      std::cin >> grade;
      std::cout << ((grade < 300) ? 2.0 : grade / 100.0);
    
  • switch: приема стойност и изпълнява случая, при който стойността съвпада с определената
  •   switch (value) {
          case valueA: doSomething1; break;
          case valueB: doSomething2; break;
          case valueC: doSomething3; break;
      }
    

    Служи като улеснение на синтаксиса:

      if (value == valueA) doSomething1;
      else if (value == valueB) doSomething2;
      else if (value == valueC) doSomething3;
    

    otherwise е като else, това е случая когато не съвпада с никоя стойност. Може да бъде изпуснат.

    Без break изпълнението продължава към следващия case

char characterInput;
std::cin >> characterInput;
switch(characterInput) {
    case 'h':
    case '?': std::cout << "This is a helpful message."; break;
    case 'w': std::cout << "Hello World"; break;
    default: std::cout << "Error"; break;
}