Letysite.ru

IT Новости с интернет пространства
0 просмотров
Рейтинг статьи
1 звезда2 звезды3 звезды4 звезды5 звезд
Загрузка...

Typedef function pointer

Понимание typedefs для указателей функций в C

Я всегда был немного озадачен, когда читал код других людей,который был набран для указателей на функции с аргументами. Я помню, что мне потребовалось некоторое время, чтобы добраться до такого определения, пытаясь понять численный алгоритм, написанный в C некоторое время назад. Итак, не могли бы вы поделиться своими советами и мыслями о том, как писать хорошие шрифты для указателей на функции (делать и не делать), почему они полезны и как понимать работу других? Спасибо!

7 Ответов

Рассмотрим функцию signal() из стандарта C:

Совершенно неясно очевидно — это функция, которая принимает два аргумента, целое число и указатель на функцию, которая принимает целое число в качестве аргумента и ничего не возвращает, и она ( signal() ) возвращает указатель на функцию, которая принимает целое число в качестве аргумента и ничего не возвращает.

тогда вы можете вместо этого объявить signal() как:

Это означает то же самое, но обычно считается несколько более легким для чтения. Очевидно, что функция принимает значения int и SignalHandler и возвращает значение SignalHandler .

Но к этому нужно немного привыкнуть. Единственное, что вы не можете сделать, это написать функцию обработчика сигнала, используя SignalHandler typedef в определении функции.

Я все еще придерживаюсь старой школы, которая предпочитает вызывать указатель функции как:

Современный синтаксис использует только:

Я понимаю, почему это работает — я просто предпочитаю знать, что мне нужно искать, где инициализируется переменная, а не для функции с именем functionpointer .

Я уже видел это объяснение раньше. И тогда, как это происходит сейчас, я думаю, что мне не удалось установить связь между этими двумя утверждениями:

Или, что я хочу спросить, это то, что лежит в основе концепции, которую можно использовать, чтобы придумать вторую версию, которая у вас есть? Что является фундаментальным, что соединяет «SignalHandler» и первый typedef? Я думаю, что здесь нужно объяснить, что на самом деле делает typedef.

Давай попробуем еще раз. Первый из них взят прямо из стандарта C — я перепечатал его и проверил, что у меня были правильные скобки (не до тех пор, пока я не исправил его — это трудно запомнить).

Прежде всего, помните, что typedef вводит псевдоним для типа. Итак, псевдоним- SignalHandler , и его тип-это:

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

Часть ‘returns nothing’ пишется как void ; аргумент, который является целым числом, является (Я надеюсь) самоочевидным. Следующая нотация-это просто (или нет) как C пишет указатель на функцию, принимая аргументы как указано и возвращая данный тип:

После создания типа обработчика сигнала я могу использовать его для объявления переменных и так далее. Например:

Итак, что же мы здесь сделали — кроме того, что опустили 4 стандартных заголовка, которые были бы необходимы для того, чтобы код компилировался чисто?

Первые две функции — это функции, которые принимают одно целое число и ничего не возвращают. Один из них на самом деле не возвращается вообще благодаря exit(1); , но другой возвращается после печати сообщения. Имейте в виду, что стандарт C не позволяет вам делать очень много внутри обработчика сигналов; POSIX немного более щедр в том, что разрешено, но официально не разрешает вызывать fprintf() . Я также распечатываю номер сигнала, который был получен. В функции alarm_handler() значение всегда будет SIGALRM , так как это единственный сигнал, для которого он является обработчиком, но signal_handler() может получить SIGINT или SIGQUIT в качестве номера сигнала, потому что одна и та же функция используется для обоих.

