Pointer Variables in C - Stack Overflow
How do function pointers in C work? - Stack Overflow
Dereferencing a char Pointer in C
pointers in C
Videos
Let's break it down:
int a = 10, *p1, *p2; // nothing special
p1 = &a; // p1 now holds the address of a. printf("%d", *p1) would print 10, as it is the current value of a.
// in this point, printf("%d-%d", *p1,a); would print 10-10 (printf("%d",*p2); is UB as p2 is uninitialized)
*p1 = 25; // remember that p1 = &a, meaning that now a = 25. Basically you changed (the variable) a, using a pointer instead of changing it directly.
p2 = p1; // p2 now holds the value of p1, meaning it too points to a
// in this point, printf("%d-%d-%d", *p1,*p2,a); would print 25-25-25
*p2 = 12; // *p2 = 12, and so does *p1, and so does a
// in this point, printf("%d-%d-%d", *p1,*p2,a); would print 12-12-12
printf("%d", *p1);
You should remember that a is an int that holds an integer value, and p1,p2 are int * that hold the address of an int. After p1 = &a, every change to a would mean that *p1 is changed too, since *p1 is actually *(&a) [which is...a]. After p2 = p1, the same holds for p2.
I thought originally that code in general executes from top to bottom.
Well, it does :)
Address values used here is totally arbitrary, just for example
int a = 10, *p1, *p2;
the previous line declare one variable of type int (a) and two pointers to int (p1 and p2)
memory after the previous line
address | memory | variable
1050 | 10 | a
1054 | xxx | p1
1058 | xxx | p2
p1 = &a; // &a is address of a, ie here, 1050
memory after the previous line
address | memory | variable
1050 | 10 | a
1054 | 1050 | p1
1058 | xxx | p2
p1stores "1050";*p1, ie value stored at address stored insidep1, is 10
*p1 = 25; // *p1 means value stored at address stored inside p1
memory after the previous line
address | memory | variable
1050 | 25 | a
1054 | 1050 | p1
1058 | xxx | p2
p1stores "1050";*p1, ie value stored at address stored insidep1, is now 25
p2 = p1;
memory after the previous line
address | memory | variable
1050 | 25 | a
1054 | 1050 | p1
1058 | 1050 | p2
p1stores "1050";*p1, ie value stored at address stored insidep1, is 25- copy values stored inside
p1inp2; sop2points toa, ie stores address "1050"
*p2 = 12;
memory after the previous line
address | memory | variable
1050 | 12 | a
1054 | 1050 | p1
1058 | 1050 | p2
printf("%d", *p1); // Print value stored at address stored inside p1
What we can see here:
p1andp2are pointers: they store address of variable&(like in&a): returns address of a variable*in declaration (like inint *p1): declare pointer to a variable (here to aintvariable)*in expression (like in*p1 = 25): access to value stored at address stored in pointer
You can see different addresses and values :
printf("address of a: %p\n", &a);
printf("address of p1: %p\n", &p1);
printf("address of p2: %p\n", &p2);
// address stored inside p1 (ie value stored inside p1)
printf("address stored inside p1: %p\n", p1);
// address stored inside p2 (ie value stored inside p2)
printf("address stored inside p2: %p\n", p2);
printf("value of a: %d\n", a);
printf("value pointed by p1: %d\n", *p1);
printf("value pointed by p2: %d\n", *p2);
Function pointers in C
Let's start with a basic function which we will be pointing to:
int addInt(int n, int m) {
return n+m;
}
First thing, let's define a pointer to a function which receives 2 ints and returns an int:
int (*functionPtr)(int,int);
Now we can safely point to our function:
functionPtr = &addInt;
Now that we have a pointer to the function, let's use it:
int sum = (*functionPtr)(2, 3); // sum == 5
Passing the pointer to another function is basically the same:
int add2to3(int (*functionPtr)(int, int)) {
return (*functionPtr)(2, 3);
}
We can use function pointers in return values as well (try to keep up, it gets messy):
// this is a function called functionFactory which receives parameter n
// and returns a pointer to another function which receives two ints
// and it returns another int
int (*functionFactory(int n))(int, int) {
printf("Got parameter %d", n);
int (*functionPtr)(int,int) = &addInt;
return functionPtr;
}
But it's much nicer to use a typedef:
typedef int (*myFuncDef)(int, int);
// note that the typedef name is indeed myFuncDef
myFuncDef functionFactory(int n) {
printf("Got parameter %d", n);
myFuncDef functionPtr = &addInt;
return functionPtr;
}
Function pointers in C can be used to perform object-oriented programming in C.
For example, the following lines is written in C:
String s1 = newString();
s1->set(s1, "hello");
Yes, the -> and the lack of a new operator is a dead give away, but it sure seems to imply that we're setting the text of some String class to be "hello".
By using function pointers, it is possible to emulate methods in C.
How is this accomplished?
The String class is actually a struct with a bunch of function pointers which act as a way to simulate methods. The following is a partial declaration of the String class:
typedef struct String_Struct* String;
struct String_Struct
{
char* (*get)(const void* self);
void (*set)(const void* self, char* value);
int (*length)(const void* self);
};
char* getString(const void* self);
void setString(const void* self, char* value);
int lengthString(const void* self);
String newString();
As can be seen, the methods of the String class are actually function pointers to the declared function. In preparing the instance of the String, the newString function is called in order to set up the function pointers to their respective functions:
String newString()
{
String self = (String)malloc(sizeof(struct String_Struct));
self->get = &getString;
self->set = &setString;
self->length = &lengthString;
self->set(self, "");
return self;
}
For example, the getString function that is called by invoking the get method is defined as the following:
char* getString(const void* self_obj)
{
return ((String)self_obj)->internal->value;
}
One thing that can be noticed is that there is no concept of an instance of an object and having methods that are actually a part of an object, so a "self object" must be passed in on each invocation. (And the internal is just a hidden struct which was omitted from the code listing earlier -- it is a way of performing information hiding, but that is not relevant to function pointers.)
So, rather than being able to do s1->set("hello");, one must pass in the object to perform the action on s1->set(s1, "hello").
With that minor explanation having to pass in a reference to yourself out of the way, we'll move to the next part, which is inheritance in C.
Let's say we want to make a subclass of String, say an ImmutableString. In order to make the string immutable, the set method will not be accessible, while maintaining access to get and length, and force the "constructor" to accept a char*:
typedef struct ImmutableString_Struct* ImmutableString;
struct ImmutableString_Struct
{
String base;
char* (*get)(const void* self);
int (*length)(const void* self);
};
ImmutableString newImmutableString(const char* value);
Basically, for all subclasses, the available methods are once again function pointers. This time, the declaration for the set method is not present, therefore, it cannot be called in a ImmutableString.
As for the implementation of the ImmutableString, the only relevant code is the "constructor" function, the newImmutableString:
ImmutableString newImmutableString(const char* value)
{
ImmutableString self = (ImmutableString)malloc(sizeof(struct ImmutableString_Struct));
self->base = newString();
self->get = self->base->get;
self->length = self->base->length;
self->base->set(self->base, (char*)value);
return self;
}
In instantiating the ImmutableString, the function pointers to the get and length methods actually refer to the String.get and String.length method, by going through the base variable which is an internally stored String object.
The use of a function pointer can achieve inheritance of a method from a superclass.
We can further continue to polymorphism in C.
If for example we wanted to change the behavior of the length method to return 0 all the time in the ImmutableString class for some reason, all that would have to be done is to:
- Add a function that is going to serve as the overriding
lengthmethod. - Go to the "constructor" and set the function pointer to the overriding
lengthmethod.
Adding an overriding length method in ImmutableString may be performed by adding an lengthOverrideMethod:
int lengthOverrideMethod(const void* self)
{
return 0;
}
Then, the function pointer for the length method in the constructor is hooked up to the lengthOverrideMethod:
ImmutableString newImmutableString(const char* value)
{
ImmutableString self = (ImmutableString)malloc(sizeof(struct ImmutableString_Struct));
self->base = newString();
self->get = self->base->get;
self->length = &lengthOverrideMethod;
self->base->set(self->base, (char*)value);
return self;
}
Now, rather than having an identical behavior for the length method in ImmutableString class as the String class, now the length method will refer to the behavior defined in the lengthOverrideMethod function.
I must add a disclaimer that I am still learning how to write with an object-oriented programming style in C, so there probably are points that I didn't explain well, or may just be off mark in terms of how best to implement OOP in C. But my purpose was to try to illustrate one of many uses of function pointers.
For more information on how to perform object-oriented programming in C, please refer to the following questions:
- Object-Orientation in C?
- Can you write object oriented code in C?