How Does Dereferencing of a Function Pointer Happen

How does dereferencing of a function pointer happen?

It's not quite the right question. For C, at least, the right question is

What happens to a function value in an rvalue context?

(An rvalue context is anywhere a name or other reference appears where it should be used as a value, rather than a location — basically anywhere except on the left-hand side of an assignment. The name itself comes from the right-hand side of an assignment.)

OK, so what happens to a function value in an rvalue context? It is immediately and implicitly converted to a pointer to the original function value. If you dereference that pointer with *, you get the same function value back again, which is immediately and implicitly converted into a pointer. And you can do this as many times as you like.

Two similar experiments you can try:

  • What happens if you dereference a function pointer in an lvalue context—the left-hand side of an assignment. (The answer will be about what you expect, if you keep in mind that functions are immutable.)

  • An array value is also converted to a pointer in an lvalue context, but it is converted to a pointer to the element type, not to a pointer to the array. Dereferencing it will therefore give you an element, not an array, and the madness you show doesn't occur.

Hope this helps.

P.S. As to why a function value is implicitly converted to a pointer, the answer is that for those of us who use function pointers, it's a great convenience not to have to use &'s everywhere. There's a dual convenience as well: a function pointer in call position is automatically converted to a function value, so you don't have to write * to call through a function pointer.

P.P.S. Unlike C functions, C++ functions can be overloaded, and I'm not qualified to comment on how the semantics works in C++.

How to understand (*****************p)(); in C [duplicate]

This works because of how function designators and function pointers work.

When a function pointer is dereferenced, the result is a function designator. However, in most contexts a function designator is converted to a function pointer.

This behavior is spelled out in section 6.3.2.1p4 of the C standard:

A function designator is an expression that has function type.
Except when it is the operand of the sizeof operator, the _Alignof
operator, or the unary & operator, a function designator with type
‘‘function returning type’’ is converted to an expression that has
type ‘‘pointer to function returning type’’.

What this means is that given function pointer p, *p is a function designator but is then converted back to a function pointer wherever it is used. This means a function pointer can be dereferenced (essentially) an unlimited number of times and yield the same result.

In addition, the function call operator () actually expects a function pointer as its argument. So if you were to execute fun(), fun would first be converted to a function pointer as per the above rule then the function call operator is applied to that function pointer.

Put those together, and that's what this code is demonstrating.

Function Pointer - Automatic Dereferencing [duplicate]

This is just a quirk of C. There's no other reason but the C standard just says that dereferencing or taking the address of a function just evaluates to a pointer to that function, and dereferencing a function pointer just evaluates back to the function pointer.

This behavior is (thus obviously) very different from how the unary & and * operators works for normal variables.

So,

test2 = myprint;
test2 = &myprint;
test2 = *myprint;
test2 = **********myprint;

All just do exactly the same, gives you a function pointer to myprint

Similarly,

test2(s);
(*test2)(s);
(***********test2)(s);

Does the same, call the function pointer stored in test2. Because C says it does.

How come pointer to a function be called without dereferencing?

Your question is confusing. Are you asking what this does:

radius_function_type(&radius)"

This is just a C++ typecast, a bit like:

radius (int (42));

but since radius is already of type radius_function_type then you can just as easily do:

bp::def("radius", radius);

but as this is code generated by Py++, it's probably being extra careful with the output.

Why does Dereferencing a Pointer Variable in my Helper Function causes my entire program to terminate?

void getInput(leaveRecord list[ ], int *n);

Here, In getInput function n is an integer type pointer variable which wants address of integer variable.
But here, getInput(list, recordsRead) you are just sending value of records read.

You have to send address of recordsRead.

getInput(list, &recordsRead)

Also in function printList you are using wrong syntax.
printf(list);
Do this :

printf("%d",list[i]);

or

printf("%d",*(list+i));

dereferencing function pointer to function returning void throws error: void value not ignored as it ought to be

This expression statement

*recurse(2); 

is equivalent to

*( recurse(2) ); 

So as the return type of the function is void then you are trying to dereference the type void.

It seems you mean

( *recurse )(2); 

Or you could just write

recurse(2); 

because the expression *recurse used in the first call will be again implicitly converted to a function pointer.

So though this call for example

( ******recurse )(2); 

is correct nevertheless dereferencing the pointer expressions are redundant.

What does dereferencing a pointer mean?

Reviewing the basic terminology

It's usually good enough - unless you're programming assembly - to envisage a pointer containing a numeric memory address, with 1 referring to the second byte in the process's memory, 2 the third, 3 the fourth and so on....

  • What happened to 0 and the first byte? Well, we'll get to that later - see null pointers below.
  • For a more accurate definition of what pointers store, and how memory and addresses relate, see "More about memory addresses, and why you probably don't need to know" at the end of this answer.