Затем я создаю массив структур, где каждый элемент идентифицирует номер сигнала и обработчик, который будет установлен для этого сигнала. Я решил беспокоиться о 3 сигналах; я бы часто беспокоился о SIGHUP , SIGPIPE и SIGTERM тоже и о том, определены ли они ( #ifdef условная компиляция), но это только усложняет дело. Я бы также, вероятно, использовал POSIX sigaction() вместо signal() , но это другая проблема; давайте придерживаться того, с чего мы начали.

Функция main() выполняет итерацию по списку устанавливаемых обработчиков. Для каждого обработчика он сначала вызывает signal() , чтобы выяснить, игнорирует ли процесс в данный момент сигнал, и при этом устанавливает SIG_IGN в качестве обработчика, что гарантирует, что сигнал остается игнорируемым. Если сигнал ранее не игнорировался, то он снова вызывает signal() , на этот раз для установки предпочтительного обработчика сигнала. (Другое значение предположительно SIG_DFL , обработчик сигнала по умолчанию для сигнала.) Поскольку первый вызов ‘signal()’ устанавливает обработчик в SIG_IGN и signal() возвращает предыдущий обработчик ошибок, значение old после оператора if должно быть SIG_IGN -следовательно, утверждение. (Ну, это может быть SIG_ERR , если что — то резко пойдет не так-но тогда я узнаю об этом из стрельбы assert.)

Затем программа делает свое дело и выходит нормально.

Обратите внимание, что имя функции можно рассматривать как указатель на функцию соответствующего типа. Когда вы не применяете скобки вызова функции — как, например, в инициализаторах-имя функции становится указателем функции. Именно поэтому целесообразно вызывать функции через обозначение pointertofunction(arg1, arg2) ; когда вы видите alarm_handler(1) , вы можете считать, что alarm_handler является указателем на функцию и, следовательно, alarm_handler(1) является вызовом функции через указатель функции.

Таким образом, до сих пор я показал, что переменная SignalHandler является относительно простой в использовании, если у вас есть какой-то правильный тип значения, чтобы присвоить ему — что и обеспечивают две функции обработчика сигналов.

Теперь мы возвращаемся к вопросу — как эти два объявления для signal() соотносятся друг с другом.

Давайте рассмотрим второе заявление:

Если бы мы изменили имя функции и тип вот так:

у вас не будет проблем с интерпретацией этого как функции, которая принимает int и double в качестве аргументов и возвращает значение double (не так ли? может быть, вам лучше не признаваться, если это проблематично , но, возможно, вы должны быть осторожны, задавая такие сложные вопросы, как этот, если это проблема).

Теперь , вместо того чтобы быть double , функция signal() принимает SignalHandler в качестве второго аргумента и возвращает один в качестве результата.

Механика, с помощью которой это также можно рассматривать как:

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

В общем, в C механизм объявления таков, что если вы пишете:

затем, когда вы пишете var , он представляет собой значение данного type . Например:

В стандарте typedef рассматривается как класс хранения в grammar, скорее как static и extern являются классами хранения.

означает, что когда вы видите переменную типа SignalHandler (скажем alarm_handler), вызываемую как:

результат имеет type void — результата нет. А (*alarm_handler)(-1); -это вызов alarm_handler() с аргументом -1 .

Итак, если мы объявили:

это означает, что:

представляет собой пустое значение. И поэтому:

эквивалентный. Теперь signal() является более сложным, потому что он не только возвращает SignalHandler , но и принимает как int, так и SignalHandler в качестве аргументов:

Если это все еще сбивает вас с толку, я не знаю, как помочь — это все еще на некоторых уровнях загадочно для меня, но я привык к тому, как это работает, и поэтому могу сказать вам, что если вы будете придерживаться этого в течение еще 25 лет или около того, это станет вашей второй натурой (и, возможно, даже немного быстрее, если вы умны).

Указатель функции похож на любой другой указатель, но он указывает на адрес функции, а не на адрес данных (в куче или стеке). Как и любой указатель, он должен быть набран правильно. Функции определяются их возвращаемым значением и типами параметров, которые они принимают. Таким образом, чтобы полностью описать функцию, вы должны включить ее возвращаемое значение и тип каждого параметра является приемлемым. Когда вы набираете такое определение, вы даете ему ‘friendly name’, что облегчает создание и использование указателей, использующих это определение.

Так например предположим что у вас есть функция:

тогда следующий typedef:

может использоваться для указания на эту функцию doMulitplication . Это просто определение указателя на функцию, которая возвращает float и принимает два параметра, каждый из которых имеет тип float. Это определение имеет понятное имя pt2Func . Обратите внимание, что pt2Func может указывать на функцию ANY, которая возвращает поплавок и принимает 2 поплавка.

Таким образом, вы можете создать указатель, который указывает на функцию doMultiplication следующим образом:

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

AticleWorld

aticleworld offer c tutorial,c programming,c courses and c++, microcontroller tips

typedef in C language: 7 application you should know

If you are fresher, then definitely a question comes in your mind what is typedef in C and why we use typedef in C programming. If you are looking for these questions like how to use typedef in c, what is typedef in C or why we use typedef in C, then you are at the right place. Here I will discuss all small and big things related to the C typedef. So let us come on the topic.

What is typedef in C?

A typedef defines a new name for existing types and does not introduce a new type. It is the (partial storage-class specifier) compiler directive mainly use with user-defined data types (structure, union or enum)) to reduce their complexity and increase code readability and portability.

