CODE 
  | 
        
DATA 
  | 
        
| 
             STACK (системен
                стек, run-time stack) 
            
  | 
        
HEAP (динамична памет)
  | 
        
** Тип указател (pointer)
        
       * Константа или променлива от тип указател съхранява адрес от
      оперативната памет на компютъра. 
      
      * Дефиниране на променлива тип указател (език C)
      
      Синтаксис: <име
на
          тип> * <име
          на променлива>
        
Примери:  Дефиниции на указателите pk,
      boss и pn.
            int* pk;          //
          без инициализация 
            Employee * boss;   // без инициализация
          
           int *pn =
            NULL;  // с инициализация 
Пример 1: 
      - дефиниране на променлива тип указател (pn)
      и инициализация на указателя: 
      int *pn = new int; 
Пример 2:
      - дефиниране на масив (с име указател arr)
      в динамичната памет с помощта на операция new:
            int *arr = new int[100];
Пример 3:
      -дефиниране на променлива тип указател (boss), инициализация на
      указателя (операция new) и задаване на начална
      стойност (конструктор) на обекта на този адрес: 
            Employee *boss = new Employ("John", 3200);
 -конструиране на обект от
      тип int,
      инициализация на указателя (операция new) и задаване на
      начална стойност  на променливата на този адрес:
      int *pn = new int(10);
          
STACK 
  | 
          Динамична памет   адрес XXX 
  | 
        ||||||||||||||
            
  | 
          
            
  | 
        
Примери: 
      Employee *boss = new Employee("John",
            3200); // началник е John, * означава
        дефиниране на указател 
          Employee
            harry("Harry", 1500); 
      *boss =
harry;                           
        // началник вече е Harry, * означава
        операция *, lvalue;
        // операция присвояване (Employee) 
      
      int *pn
                  = new int(10);
                  cout << *pn;     //
          10; операция *, стойността на оперцията е използвана като
          rvalue
            *pn = 12;             
      // операция *, стойността
              на оперцията е lvalue; операция присвояване
          (int)
      cout << *pn;   
          // 12; операция *, стойността
              на оперцията е използвана като rvalue
    
* Обекти и променливи без имена
      Операция new създава обект без име, като достъпът до
      този обект става с указатели.
    
Примери: Указатели boss
        и h осигуряват дастъп до обекта Employee("John",
          3200). 
      Employee
                  *boss = new Employee("John", 3200); //
          *boss е John
            Еmployee *h; 
      h = boss;     //
        операция присвояване (Employee*); *boss и *h е все John
            boss = NULL;  //
      операция присвояване (Employee*); само *h е
        John, *boss е грешка!
            
          
Примери:  
      delete boss; //
        boss е указател със стойност адрес в динамичната памет
      delete pn;   //
        pn е указател със стойност адрес в динамичната
          памет
      delete [] arr; // освобождава паметта, запазена с new (целия
          масив)
        
-- Опит за
        работа със стойност на указател след освобождаване на заетата
        памет
      
Пример: След операция delete, pk става
          невалиден указател. 
      int *pk = new int(10); 
      cout << *pk; // 10; операция *  
      delete pk;   // операция delete    
      *pk = 100;   // операция *, операция присвояване
        (int); невалиден
              указател
            // на адрес pk няма променлива
        тип int         
      Опасна грешка! 
      
delete
            pk;   //
          операция delete 
          pk = NULL; 
        
      *pk = 100;   // ОС идентифицира грешката
    
-- Неопределена
      (незаредена) променлива
        
Пример: Операция
      * връща невалидна променлива.
            int *pk;
      
      *pk = 100; // невалиден
              указател
            // указателят
        pk съдържа неопределена стойност (незаредена променлива)
      Опасна грешка
      
int*
            pk = NULL; 
        
      *pk = 100;   // ОС идентифицира грешката
    
Пример: Адрес на променлива в динамичната
        памет.
          int *p = new int(20); 
            int *p1 = &(*p); // инициализира p1
        с адреса на променливата *p
            int *p2 = p;     //
          инициализира p2 с адреса, записан в p
                 
 Пример: Локални обект harry
      и указател h, инициализиран с адреса на harry. 
            Employee harry("Harry", 4300); // обект в системния стек  
      Employee *h =
            &harry;         
          // h съдържа адрес
          на обект в системниея стек
    
| int k = 10;  int *pk = &k; cout << k; cout << (*pk);  | 
          STACK  Адрес XXX 
  | 
          STACK 
  | 
        
Пример: Разменяне на стойностите на две променливи
// формалните параметри са
        параметри-псевдоними
      void swap1(int &a, int &b) //
        разменя стойностите на променливите a и b
            {
                int w = a;
                a = b;
                b = w;
            } 
      // формалните параметри са параметри-променливи от
          тип указатели
            void swap2(int *pa, int *pb)  // разменя
          стойностите на променливите с адреси указателите a и b
          
          {
                int w = *pa;
                *pa = *pb;
                *pb = w;
            }
            int main()
            {
                int x = 10, y = 12;
                swap1(x, y);    //
        фактическите параметри са промелниви от тип int
                cout << x << " " << y
            << endl;
                swap2(&x, &y); //
        фактическите параметри са адреси на променливи от тип int
                cout << x << " " << y
            << endl;
                return 0;
            } 
    
 * Пример (optional attribute): Класът
      Department представя департамент с име (тип string)
      и администратор (тип Employee), като в някой департамент
      може да няма администратор. 
      
      1. Решение без указатели с допълнителна данна
        exists, която показва има ли администратор:
        