When you want to access the data/value in the memory that the pointer points to - the contents of the address with that numerical index - then you dereference the pointer.

Different computer languages have different notations to tell the compiler or interpreter that you're now interested in the pointed-to object's (current) value - I focus below on C and C++.

A pointer scenario

Consider in C, given a pointer such as p below...

const char* p = "abc";

...four bytes with the numerical values used to encode the letters 'a', 'b', 'c', and a 0 byte to denote the end of the textual data, are stored somewhere in memory and the numerical address of that data is stored in p. This way C encodes text in memory is known as ASCIIZ.

For example, if the string literal happened to be at address 0x1000 and p a 32-bit pointer at 0x2000, the memory content would be:

Memory Address (hex)    Variable name    Contents
1000 'a' == 97 (ASCII)
1001 'b' == 98
1002 'c' == 99
1003 0
...
2000-2003 p 1000 hex

Note that there is no variable name/identifier for address 0x1000, but we can indirectly refer to the string literal using a pointer storing its address: p.

Dereferencing the pointer

To refer to the characters p points to, we dereference p using one of these notations (again, for C):

assert(*p == 'a');  // The first character at address p will be 'a'
assert(p[1] == 'b'); // p[1] actually dereferences a pointer created by adding
// p and 1 times the size of the things to which p points:
// In this case they're char which are 1 byte in C...
assert(*(p + 1) == 'b'); // Another notation for p[1]

You can also move pointers through the pointed-to data, dereferencing them as you go:

++p;  // Increment p so it's now 0x1001
assert(*p == 'b'); // p == 0x1001 which is where the 'b' is...

If you have some data that can be written to, then you can do things like this:

int x = 2;
int* p_x = &x; // Put the address of the x variable into the pointer p_x
*p_x = 4; // Change the memory at the address in p_x to be 4
assert(x == 4); // Check x is now 4

Above, you must have known at compile time that you would need a variable called x, and the code asks the compiler to arrange where it should be stored, ensuring the address will be available via &x.

Dereferencing and accessing a structure data member

In C, if you have a variable that is a pointer to a structure with data members, you can access those members using the -> dereferencing operator:

typedef struct X { int i_; double d_; } X;
X x;
X* p = &x;
p->d_ = 3.14159; // Dereference and access data member x.d_
(*p).d_ *= -1; // Another equivalent notation for accessing x.d_

Multi-byte data types

To use a pointer, a computer program also needs some insight into the type of data that is being pointed at - if that data type needs more than one byte to represent, then the pointer normally points to the lowest-numbered byte in the data.

So, looking at a slightly more complex example:

double sizes[] = { 10.3, 13.4, 11.2, 19.4 };
double* p = sizes;
assert(p[0] == 10.3); // Knows to look at all the bytes in the first double value
assert(p[1] == 13.4); // Actually looks at bytes from address p + 1 * sizeof(double)
// (sizeof(double) is almost always eight bytes)
++p; // Advance p by sizeof(double)
assert(*p == 13.4); // The double at memory beginning at address p has value 13.4
*(p + 2) = 29.8; // Change sizes[3] from 19.4 to 29.8
// Note earlier ++p and + 2 here => sizes[3]

Pointers to dynamically allocated memory

Sometimes you don't know how much memory you'll need until your program is running and sees what data is thrown at it... then you can dynamically allocate memory using malloc. It is common practice to store the address in a pointer...

int* p = (int*)malloc(sizeof(int)); // Get some memory somewhere...
*p = 10; // Dereference the pointer to the memory, then write a value in
fn(*p); // Call a function, passing it the value at address p
(*p) += 3; // Change the value, adding 3 to it
free(p); // Release the memory back to the heap allocation library

In C++, memory allocation is normally done with the new operator, and deallocation with delete:

int* p = new int(10); // Memory for one int with initial value 10
delete p;

p = new int[10]; // Memory for ten ints with unspecified initial value
delete[] p;

p = new int[10](); // Memory for ten ints that are value initialised (to 0)
delete[] p;

See also C++ smart pointers below.

Losing and leaking addresses

Often a pointer may be the only indication of where some data or buffer exists in memory. If ongoing use of that data/buffer is needed, or the ability to call free() or delete to avoid leaking the memory, then the programmer must operate on a copy of the pointer...

const char* p = asprintf("name: %s", name);  // Common but non-Standard printf-on-heap

// Replace non-printable characters with underscores....
for (const char* q = p; *q; ++q)
if (!isprint(*q))
*q = '_';

printf("%s\n", p); // Only q was modified
free(p);

...or carefully orchestrate reversal of any changes...

const size_t n = ...;
p += n;
...
p -= n; // Restore earlier value...
free(p);

C++ smart pointers