In C programming, we can use typedef declarations to create shorter or more meaningful names for types already defined by C (inbuilt data types like int, char, float) or for types that you have declared.

Syntax of typedef in C:

We can see that the declaration of typedef looks like the declaration of a variable but in the case of the typedef, the identifier becomes a synonym for the type. Let us see an example,

Now UnsignedInt becomes a synonym of unsigned int and we can use UnsignedInt in place of unsigned int.

Note: A typedef creates synonyms or a new name for existing types it does not create new types.

Why we use typedef in C?

There might be many reasons but here I am explaining two important reasons why we should use typedef in C programming. If you know more reasons, then please write it in the comment box it will help others.

Code Portability

We know that the size of the int is not specified by the C standard. The C standard only explained the minimum size of the integer that is 16 bits. So if you working on a platform for where you want the size of int is always 32 bits, so in that situation typedef is useful. Let us see an example, where int size is always 32 bits with the help of a typedef.

Читать еще:  Как усилить wifi в квартире

Code Readability:

Consider the below declaration,

Here, pf is a function returning a pointer to an array whose size is 4 and contains pointers to function returning an int. Let use see code without the typedef.

Output:

We can make the above declaration understandable and increase the code readability using the typedef in C. Please see the example below.

Output:

Code Clarity:

A typedef is increased the code readability to give the new name to a complex declaration. In the case of the structure and union, it is very good to use a typedef, it helps to avoid the struct keyword at the time of variable declaration. You can also give a meaningful name to an existing type (predefined type or user-defined type).

Let’s take an example
Suppose there is a structure, which contains the information of a student.

Whenever in the program we need to declare a variable of the above structure than every time we have to write the struct keyword with the tag name.

But if we are used the typedef with the above structure then there is no need to write struct keyword at the time of variable declaration.

Now sStudentInformation is new types and whenever in the program we need the above structure variable then no need to write the struct.

Scope of typedef in C

Good thing is that the typedef follows the scope rules. It means, it can have block scope, file scope, etc. If we want we can use the same name for typedef in different scope. Let us see examples to understand how the typedef in C follows the scope rule.

Example Code when typedef in different Scope:

OutPut:

Explanation of the code

We can see that in the code INT is using with typedef for two different types int and char *. We are not getting compiler error because both INT has a different meaning in different scope. Globally INT behaves like int but in the main function, it behaves like the char *.

Example code when typedef in the same Scope:

OutPut:

Explanation of the code

Both typedef in the same scope with same name (TypeName). So here compiler getting confuses and giving the error, see the error message.

If you want to learn more about the c language, here 10 Free days C video course for you.

Application of typedef in C

typedef keyword is used in many places in C programming. But mostly it uses with user-defined data types like structure, union or enum. So here, we are going to discuss some places where you can use typedef wisely which increases the code readability and complexity.

Use of typedef with predefine data types:

Some time to increase the code readability and portability we need to use the typedef with predefined data types. Let us see few examples to understand this concept.

Suppose you are working on an application which used to measure the distance. So beside using the unsigned int, you can create own types with a meaning full name using the typedef. In the below example, we are creating own type distance.

At the beginning of the article, I have already discussed how you can increase the code portability using the typedef.

Use of typedef with pointers

In C programming, we can use the typedef with pointers. Let us see an example,

After the above statement, intPtr becomes an alias of a pointer to int and charPtr becomes an alias of a pointer to char. So if we need to declare the pointer to int or pointer to char, then we can declare it without using the asterisk (*) symbol. So let us see how we can declare a pointer to int or pointer to char using the intPtr or charPtr.

Above declaration is same as:

Let us see example code for better understanding,

Output:

Use of typedef with a structure

When we used typedef with structure then there is no need to write struct keyword at the time of variable declaration. It saves the extra keystroke and makes the code cleaner. Let us see the example,

After this declaration, sStudInfo is an alias of struct sStudentsInformations. So instead of using struct sStudentsInformations to declare new structure variables we can use just use sStudInfo.