class Department {
 . . .
private:
   string name;
   bool exists;     // true ако има администратор 
   Employee recept; // обектът съществува само ако има администратор
};
    2. Решение с указател. Ако в департамента има администратор, то
    указателят ще сочи към този обект (ще съдържа адреса на този обект),
    ако няма - указателят ще съдържа NULL.class Department {
 . . .
private:
   string name;
   Employee* receptionist;
};
    Пример (sharing):  Някои
      департаменти могат да имат администратор и/или секретар, като
      някъде един и същи служител може да заема и двете длъжности.
    
class Department {
. . .
private:
string name;
Employee* receptionist; // NULL ако няма; адреса на администратора, ако има
Employee* secretary; // NULL ако няма; адреса на секретаря, ако има
};
// department.cpp
#include <string> #include <iostream> using namespace std; #include "ccc_empl.h" /**
A department in an organization. */ class Department { public: Department(string n); void set_receptionist(Employee* e); void set_secretary(Employee* e); void print() const; private: string name; Employee* receptionist; Employee* secretary; }; /** Constructs a department with a given name. @param n the department name */ Department::Department(string n) { name = n; receptionist = NULL; secretary = NULL; } /** Sets the receptionist for this department. @param e the receptionist */ void Department::set_receptionist(Employee* e) { receptionist = e; } /** Sets the secretary for this department. @param e the secretary */ void Department::set_secretary(Employee* e) { secretary = e; } /** Prints a description of this department. */ void Department::print() const { cout << "Name: " << name << "\n" << "Receptionist: "; if (receptionist == NULL) cout << "None"; else cout << receptionist->get_name() << " " << receptionist->get_salary(); cout << "\nSecretary: "; if (secretary == NULL) cout << "None"; else if (secretary == receptionist) cout << "Same"; else cout << secretary->get_name() << " " << secretary->get_salary();
cout << "\n"; } int main() { Department shipping("Shipping"); Employee* harry = new Employee("Hacker, Harry", 45000); shipping.set_secretary(harry);
Department qc("Quality Control");
Employee* tina = new Employee("Tester, Tina", 50000); qc.set_receptionist(tina); qc.set_secretary(tina);
tina->set_salary(55000);
Department cs("Computer Support");
Employee* mary = new Employee("Brown, Mary", 33000);
cs.set_receptionist(mary);
Employee* boby = new Employee("Hill, Boby", 44000);
cs.set_secretary(boby);
shipping.print(); qc.print();
cs.print();
return 0; }
| nkirov@cpp % c++ department.cpp
              ccc_empl.cpp nkirov@cpp % ./a.out Name: Shipping Receptionist: None Secretary: Hacker, Harry 45000 Name: Quality Control Receptionist: Tester, Tina 55000 Secretary: Same Name: Computer Support Receptionist: Brown, Mary 33000 Secretary: Hill, Boby 44000  | 
        
 * Името на масив е константен
      указател.  Променлива
        указател може да служи за име на масив.
      
Пример:
       int a[3] = {10, 20, 30}; 
      int *pa = a; 
      // отпечатва 3 пъти стойността на a[0] 
      cout << a[0] <<"
            "<< pa[0] <<" "<< *pa;  // 10 10 10
      
* Адресна аритметика
 Синтаксис: <указател> + <число>
          Стойността на операцията е адрес, отместен с <число>
                променливи от същия тип
                
Примери:
       // отпечатва 4 пъти стойността на a[1]
      cout << a[1] <<"
            "<< pa[1] <<" "
                 << *(pa+1) <<"
            "<<(pa+1)[0]; // 20 20 20 20  
| Адрес | pa или a | pa+1 или a+1 | pa+2 или a+2 | 
| Стойност | 10 | 20 | 30 | 
| Индекс | 0 | 1 | 2 | 
| Достъп 1 | a[0] | a[1] | a[2] | 
| Достъп 2 | *pa | *(pa+1) | *(pa+2) | 
| Достъп 3 | pa[0] | (pa+1)[0] | (pa+1)[1] | 
| Достъп 4 | *a | *(a+1) | *(a+2) | 
char s[] = "Harry"; // същото като
char *s = "Harry";
| *s | *(s+1) | *(s+2) | *(s+3) | *(s+4) | *(s+5) | 
| 
                 s[0]  | 
              s[1] | s[2] | s[3] | s[4] | s[5] | 
| 'H' | 'a' | 'r' | 'r' | 'y' | '\0' | 
char* p = "Harry";Превръщане на обекти от тип string в тип char*.
string name(p); // or string name = p;
string num = "123";
const char* cnum = num.c_str();
int n = atoi(cnum);