One of the most potentially confusing, but powerful tools in the C programming language is the amount of control and flexibility a developer has over memory using pointers. Here are a few things that I think every aspiring dabbler or beginner should know about pointers and memory at least at a layman's level.
I don't aim for this article to be 100% exhaustive, but to provide a quick reference or supplementary material for those learning pointers in C.
What is memory (RAM) and is it laid out linearly?
Memory in its simplest form is a big array of bytes and pointers point to bytes in memory. Memory can be contiguous or at least appear linear depending on the memory addressing paradigm.
What is a pointer?
Again so a pointer is a data type (or simply a classification of a type of data) that refers to another value stored in memory by using that value's address.
Referencing and de-referencing?
A pointer
For example, think of a book with a marker in it on page 100. The book marker is the pointer that references an address (or location) in the book. The value that the book marker "de-references" is the the information contained on that page.
When to use & or * with pointers
Simple right? you use a & operator when referring to an address in memory and a * operator to retrieve the value stored at that address. Right now I am assuming you are lost, so have a look at this example:
-
int a = 4;
-
int *b = NULL;
-
b = &a;
Int A is declared and has a value of 4, and pointer Int B is declared as NULL. Then b references the address of A.
To print the values:
-
printf(" int A: %d %p\n", a, &a);
-
printf(" int B: %d %p\n", *b, &b);
If you try and print/access a null value, you will get a memory segmentation fault and your application will segfault.
Can I increment a pointer? (simple example of pointer arithmetic)
-
char myString[] = "deadbeef";
-
char *str_ptr = NULL;
-
-
printf("Here is a string: %s\n", myString);
-
-
printf("Now lets increment the pointer by 4\n");
-
str_ptr = (char *)&myString;
-
str_ptr += 4;
-
printf("Here is the string after we have incremented it: %s\n",
-
str_ptr);
The output from the incremented string should be: beef
However, when incrementing pointers, you should always perform bounds checking to verify that your application is not referencing invalid memory. In other words, if you access information either before or after what your aiming to look at, you will get a memory segmentation fault
How do I pass pointers to functions?
Pointers to variables may be passed through functions a few ways, the easiest is like this:
-
struct example_s example_struct = { }; // = {} zeros the structure
-
function_1(&example_struct);
And once a pointer has been passed, you can pass that pointer again like so:
-
void function_1(struct example_s *ex)
-
{
-
// Ignored lines
-
function_2(ex);
-
}
How do I modify pointers that have been passed as parameters in functions?
There are a few older and syntactically correct methods to access and modify a pointer's values. Here is one of the easiest (and cleanest in my opinion):
-
// Setting a pointers attribute
-
strncpy(ex->b, "ARE", strlen("ARE"));
-
-
//If not a pointer
-
strncpy(example_struct.a, "POINTERS", strlen("POINTERS"));
Notice the -> and the .
The rule to modifying is: to modify the value of a variable using pointers in a function, you need to pass the address of that variable. - said someone on the Internet.
What is a ** and so on...?
From time-to-time you will see or need to use a double pointer or **. This is commonly done for special data structures (dynamic multidimensional arrays) and returning pointers from functions.
If you look at multidimensional arrays for example - argv can be shown in two different ways. In this case, a double pointer or ** can be used to represent an array of pointers or two dimensional array of strings.
-
int main( int argc, char **argv)
-
int main( int argc, char *argv[])
Alternatively, if you want to retain the location of a pointer when passed to a function or to allocate memory for a pointer like in this example, you would use it like so:
-
void foo( char ** ptr)
-
{
-
*ptr = malloc(255);
-
strcpy( *ptr, "This is ground control");
-
}
-
int main()
-
{
-
char *ptr = 0;
-
// call function with a pointer to pointer
-
foo( &ptr );
-
printf("%s\n", ptr);
-
free(ptr);
-
return 0;
-
}
If you had used only a since *ptr instead of **ptr when calling function foo, the memory that had been allocated would have been lost and a memory leak would have been created. This is because ptr in this case, has a scope limited to only that of its function and the value of ptr would still be zero inside of main. Your goal is to preserve the memory location of the original variable!
I won't explain this further, but stack overflow has some great explanations and so does this link.
-
// Assuming other code from example
-
int **c = &b;
-
printf("\nOkay so what is ** and what does it mean?\n");
-
printf
-
("The double pointer points to the single pointer which points to variable A\n");
-
printf
-
("You may use the ** to reference the value of the information that is being pointed to\n");
-
printf(" int C: %d %p %p %p\n", **c, c, *c, &c);
What is a function pointer?
Function pointers are pointers that point to functions. They are extremely handy and can be used to create clean/functional APIs and callback functions. Much of their use
-
void our_function(char *str)
-
{
-
if (!str)
-
return;
-
printf("Inside out_function()\n");
-
printf("string: %s\n", str);
-
}
-
-
main() {
-
// Define and call our_function()
-
void (*fp) (char *);
-
fp = &our_function;
-
-
fp(myString);
-
// Alternately can be called using (*fp)(myString);
-
}
Example code is attached to this :) Feel free to make any updates, post comments, ideas or questions. Perhaps at a later date I will add more to the more barren sections.
Blog tags:
Attachment | Size |
---|---|
Pointer example C file | 3.03 KB |
Add new comment