We can also combine a structure definition with a typedef. Let us see the syntax,

If we used a typedef with the structure definition, then we can also remove the structure tag.

But it is my recommendation to use the structure tag at the time of the structure declaration. Because if we have not used a structure tag with structure then we will get a compiler error when structure tries to reference itself.

But if we used a structure tag with the above code then it will work fine.

Use of typedef with structure pointer

We can also use a typedef with a structure pointer and avoid the use of struct keyword at the time of the structure pointer declaration. Let us see the example,

We can create different types using the typedef with structure definition. See the below example in which we are creating two types one is structure pointer and the second is structure variable.

Use of typedef with arrays:

We can use a typedef with an array. It increases the readability. Some times in the code we need to create a multi-dimension array. We know that the handling of a multidimensional array is complicated. It becomes more dangerous when we need to pass a multidimensional array in the function. So let us see an example where we are using a typedef with an array and passing this array in function by the call by reference.

OutPut:

Code Explanation:

In the above example, InitBrickPrice is the function that takes the pointer to the array as arguments and initializes the passed array with a defined price. After using the typedef Brick_Price becomes an array of three integers.

Use of typedef with the 2D array,

Use of typedef with the function pointer

Using a typedef, we can make the declaration of function pointer easy and readable. The typedef is very helpful when we create an array of the function pointer or a function returns a function pointer. Let us see the example,

Now, apfArithmatics is a type of array of a function pointer and we can create a variable using this created type. Let us see the example where we have created a variable and initializing it by three functions AddTwoNumber, SubTwoNumber, and MulTwoNumber.

Some times in the code we need to typecast the address using the function pointer. It also becomes easy using the typedef.

Now using typedef,

Use of typedef enum in C

We can use the typedef and enum together in C programming. If we use typedef with enum in C then it increases the code readability and creates a new type for the enum. Let see an example, where I am creating a list of errors using the enum and defining a new type using the typedef.

Function Pointers in C and C++

Example Uses of Function Pointers

Functions as Arguments to Other Functions

If you were to write a sort routine, you might want to allow the function’s caller to choose the order in which the data is sorted; some programmers might need to sort the data in ascending order, others might prefer descending order while still others may want something similar to but not quite like one of those choices. One way to let your user specify what to do is to provide a flag as an argument to the function, but this is inflexible; the sort function allows only a fixed set of comparison types (e.g., ascending and descending).

A much nicer way of allowing the user to choose how to sort the data is simply to let the user pass in a function to the sort function. This function might take two pieces of data and perform a comparison on them. We’ll look at the syntax for this in a bit.

Callback Functions

Another use for function pointers is setting up «listener» or «callback» functions that are invoked when a particular event happens. The function is called, and this notifies your code that something of interest has taken place.

Why would you ever write code with callback functions? You often see it when writing code using someone’s library. One example is when you’re writing code for a graphical user interface (GUI). Most of the time, the user will interact with a loop that allows the mouse pointer to move and that redraws the interface. Sometimes, however, the user will click on a button or enter text into a field. These operations are «events» that may require a response that your program needs to handle. How can your code know what’s happening? Using Callback functions! The user’s click should cause the interface to call a function that you wrote to handle the event.

To get a sense for when you might do this, consider what might happen if you were using a GUI library that had a «create_button» function. It might take the location where a button should appear on the screen, the text of the button, and a function to call when the button is clicked. Assuming for the moment that C (and C++) had a generic «function pointer» type called function, this might look like this: Whenever the button is clicked, callback_func will be invoked. Exactly what callback_func does depends on the button; this is why allowing the create_button function to take a function pointer is useful.

Function Pointer Syntax

The syntax for declaring a function pointer might seem messy at first, but in most cases it’s really quite straight-forward once you understand what’s going on. Let’s look at a simple example: In this example, foo is a pointer to a function taking one argument, an integer, and that returns void. It’s as if you’re declaring a function called «*foo», which takes an int and returns void; now, if *foo is a function, then foo must be a pointer to a function. (Similarly, a declaration like int *x can be read as *x is an int, so x must be a pointer to an int.)

Читать еще:  Как восстановить powerpoint

The key to writing the declaration for a function pointer is that you’re just writing out the declaration of a function but with (*func_name) where you’d normally just put func_name.

Reading Function Pointer Declarations

Initializing Function Pointers

