Cpp указатель на функцию

Необработанные указатели (C++)

Указатель — это тип переменной. Он сохраняет адрес объекта в памяти и используется для доступа к нему. Необработанный указатель — это указатель, время существования которого не контролируется инкапсулирующим объектом, например интеллектуальным указателем. Необработанный указатель может быть назначен адрес другой переменной, не являющейся указателем, или ему может быть присвоено значение nullptr . Указатель, которому не назначено значение, содержит случайные данные.

Также можно разыменовать указатель, чтобы получить значение объекта, на который он указывает. Оператор доступа к членам предоставляет доступ к членам объекта.

 int* p = nullptr; // declare pointer and initialize it // so that it doesn't store a random address int i = 5; p = &i; // assign pointer to address of object int j = *p; // dereference p to retrieve the value at its address 

Указатель может указывать на типизированный объект или на void . Когда программа выделяет объект в куче в памяти, она получает адрес этого объекта в виде указателя. Такие указатели называются указателями-владельцем. Для явного освобождения объекта, выделенного в куче, необходимо использовать указатель-владение (или его копию), если он больше не нужен. Сбой при освобождении памяти приводит к утечке памяти и делает ее недоступной для любой другой программы на компьютере. Память, выделенная с помощью new , должна быть освобождена с помощью delete (или delete[] ). Дополнительные сведения см. в разделе new Операторы и delete .

 MyClass* mc = new MyClass(); // allocate object on the heap mc->print(); // access class member delete mc; // delete object (please don't forget!) 

Указатель (если он не объявлен как const ) можно увеличивать или уменьшать, указывая на другое расположение в памяти. Эта операция называется арифметикой указателя. Он используется в программировании в стиле C для перебора элементов в массивах или других структурах данных. Указатель const не может указывать на другое расположение памяти, и в этом смысле он похож на ссылку. Дополнительные сведения см. в разделах const и volatile указатели.

 // declare a C-style string. Compiler adds terminating '\0'. const char* str = "Hello world"; const int c = 1; const int* pconst = &c; // declare a non-const pointer to const int const int c2 = 2; pconst = &c2; // OK pconst itself isn't const const int* const pconst2 = &c; // pconst2 = &c2; // Error! pconst2 is const. 

В 64-разрядных операционных системах размер указателя — 64 бита. Размер указателя системы определяет, какой объем адресуемой памяти она может иметь. Все копии указателя указывают на одно и то же расположение в памяти. Указатели (вместе со ссылками) широко используются в C++ для передачи больших объектов в функции и из функций. Часто бывает эффективнее скопировать адрес объекта, чем скопировать весь объект. При определении функции укажите параметры указателя как const , если только функция не намерена изменять объект . Как правило, ссылки являются предпочтительным способом передачи объектов в функции, const если только значение объекта не может иметь значение nullptr .

Читайте также:  Php utf8 to text

Указатели на функции позволяют передавать функции другим функциям. Они используются для «обратных вызовов» в программировании в стиле C. Для этой цели в современном C++ используются лямбда-выражения .

Инициализация и доступ к членам

В следующем примере показано, как объявить, инициализировать и использовать необработанный указатель. Он инициализируется с помощью new , чтобы указать объект, выделенный в кучу, который необходимо явно delete . В примере также показан ряд опасностей, связанных с необработанными указателями. (Помните, что этот пример является программированием в стиле C, а не современным C++!)

#include #include class MyClass < public: int num; std::string name; void print() < std::cout >; // Accepts a MyClass pointer void func_A(MyClass* mc) < // Modify the object that mc points to. // All copies of the pointer will point to // the same modified object. mc->num = 3; > // Accepts a MyClass object void func_B(MyClass mc) < // mc here is a regular object, not a pointer. // Use the "." operator to access members. // This statement modifies only the local copy of mc. mc.num = 21; std::cout int main() < // Use the * operator to declare a pointer type // Use new to allocate and initialize memory MyClass* pmc = new MyClass< 108, "Nick" >; // Prints the memory address. Usually not what you want. std:: cout operator to access the object's public members pmc->print(); // "Nick, 108" // Copy the pointer. Now pmc and pmc2 point to same object! MyClass* pmc2 = pmc; // Use copied pointer to modify the original object pmc2->name = "Erika"; pmc->print(); // "Erika, 108" pmc2->print(); // "Erika, 108" // Pass the pointer to a function. func_A(pmc); pmc->print(); // "Erika, 3" pmc2->print(); // "Erika, 3" // Dereference the pointer and pass a copy // of the pointed-to object to a function func_B(*pmc); pmc->print(); // "Erika, 3" (original not modified by function) delete(pmc); // don't forget to give memory back to operating system! // delete(pmc2); //crash! memory location was already deleted > 