In C++, it's best practice to use smart pointer objects to store and manage the pointers, automatically deallocating them when the smart pointers' destructors run. Since C++11 the Standard Library provides two, unique_ptr for when there's a single owner for an allocated object...

{
std::unique_ptr<T> p{new T(42, "meaning")};
call_a_function(p);
// The function above might throw, so delete here is unreliable, but...
} // p's destructor's guaranteed to run "here", calling delete

...and shared_ptr for share ownership (using reference counting)...

{
auto p = std::make_shared<T>(3.14, "pi");
number_storage1.may_add(p); // Might copy p into its container
number_storage2.may_add(p); // Might copy p into its container } // p's destructor will only delete the T if neither may_add copied it

Null pointers

In C, NULL and 0 - and additionally in C++ nullptr - can be used to indicate that a pointer doesn't currently hold the memory address of a variable, and shouldn't be dereferenced or used in pointer arithmetic. For example:

const char* p_filename = NULL; // Or "= 0", or "= nullptr" in C++
int c;
while ((c = getopt(argc, argv, "f:")) != -1)
switch (c) {
case f: p_filename = optarg; break;
}
if (p_filename) // Only NULL converts to false
... // Only get here if -f flag specified

In C and C++, just as inbuilt numeric types don't necessarily default to 0, nor bools to false, pointers are not always set to NULL. All these are set to 0/false/NULL when they're static variables or (C++ only) direct or indirect member variables of static objects or their bases, or undergo zero initialisation (e.g. new T(); and new T(x, y, z); perform zero-initialisation on T's members including pointers, whereas new T; does not).

Further, when you assign 0, NULL and nullptr to a pointer the bits in the pointer are not necessarily all reset: the pointer may not contain "0" at the hardware level, or refer to address 0 in your virtual address space. The compiler is allowed to store something else there if it has reason to, but whatever it does - if you come along and compare the pointer to 0, NULL, nullptr or another pointer that was assigned any of those, the comparison must work as expected. So, below the source code at the compiler level, "NULL" is potentially a bit "magical" in the C and C++ languages...

More about memory addresses, and why you probably don't need to know

More strictly, initialised pointers store a bit-pattern identifying either NULL or a (often virtual) memory address.

The simple case is where this is a numeric offset into the process's entire virtual address space; in more complex cases the pointer may be relative to some specific memory area, which the CPU may select based on CPU "segment" registers or some manner of segment id encoded in the bit-pattern, and/or looking in different places depending on the machine code instructions using the address.

For example, an int* properly initialised to point to an int variable might - after casting to a float* - access memory in "GPU" memory quite distinct from the memory where the int variable is, then once cast to and used as a function pointer it might point into further distinct memory holding machine opcodes for the program (with the numeric value of the int* effectively a random, invalid pointer within these other memory regions).

3GL programming languages like C and C++ tend to hide this complexity, such that:

  • If the compiler gives you a pointer to a variable or function, you can dereference it freely (as long as the variable's not destructed/deallocated meanwhile) and it's the compiler's problem whether e.g. a particular CPU segment register needs to be restored beforehand, or a distinct machine code instruction used

  • If you get a pointer to an element in an array, you can use pointer arithmetic to move anywhere else in the array, or even to form an address one-past-the-end of the array that's legal to compare with other pointers to elements in the array (or that have similarly been moved by pointer arithmetic to the same one-past-the-end value); again in C and C++, it's up to the compiler to ensure this "just works"

  • Specific OS functions, e.g. shared memory mapping, may give you pointers, and they'll "just work" within the range of addresses that makes sense for them

  • Attempts to move legal pointers beyond these boundaries, or to cast arbitrary numbers to pointers, or use pointers cast to unrelated types, typically have undefined behaviour, so should be avoided in higher level libraries and applications, but code for OSes, device drivers, etc. may need to rely on behaviour left undefined by the C or C++ Standard, that is nevertheless well defined by their specific implementation or hardware.

Dereferenced pointer changes address in function call

You need to make sure that :

  • the definition of configData_t is exactly the same in both the function A and function B compilation units
  • the struct padding of configData_t is exactly the same for both the function A and function B compilation units

Red flags of the above for your specific issue would be eg. :

  • sizeof(configData_t) is different
  • offsetof(configData_t, notificationConfig) is different
  • sizeof(notificationConfig_t) is different

If one or more of these red flags are raised (and in a comment, you confirm that), you need to determine which of the two earlier options causes it :

  • a difference in definition can be caught by verifying the source code :

    • make sure the same struct definitions are used throughout the code (typically with the use of an include file)
    • make sure supporting compile time values are the same (eg. array dimensions MAX_INPUT, MAX_OUTPUT, ... in your case)
  • a difference in padding can be caused by the use of different compilers and/or different compiler flags - refer to your compiler's documentation for details (specifically wrt. struct padding/packing)


Related Topics



Leave a reply



Submit