Using a Function Pointer

Function Pointers in the Wild

Let’s go back to the sorting example where I suggested using a function pointer to write a generic sorting routine where the exact order could be specified by the programmer calling the sorting function. It turns out that the C function qsort does just that.

From the Linux man pages, we have the following declaration for qsort (from stdlib.h): Note the use of void*s to allow qsort to operate on any kind of data (in C++, you’d normally use templates for this task, but C++ also allows the use of void* pointers) because void* pointers can point to anything. Because we don’t know the size of the individual elements in a void* array, we must give qsort the number of elements, nmemb, of the array to be sorted, base, in addition to the standard requirement of giving the length, size, of the input.

But what we’re really interested in is the compar argument to qsort: it’s a function pointer that takes two void *s and returns an int. This allows anyone to specify how to sort the elements of the array base without having to write a specialized sorting algorithm. Note, also, that compar returns an int; the function pointed to should return -1 if the first argument is less than the second, 0 if they are equal, or 1 if the second is less than the first.

For instance, to sort an array of numbers in ascending order, we could write code like this:

Using Polymorphism and Virtual Functions Instead of Function Pointers (C++)

But Are You Really Not Using Function Pointers?

Function Pointers Summary

Syntax

Declaring

Initializing

Invoking

Benefits of Function Pointers

  • Function pointers provide a way of passing around instructions for how to do something
  • You can write flexible functions and libraries that allow the programmer to choose behavior by passing function pointers as arguments
  • This flexibility can also be achieved by using classes with virtual functions

I am grateful to Alex Hoffer and Thomas Carriero for their comments on a draft of this article.

Function Pointers in C

What is Function Pointer?

In C function pointers are usual pointer variables, but they are little different from pointer to objects. For example, function pointers are not allowed to cast void* . Also, pointer arithmetic is also not defined anywhere in the C standard for pointer to function types. Leaving these differences apart function pointer points to the address of a function as normal pointer variable points to the address of a variable.

Being C programmers, we all know that a running C program occupies certain space in memory. Both, the executable compiled program code and the variables used by the program, are put inside the memory. And, every byte of memory is addressable. Thus a function in the program code will have an address just like a character variable or any other variable stored in memory. While, using function pointer; function’s physical location or address in memory is assigned to a pointer. Function’s address is the entry point of the function and it is the address used when the function is called. Once a pointer points to a function, the function can be called through that pointer.

A function name, without a following set of parentheses, produces a pointer to that function. The address operator can also be applied to a function name to produce a pointer to it. For example, if myFunc is a pointer to a function, the corresponding function can be called either by writing myFunc() or (*myFunc)() . If the function takes arguments, they can be listed inside the parentheses.

Define Function Pointers

Because a function pointer is nothing else than a variable, so it must be defined as normal pointer variables are defined. For illustration, we define a function pointer ptrMaxFunctin . Function pointer ptrMaxFunctin points to a function retMax , which takes two int values as arguments and returns an int value (the maximum one). Let’s take a look at the following declaration.

We obtain the address of a function by using the function’s name without any parentheses or arguments. (This is similar to the way an array’s address is obtained when only the array name, without indexes, is used.)

For beginners, a way to remember writing function pointer syntax:
Write a normal function declaration: int retMax (int n1, int n2);
Wrap function name with pointer syntax: int (*retMax) (int n1, int n2);
Change the name to the pointer name: int (*ptrMaxFunctin) (int n1, int n2);

Calling a Function Using Function Pointer

Let’s put the function pointer ptrMaxFunctin in a small program to see how to call a function using function pointer. The following program develops a trivial function retMax , which returns the maximum number among passed integers, to demonstrate calling a function by function pointer.

Let’s look closely at above program. First, examine the declaration for ptrMaxFunctin in main() . It is shown here:

This declaration tells the compiler that ptrMaxFunctin is a pointer to a function that has two int parameters, and returns an int result. The parentheses around ptrMaxFunctin are necessary because * is a prefix operator and it has lower precedence than (), so parentheses are necessary to force the proper association.

Now examine the next statement ptrMaxFunctin = retMax; where ptrMaxFunctin is assigned the address of retMax . After this assignment; function retMax can be called with the help of the pointer ptrMaxFunctin to function retMax . In above program it has been called inside the printf .

Pass a Function Pointer as an Argument

