Try   HackMD

07 лекция C/C++

(02.04.2022)

Про переменные

volatile - информирует компилятор, что значение переменной может меняться извне. Это может произойти под управлением операционной системы, аппаратных средств или другого потока. Поскольку значение может измениться, компилятор каждый раз загружает его из памяти.

int t = time();
//...
//int t = time(); //может преместить туда, и тогда
printf("Time: %i\n", time() - t);   //выведет 0
volatine int t = time(); //(оптимизатор, не трогай переменную)
//...
printf("Time: %i\n", time() - t);   //выведет правильно

Волатильную целочисленную переменную можно объявить как:

int volatile х; volatile int х;

Чтобы объявить указатель на эту переменную, нужно сделать следующее:

volatile int *х; int volatile *х;

Волатильный указатель на неволатильные данные используется редко, но допустим: int *volatile х;

Если вы хотите объявить волатильный указатель на волатильную область памяти, необходимо сделать следующее:

int volatile *volatile х;

Волатильные переменные не оптимизированы, что может пригодиться. Представьте следующую функцию:

int opt = 1;
void Fn(void) {
    start:
        if (opt == 1)
            goto start;
        else
            break;
}

На первый взгляд кажется, программа зациклится. Компилятор может оптимизировать ее следующим образом:

void Fn(void) {
    start:
    int opt = 1;
    if (true)
        goto start;
}

Вот теперь цикл точно станет бесконечным. Однако внешняя операция позволит записать 0 в переменную opt и прервать цикл.

Предотвратить такую оптимизацию можно с помощью ключевого слова volatile, например объявить, что некий внешний элемент системы изменяет переменную:

volatile int opt = 1;
void Fn(void) {
    start:
    if (opt == 1)
        goto start;
    else
        break;
}

Волатильные переменные используются как глобальные переменные в многопотоковых программах — любой поток может изменить общие переменные. Мы не хотим оптимизировать эти переменные.

Про указатели

int (*p)(int, int), p - это указатель на функцию возвращающую int и принимающую два аргумента типа int

Имя функции - указатель на эту функцию:

int f(int a, int b)
{
    //...
}

int (*p)(int, int);
p = f;
int x = p(2, 4);

Про выделение памяти

int *p2 = realloc(p, new_size) - изменяет размер выделенной памяти, на то, что попросили (и увел., и умен.) (может двигать или не двигать указатель, зависит от ситуации)

Может закончиться неудачей, если вернет NULL - не освободит память, правильный код

int *p = malloc(old_size);

{
    int *p2 = realloc(p, new_size); 
    //если p исходно NULL - работает как malloc

    if(p2)  //p2 != NULL
    p = p2;
    else
    //error    
}   // чтобы p2 уничтожился после этого

free (p);

realloc(p, 0) - НЕ освобождение памяти! (malloc и realloc тратит прамяти больше, чем вы просили!)

realloc - очевидно, медленно работает, не надо его писать в цикле, увеличивая рамер на 1. (после иниц массива, можно изменить размер на строго нужное)

Если выделять память в 2 раза больше - если realloc не удался - можем попробовать выделить на четверть (главное, чтобы 1/4 != 0).

Про структуры

struct A
{
    int x;
} *p;

(*p).x;
//или
p->x;    // полный эквивалент

За жизнь

typedef - это как создание переменной, но создание типа (как можно создать переменную, так можно создать и тип)

typedef int ( * structure_function ) (  struct A const * ,
                                        struct A const * );
// создает тип указатель на функцию которая отдает int, 
// принимает 2 константных указателя на структуру A, 
// называется structure_function 

Не трогали:

  • Модель памяти и треды

C закончился.

C++

Синтаксический сахар - эквивалент для уменьшения кода. Плюс C++ в том, что идею можно отобразить меньшим кодом, минус - понять такой код сложнее

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

С - void * <----> ...*

С++ - void * <---- ...*

В C++ необходимо писатьint *p2 = (int *) realloc(p, new_size);

Перегрузка функций

Отключить - extern "C" перед обьявлением функции (нельзя давать комп С)

#ifdef __cplusplus
    extern "C" {
#endif

//...code


#ifdef __cplusplus
    }
#endif

Можно значения по умолчанию писать через

#ifdef __cplusplus
    #define S(x) x
#else
    #define S(x)
#endif

int f(int a S(=0));

не перегрузка: (аргументы по умолчанию)

int y(int a, int b = 0);    //можно указывать сконца

y(2,4);
y(2); //y(2, 0)