Арифметика указателя и массивы

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

  • Оператор sizeof возвращает общий размер массива в байтах.
  • Чтобы определить количество элементов, разделите общее количество байтов на размер одного элемента.
  • При передаче массива в функцию он преобразуется в тип указателя.
  • sizeof Когда оператор применяется к указателю, он возвращает размер указателя, например 4 байта в x86 или 8 байт в 64-разрядной архитектуре.
#include void func(int arr[], int length) < // returns pointer size. not useful here. size_t test = sizeof(arr); for(int i = 0; i < length; ++i) < std::cout > int main() < int i[5]< 1,2,3,4,5 >; // sizeof(i) = total bytes int j = sizeof(i) / sizeof(i[0]); func(i,j); > 

Некоторые арифметические операции можно использовать для указателей, отличных const от указателей, чтобы они указывали на другое расположение памяти. Указатели увеличиваются и уменьшаются с помощью ++ операторов , += -= и . Этот метод можно использовать в массивах и особенно полезен в буферах нетипизированных данных. Увеличивается void* на размер char (1 байт). Типизированный указатель увеличивается в зависимости от размера типа, на который он указывает.

В следующем примере показано, как можно использовать арифметику указателя для доступа к отдельным пикселям в растровом рисунке в Windows. Обратите внимание на использование new и delete и оператора разыменования.

#include #include using namespace std; int main() < BITMAPINFOHEADER header; header.biHeight = 100; // Multiple of 4 for simplicity. header.biWidth = 100; header.biBitCount = 24; header.biPlanes = 1; header.biCompression = BI_RGB; header.biSize = sizeof(BITMAPINFOHEADER); constexpr int bufferSize = 30000; unsigned char* buffer = new unsigned char[bufferSize]; BITMAPFILEHEADER bf; bf.bfType = 0x4D42; bf.bfSize = header.biSize + 14 + bufferSize; bf.bfReserved1 = 0; bf.bfReserved2 = 0; bf.bfOffBits = sizeof(BITMAPFILEHEADER) + sizeof(BITMAPINFOHEADER); //54 // Create a gray square with a 2-pixel wide outline. unsigned char* begin = &buffer[0]; unsigned char* end = &buffer[0] + bufferSize; unsigned char* p = begin; constexpr int pixelWidth = 3; constexpr int borderWidth = 2; while (p < end) < // Is top or bottom edge? if ((p < begin + header.biWidth * pixelWidth * borderWidth) || (p >end - header.biWidth * pixelWidth * borderWidth) // Is left or right edge? || (p - begin) % (header.biWidth * pixelWidth) < (borderWidth * pixelWidth) || (p - begin) % (header.biWidth * pixelWidth) >((header.biWidth - borderWidth) * pixelWidth)) < *p = 0x0; // Black >else < *p = 0xC3; // Gray >p++; // Increment one byte sizeof(unsigned char). > ofstream wf(R"(box.bmp)", ios::out | ios::binary); wf.write(reinterpret_cast(&bf), sizeof(bf)); wf.write(reinterpret_cast(&header), sizeof(header)); wf.write(reinterpret_cast(begin), bufferSize); delete[] buffer; // Return memory to the OS. wf.close(); > 

void* Указатели

Указатель на void просто указывает на необработанное расположение памяти. Иногда необходимо использовать void* указатели, например при передаче между кодом C++ и функциями C.

При приведение типизированного указателя к указателю void содержимое расположения памяти не изменяется. Однако сведения о типе теряются, поэтому вы не можете выполнять операции приращения или уменьшения. Расположение памяти может быть приведено, например, от MyClass* к void* и обратно к MyClass* . Такие операции по своей сути подвержены ошибкам и требуют тщательногоvoid анализа ошибок. Современный C++ не рекомендует использовать указатели void практически во всех обстоятельствах.

//func.c void func(void* data, int length) < char* c = (char*)(data); // fill in the buffer with data for (int i = 0; i < length; ++i) < *c = 0x41; ++c; >> // main.cpp #include extern "C" < void func(void* data, int length); >class MyClass < public: int num; std::string name; void print() < std::cout >; int main() < MyClass* mc = new MyClass; void* p = static_cast(mc); MyClass* mc2 = static_cast(p); std::cout name (pvoid); for(char* c = pchar; c < pchar + 1000; ++c) < *c = 0x00; >func(pvoid, 1000); char ch = static_cast(pvoid)[0]; std::cout 

Указатели на функции