Function pointers also allow functions to be passed as arguments to other functions. We pass function name to other function as a parameter and it is received by the formal function pointer parameter. It is in no way different from usual pointer arguments. Following program fp-arguments.c demonstrates passing function pointer as argument.

Read above program carefully, and examine compareStrings() function. It declares three parameters: two character pointers, and one function pointer, cmp . Also notice that the function pointer is declared using the same format as was functPtr inside main() . Thus, cmp is able to receive a pointer to a function that takes two const char* arguments and returns an int result. Like the declaration for functPtr , the parentheses around the *cmp are necessary for the compiler to interpret this statement correctly. Moreover, C provides more flexible syntax for function pointers just like ordinary functions. For example, (*cmp)(a, b) and cmp(a, b) are the same. However, latter may cause confusion if this is a function pointer or ordinary function call.

When the program begins, it assigns functPtr the address of strcmp() , the standard string comparison function. Next, it prompts the user for two strings, and then it passes pointers to those strings along with functPtr to compareStrings() , which compares the strings for equality. Inside compareStrings() , the expression calls strcmp() , which is pointed to by cmp , with the arguments a and b . The parentheses around *cmp are necessary. This is one way to call a function through a pointer.

Note that you can call compareStrings() by using strcmp() directly, as shown here:

That’s the way you can pass a function pointer to a function as an argument. But, you may be wondering that why anyone would write a program like the one just shown. Obviously, nothing is gained, and significant confusion is introduced. So, it seems nice time to prove the worth of function pointers.

Application Uses of Function Pointers

One common application for pointers to functions is in passing them as arguments to other functions.The standard C library uses this, for example, in the function qsort , which performs a quick sort on an array of data elements. This function takes as one of its arguments a pointer to a function that is called whenever qsort needs to compare two elements in the array being sorted. In this manner, qsort can be used to sort arrays of any type, as the actual comparison of any two elements in the array is made by a user supplied function, and not by the qsort function itself.

Another common application for function pointers is to create what is known as dispatch tables. You can’t store functions themselves inside the elements of an array. However, it is valid to store function pointers inside an array. Given this, you can create tables that contain pointers to functions to be called. For example, you might create a table for processing different commands that will be entered by a user. Each entry in the table could contain both the command name and a pointer to a function to call to process that particular command. Now, whenever the user enters a command, you can look up the command inside the table and invoke the corresponding function to handle it.

Subsequent sections explain you typedef and array of function pointers.

How to typedef Function Pointer?

C language provides a language construct typedef that associates a keyword to a type. It gives a new name to a type that may make program more readable. Type definition is proved very useful especially for pointers to functions. However, typedef construct only makes the writing/reading of a program easy; the compiler just expands the typedef definition before compiling the actual code. If you want to create an array of a certain pointer to function then typedef makes it readable because the original syntax of pointer to function looks odd and confusing too. Below declaration tells how to define a custom name for a function pointer. While creating an array of pointers to functions typedef really helps. Next section demonstrates that.

Читать еще:  Tpoint c builder

Using Arrays of Function Pointers?

An array of function pointers can be created for the functions of same return type and taking same type and same number of arguments. For example, if you have four function definitions for performing basic arithmetic operations (addition, subtraction, multiplication, and division) on two integer quantities, all these functions will take two integer arguments and will return an integer as a result. For such similar

argument operations you can create an array of function pointers and select a proper function by its index rather than its name. The following example demonstrates it.

Read above program carefully and you will observe that first functionPtr type is defined and then an array of four function pointers has been created. Array arrayFp of function pointers holds addresses of four functions getSum , getDifference , getProduct , getDivision respectively. All four functions are later called with the help of arrayFp using their index in the array.

Last Word

In this tutorial we discussed how to define and use function pointers in C/C++ programs? We also discussed calling a function using function pointer, passing a function to another function as an argument using function pointer, typedef of function pointers and array of function pointers. A function pointer or pointer to function in C is a usual pointer variable that points to the address of a function in memory. Through a pointer a function can be passed to other function as an argument and returned from a function. Hope you have enjoyed reading this article. Please do write us if you have any suggestion/comment or come across any error on this page. Thanks for reading!

Понимание typedefs для указателей функций в C

172 [2009-10-20 00:55:00]

Я всегда был немного в тупике, когда читал код других народов, который имел typedefs для указателей на функции с аргументами. Я помню, что мне потребовалось некоторое время, чтобы обойти такое определение, пытаясь понять численный алгоритм, написанный на C некоторое время назад. Итак, вы могли бы поделиться своими советами и мыслями о том, как писать хорошие typedefs для указателей на функции (Do and Do not), почему они полезны и как понимать работу других? Спасибо!

c typedef function-pointers

6 ответов

Рассмотрим функцию signal() из стандарта C:

Совершенно неясно очевидно — это функция, которая принимает два аргумента, целое число и указатель на функцию, которая принимает целое число в качестве аргумента и ничего не возвращает, и она ( signal() ) возвращает указатель на функцию, которая принимает целое число как аргумент и ничего не возвращает.

то вы можете вместо этого объявить signal() как:

Это означает то же самое, но обычно считается несколько более легким для чтения. Понятно, что функция принимает int и a SignalHandler и возвращает a SignalHandler .

Придется немного привыкнуть. Единственное, что вы не можете сделать, — это написать функцию обработчика сигнала, используя SignalHandler typedef в определении функции.

Я все еще из старой школы, которая предпочитает ссылаться на указатель функции как:

Современный синтаксис использует только:

Я вижу, почему это работает — я просто предпочитаю знать, что мне нужно искать, где инициализируется переменная, а не для функции с именем functionpointer .

Я уже видел это объяснение. И тогда, как и сейчас, я думаю, что я не получал связь между двумя утверждениями:

Или, что я хочу спросить, какова основная концепция, которую можно использовать, чтобы придумать вторую версию, которую у вас есть? Что является фундаментальным, что связывает «SignalHandler» и первый typedef? Я думаю, что здесь должно быть объяснено то, что на самом деле делает typedef.

Повторите попытку. Первый из них поднят прямо из стандарта C — я перепечатал его и проверил, что у меня есть круглые скобки справа (не до тех пор, пока я не исправил его — это жесткий файл cookie, чтобы помнить).

Прежде всего, помните, что typedef вводит псевдоним для типа. Итак, псевдоним SignalHandler , и его тип:

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

Часть «возвращает ничего» написана void ; аргумент, являющийся целым числом, является (я уверен) самоочевидным. Следующие обозначения просто (или нет), как C заклинания указатель на функцию принятия аргументов, как указано, и возвращения данного типа:

После создания типа обработчика сигнала я могу использовать его для объявления переменных и т.д. Например:

Итак, что мы здесь сделали — кроме опускания 4 стандартных заголовков, которые необходимы для того, чтобы код был скомпилирован?

Первые две функции — это функции, которые берут одно целое и не возвращают ничего. Один из них фактически не возвращается вообще благодаря exit(1); , а другой возвращает после печати сообщения. Имейте в виду, что стандарт C не позволяет вам делать очень многое внутри обработчика сигнала; POSIX является немного более щедрым в том, что разрешено, но официально не санкционирует вызов fprintf() . Я также распечатываю полученный номер сигнала. В функции alarm_handler() значение всегда будет SIGALRM , так как это единственный сигнал, которым он является обработчик, но signal_handler() может получить SIGINT или SIGQUIT в качестве номера сигнала, поскольку эта же функция используется для обоих.