В программировании в стиле C указатели функций используются в основном для передачи функций другим функциям. Этот метод позволяет вызывающему объекту настраивать поведение функции, не изменяя ее. В современном языке C++ лямбда-выражения предоставляют те же возможности с большей безопасностью типов и другими преимуществами.

Объявление указателя функции указывает сигнатуру, которую должна иметь функция , указывающая на:

// Declare pointer to any function that. // . accepts a string and returns a string string (*g)(string a); // has no return value and no parameters void (*x)(); // . returns an int and takes three parameters // of the specified types int (*i)(int i, string s, double d); 

В следующем примере показана функция combine , которая принимает в качестве параметра любую функцию, которая принимает std::string и возвращает std::string . В зависимости от функции, передаваемой в combine , она либо добавляет строку в начале, либо добавляет ее.

#include #include using namespace std; string base ; string append(string s) < return base.append(" ").append(s); >string prepend(string s) < return s.append(" ").append(base); >string combine(string s, string(*g)(string a)) < return (*g)(s); >int main()

Источник

Cpp указатель на функцию

В языке программирования C функция тоже имеет адрес и может иметь указатель. Указатель на функцию представляет собой выражение или переменную, которые используются для представления адреса функции. Указатель на функцию содержит адрес первого байта в памяти, по которому располагается выполняемый код функции.

Самым распространенным указателем на функцию является ее имя. С помощью имени функции мы можем вызывать ее и получать результат ее работы.

Но также указатель на функцию можно определять в виде отдельной переменной с помощью следующего синтаксиса:

тип (*имя_указателя) (типы_параметров);

Здесь тип представляет тип возвращаемого функцией значения.

имя_указателя представляет произвольно выбранный идентификатор в соответствии с правилами о наименовании переменных.

После названия указателя в скобках идут через запятую типы параметров. Если функция не принимает параметров, то указывается void .

Например, определим указатель на функцию:

Здесь определен указатель, который имеет имя message . Он может указывать на функции без параметров, которые возвращают тип void (то есть ничего не возвращают). После названия указателя идет (void) , что указывает, что функция не принимает параметров.

Применим этот указатель на функцию:

#include void hello() < printf("Hello, World \n"); >void goodbye() < printf("Good Bye, World \n"); >int main(void) < // определяем указатель на функцию void (*message) (void); message=hello; // указатель указывает на функцию hello message(); // вызываем функцию, на которую указыывет указатель message = goodbye; // указатель указывает на функцию goodbye message(); // вызываем функцию, на которую указыывет указатель return 0; >

Указателю на функцию можно присвоить функцию, которая соответствует указателю по возвращаемому типу и спецификации параметров:

То есть в данном случае указатель message теперь хранит адрес функции hello. И посредством обращения к указателю мы можем вызвать эту функцию:

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

Hello, World Good Bye, World

При определении указателя стоит обратить внимание на скобки вокруг имени. Так, использованное выше определение

НЕ будет аналогично следующему определению:

Во втором случае определен не указатель на функцию, а прототип функции message, которая возвращает указатель типа void* .

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

Рассмотрим еще один указатель на функцию, которая возвращает значение типа int и принимает два параметра типа int :

#include int add(int x, int y) < return x + y; >int subtract(int x, int y) < return x - y; >int main(void) < int a = 10; int b = 5; int result; int (*operation)(int, int); operation=add; result = operation(a, b); printf("result = %d \n", result); // result=15 operation = subtract; result = operation(a, b); printf("result = %d \n", result); // result=5 return 0; >

Здесь определен указатель operation, который может указывать на функцию с двумя параметрами типа int , возвращающую также значение типа int . Соответственно мы можем присвоить указателю адреса функций add и subtract и вызвать их, передав при вызове в указатель нужные значения для параметров.

Массивы указателей на функции

Кроме одиночных указателей на функции мы можем определять их массивы. Для этого используется следующий формальный синтаксис:

тип (*имя_массива[размер]) (параметры)

Здесь actions представляет массив указателей на функции, каждая из которых обязательно должна принимать два параметра типа int и возвращать значение типа double .

Посмотрим применение массива указателей на функции на примере:

#include void add(int x, int y) < printf("x + y = %d \n", x + y); >void subtract(int x, int y) < printf("x + y = %d \n", x - y); >void multiply(int x, int y) < printf("x * y = %d \n", x * y); >int main(void) < int a = 10; int b = 5; void (*operations[3])(int, int) = ; // получаем длину массива int length = sizeof(operations)/sizeof(operations[0]); for(int i=0; i < length; i++) < operations[i](a, b); // вызов функции по указателю >return 0; >

Здесь массив operations содержит три функции add, subtract и multiply, которые последовательно вызываются в цикле через перебор массива в функции main.

Источник

Оцените статью