Затем я создаю массив структур, где каждый элемент идентифицирует номер сигнала и обработчик, который должен быть установлен для этого сигнала. Я решил беспокоиться о 3 сигналах; Я часто беспокоюсь о SIGHUP , SIGPIPE и SIGTERM тоже и о том, определены ли они ( #ifdef условная компиляция), но это просто усложняет ситуацию. Я бы также использовал POSIX sigaction() вместо signal() , но это еще одна проблема; пусть будет придерживаться того, с чего мы начали.

Функция main() выполняет итерацию по списку установленных обработчиков. Для каждого обработчика он сначала вызывает signal() , чтобы узнать, игнорирует ли этот процесс данный процесс, и при этом устанавливает SIG_IGN в качестве обработчика, который гарантирует, что сигнал остается игнорированным. Если ранее сигнал не был проигнорирован, он снова вызывает signal() , на этот раз для установки предпочтительного обработчика сигнала. (Другое значение, по-видимому, SIG_DFL , обработчик сигнала по умолчанию для сигнала.) Поскольку первый вызов «signal()» устанавливает обработчик на SIG_IGN и signal() , возвращает предыдущий обработчик ошибок, значение old после оператора if должно быть SIG_IGN — следовательно, утверждение. (Ну, это могло бы быть SIG_ERR , если что-то пошло не так, но потом я узнаю об этом из-за увольнения.)

Затем программа выполняет свои действия и обычно выходит.

Обратите внимание, что имя функции можно рассматривать как указатель на функцию соответствующего типа. Если вы не применяете круглые скобки функций — например, в инициализаторах — имя функции становится указателем на функцию. Именно поэтому разумно вызывать функции через нотацию pointertofunction(arg1, arg2) ; когда вы видите alarm_handler(1) , вы можете считать, что alarm_handler является указателем на функцию, и поэтому alarm_handler(1) является вызовом функции с помощью указателя функции.

Итак, до сих пор я показал, что переменная SignalHandler относительно проста в использовании, если у вас есть подходящий тип значения для ее назначения — это то, что два обработчика сигнала функции обеспечивают.

Теперь мы вернемся к вопросу — как два объявления для signal() относятся друг к другу.

Просмотрите второе объявление:

Если мы изменили имя функции и тип вроде этого:

у вас не возникло бы проблемы с интерпретацией этого как функции, которая принимает аргументы int и double в качестве аргументов и возвращает значение double (не так ли? возможно, вам лучше не «испортиться», если это проблематично — но, может быть, вы должны быть осторожны, задавая вопросы так же сложно, как это, если это проблема).

Теперь вместо double функция signal() принимает в качестве второго аргумента SignalHandler и возвращает ее как результат.

Механика, с помощью которой это также можно трактовать как:

сложно объяснить, поэтому я, вероятно, испортил это. На этот раз я дал имена параметров, хотя имена не являются критическими.

В общем случае в C механизм объявления таков, что если вы пишете:

тогда, когда вы пишете var , оно представляет значение данного type . Например:

В стандарте typedef рассматривается как класс хранения в грамматике, скорее, как static и extern являются классами хранения.

означает, что когда вы видите переменную типа SignalHandler (скажем, alarm_handler), вызывается как:

результат имеет type void — результата нет. И (*alarm_handler)(-1); является вызовом alarm_handler() с аргументом -1 .

Итак, если мы объявили:

это означает, что:

представляет значение void. И поэтому:

эквивалентен. Теперь signal() является более сложным, поскольку он возвращает не только SignalHandler , но и принимает как int, так и a SignalHandler в качестве аргументов:

Если это все еще вас смущает, я не уверен, как помочь — это все еще на некоторых уровнях таинственно для меня, но я привык к тому, как это работает, и поэтому может сказать вам, что если вы придерживаетесь его для еще 25 лет или около того, это станет для вас второй натурой (а может быть, даже немного быстрее, если вы умны).

52 psychotik [2009-10-20 01:17:00]

Указатель функции подобен любому другому указателю, но он указывает на адрес функции вместо адреса данных (в куче или стеке). Как и любой указатель, его нужно набирать правильно. Функции определяются их возвращаемым значением и типами параметров, которые они принимают. Поэтому, чтобы полностью описать функцию, вы должны включить ее возвращаемое значение и тип каждого параметра принимает. Когда вы вводите такое определение, вы даете ему «дружественное имя», которое упрощает создание и ссылки на указатели, используя это определение.

Итак, предположим, что у вас есть функция:

то следующий typedef:

может использоваться для указания на эту функцию doMulitplication . Он просто определяет указатель на функцию, которая возвращает float и принимает два параметра, каждый из которых имеет тип float. Это определение имеет дружественное имя pt2Func . Обратите внимание, что pt2Func может указывать на ЛЮБЮЮ функцию, которая возвращает float и принимает 2 поплавки.

Итак, вы можете создать указатель, который указывает на функцию doMultiplication следующим образом:

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

20 Carl Norum [2009-10-20 01:11:00]

cdecl — отличный инструмент для дешифрования странных синтаксисов, таких как объявления указателей функций. Вы также можете использовать его для генерации.

Что касается советов по сложным декларированиям, которые легче анализировать для будущего обслуживания (самостоятельно или другими), я рекомендую сделать typedef небольших фрагментов и использовать эти небольшие куски в качестве строительных блоков для более крупных и более сложных выражений. Например:

cdecl может помочь вам в этом:

И есть (фактически) именно то, как я породил этот сумасшедший беспорядок выше.

Ссылка на основную публикацию
Adblock
detector