Std:: function.

Class template std::function is a general-purpose polymorphic function wrapper. Instances of std::function can store, copy, and invoke any CopyConstructible Callable target -- functions (via pointers thereto), lambda expressions , bind expressions , or other function objects, as well as pointers to member functions and pointers to data members.

The stored callable object is called the target of std::function . If a std::function contains no target, it is called empty . Invoking the target of an empty std::function results in std::bad_function_call exception being thrown.

std::function satisfies the requirements of CopyConstructible and CopyAssignable .

[ edit ] Member types

[ edit ] member functions, [ edit ] non-member functions, [ edit ] helper classes, [ edit ] deduction guides (since c++17), [ edit ] notes, [ edit ] example.

Possible output:

[ edit ] See also

  • Recent changes
  • Offline version
  • What links here
  • Related changes
  • Upload file
  • Special pages
  • Printable version
  • Permanent link
  • Page information
  • In other languages
  • This page was last modified on 10 September 2023, at 08:07.
  • This page has been accessed 3,407,181 times.
  • Privacy policy
  • About cppreference.com
  • Disclaimers

Powered by MediaWiki

  • Understanding Quick Sort for coding interviews
  • Understanding Insertion Sort for coding interviews
  • Understanding Bubble Sort for coding interviews
  • Understanding selection sort for coding interviews
  • Generate binary numbers using a queue


Top 20 C pointer mistakes and how to fix them

After I graduated college with a BS in Electrical Engineering, I thought that was the last time I was going to program in “C”. I could not have been more wrong. Throughout various points in my career, I’ve encountered and wrangled with a decent amount of “C” code either due to legacy or portability reasons.

Pointers are the most complicated and fundamental part of the C Programming language. Most of the mistakes I’ve made in school assignments and production code is in handling pointers. So here is my attempt to catalog some of the common and not so common mistakes – something I ca refer back to the next time I have to write production code in C. Hope it helps you as well.

Mistake # 1: Omitting the pointer “*” character when declaring multiple pointers in same declaration

Consider the following declaration:

It declares an integer pointer p1 and an integer p2 . More often than not, the intent is to declare two integer pointers.

In the test code below, the last line will result in a compile error “Error C2440 ‘=’: cannot convert from ‘int *’ to ‘int’ ”

This is a pretty basic mistake that most modern compilers will catch.

Recommended Fix:

Use the following declaration to declare two pointers of the same type:

Alternatively, use a typedef – for example,

and then, use this type when declaraing pointers:

Mistake # 2: Using uninitialized pointers

The usage of an uninitialized pointer typically results in program crashes if the pointer accesses memory it is not allowed to.

Consider the code below:

On debug builds in Visual Studio, you’ll first get the following error:

followed by:

0xcc is microsoft’s debug mode marker for uninitialized stack memory.

On release builds, you’ll encounter a runtime crash on the line :printf(“%d”, n);

Recommended Fix: Always initialize pointers to a valid value.

Mistake # 3: Assigning a pointer to an uninitialized variable

This is more dangerous, IMHO, than an uninitialized pointer.In this case, unlike an uninitialized pointer, you won’t get a crash. Instead it can lead to serious logic errors in your code.

On debug builds, it’ll result in a large negative number like “-858993460”. In VC++, the result will be 0 but that is not guaranteed by the C standard .  More specifically item 1652 in the referenced doc states that If an object that has automatic storage duration is not initialized explicitly, its value is indeterminate.

Deceptively simple – do not assign pointers to uninitialized variables.

Mistake # 4: Assigning value to pointer variables

Another one of the novice errors where the IDE/compiler will most likely bail you out. Consider the code:

The problem is that p1 can contain an address of an int and not the int value itself. You’ll get a compiler error:

Assign the address of the integer variable to the pointer .

Mistake # 5: Incorrect syntax for incrementing dereferenced pointer values

If the intent is to increment a variable pointed to by a pointer, the following code fails to achieve that.

In fact, p1 now points to an undefined memory location. When you run this code, you get the following output with the first line corresponding to the value at the address p1 points to.

Recommended Fix: To increment a dereferenced pointer, use : (*p1)++;

Mistake # 6: Trying to deallocate stack memory using free()

Consider the code below where variable m is allocated on the stack.

Attempting to free memory on the stack using the free() function throws an access violation.

Memory on the stack(non-pointer variables) is done implicitly by the system. It is illegal to get memory from the stack and return it to the heap.

Recommended Fix: Use free() to deallocate memory that has been previously allocated by malloc() or one of its variants. Always remember where the memory came from – stack or heap 🙂

Mistake # 7: Dereferncing the value of a pointer after it has been freed

Consider the following code – we allocate an integre pointer, use it , free the memory associated with the pointer and then try to use the pointer again. This’ll end in undefined behavior – maybe crashes depending on the state of the system/platform.

Never use a pointer after it has been freed. A good practice is to set the pointer to NULL after it has been freed such that any attempt to use it again is caught by an access violation.A crash during development is better than undefined behavior after release 🙂

Mistake # 8 : Double free()

Calling free() on a block of memory twice will lead to heap corruption. For example, the following code results in an unhandled exception indicating heap corruption using MS VC++:

This type of issue caused a security vulnerability in zlib which you can read about here .

Do not free the same block of memory twice! Simply assign NULL to a pointer after it has been freed. Subsequent attempts to free a null pointer will be ignored by most heap managers.

Mistake # 9 : Not using sizeof() operator with malloc

If you’re implementing something in C in this day and age, most likely you’re doing it with platform portability in mind. The size of data types can vary across different platform architectures. If you write something like malloc(2), you might have trouble porting it across platforms.

Recommended Fix: Always use sizeof(type) with malloc – for example:

Mistake # 10 : Using a pointer and sizeof() to determine the size of an array

In the code below, sizeof(arr) will correctly determine the size of the char array but a pointer to the array won’t. The type of *cp is const char, which can only have a size of 1, whereas the type of arr is different: array of const char.

Recommended Fix: Never use sizeof on a pointer to an array to determine the size of the array.

Mistake # 11 : Creating garbage objects using C pointers

You need a pointer to a memory location to free / deallocate that memory. If you re-assign a pointer and there is no other pointer pointing to that memory block, you cannot deallocate that previous memory block. This causes a memory leak.

“Memory block 1” is not inaccessible because we don’t have a pointer to it. Without having a pointer to a memory block, we cannot call free() on a block and we’ve created a garbage object in that block – in other words, we leaked memory.

In general, it’s not a good idea to recycle pointer variables. Use new pointer variables where possible and remember to set a pointer variable to NULL right after it has been freed.

Mistake # 12 : Not Understanding the difference between shallow copy and deep copy

Given two pointers p and q, the assignment p = q does not copy the block of memory pointed to by q into a block of memory pointed to by p; instead it assigns memory addresses ( so that both p and q point to the same memory location; changing the value of that memory location affects both pointers).

So what just happened?

In the shallow copy case, af1 and af2 both points to the same memory location. Any change to the memory location via af2 is reflected when af1 is used.

In the deep copy case, when we modify af3 (which points to an entirely different memory block than af1), the memory block pointed by af1 is not affected.

Mistake # 13 : Freeing a memory block shared by two pointers using one of the pointers and subsequently trying to use the other pointer

In the code below,. str1 and str2 points to the same memory block – so when str1 is freed, essentially the memory block pointed to by str2 is freed. Any attempt to use str2 after str1 has been freed will cause undefined behavior. In the case of the program below – it’ll print some garbage value.

There’s really no good way around this in C except to use static analyzers. If you’re in C++, you can use shared_pointers – but use caution as advised in the linked article. . There’s also a good discussion on Stackoverflow on this topic.

Mistake # 14 : Trying to access memory locations not allocated by your code

If you have allocated a block of n objects, do not try to access objects beyond this block ( which includes any objects in locations p+n and beyond)

The statement  doubleVals[SIZE] = 25.99  is essentially writing over memory it does not own – which can cause undefined behavior in programs.

Always be aware of the bounds of memory allocated by your code and operate within those safe limits.

Mistake # 15 : Off by one errors when operating on C pointers

Given a block of memory of SIZE objects pointed to by p, the last object in the block can be retrieved by using another pointer q and setting it to (p+SIZE-1) instead of (p+SIZE).

The first print statement incorrectly prints “0” while the last element is “9”. The second print statement fixes it by accessing the last element at (q + SIZE – 1)

Carefully apply the “off by one error” rules that you learnt for array access to pointers.

Mistake # 16 : Mismatching the type of pointer and type of underlying data

Always use the appropriate pointer type for the data. Consider the code below where a pointer to an integer is assigned to a short:

Notice that it appears that the first hexadecimal digit stored at address 100 is 7 or f, depending on whether it is displayed as an integer or as a short. This apparent contradiction is an artifact of executing this sequence on a little endian machine.If we treat this as a short number and only use the first two bytes, then we get the short value of –1. If we treat this as an integer and use all four bytes, then we get 2,147,483,647.

Always use the correct pointer type for a specific data type – int* for int , double* for double etc.

Mistake # 17 : Comparing two pointers to determine object equality

Often we want to compare if the contents of two objects are same – for example check if two strings are equal.

In the code below, clearly the intent was to check if both strings are “Thunderbird”. But, we ended up comparing the memory addresses with the statement “str1 == str2”. Here str1 and str2 are essentially pointers to different memory addresses which holds the same string.

The code can be made to work as intended, i.e., compare string contents by making the following changes:

Always remember to compare the contents of the memory location pointed to by pointers instead of comparing the address of pointer themselves.

Mistake # 18 : Thinking that C arrays are pointers

While C pointers and Arrays can be used interchangeably in most situations, they are not quite the same. Here’s an example of where it is a recipe for access violation.

In File2.cpp, global_array is declared as an pointer but defined as an array in File1.cpp. At a high level, the compile generates different code for array indexing and access via pointer.

Change the declaration so it does match the definition, like:

Note: A detailed discussion is beyond the scope of this article. The best explanation of this issue I found was in the section, “Chapter 4. The Shocking Truth: C Arrays and Pointers Are NOT the Same!” in Deep C Secrets . It’s a fantastic book if you really want to become an expert C programmer – highly recommended.

Mistake # 19 : Not clearing out sensitive heap data managed through pointers

When an application terminates, most operating systems do not zero out or erase the heap memory that was in use by your application. The memory blocks used by your application can be allocated to another program, which can use the contents of non-zeroed out memory blocks. Just imagine you asked for a security question from the user and stored it in heap memory – it’s always a good idea to erase that memory block contents before returning the memory to the Operating System via free().

Mistake # 20 : Not taking time to understand C function pointers

Functions pointers are used extensively in many large scale production system. It’s also critical to understand more advanced concepts like callbacks, events in Win32 or lambdas in standard C++.

Here’s an example of function pointer in linux kernel:

If code like this makes your head swivel, no sweat – mine did too when i started my career. 🙂

The problem is that most college level C courses seldom does any deep exploration of function pointers, whereas once you’re in industry, it’s all over the place. Here is a good book that has an in-depth treatment of C function pointers : Understanding and Using C Pointers .

Final Thoughts

C is one of the oldest languages in use today. Pointers forms the heart and soul of C. Pointers are not only useful for writing production quality code but also in school for understanding the concepts behind self referential data structures like linked list and binary trees. Even if you are working in a high level language like Java or C#, an object is essentially a pointer. So, study pointers well because they keep showing up in coding interviews and tech screens – I wouldn’t be surprised if you get a question similar to the code snippets in this article and asked “what’s wrong with this piece of C code?”.

Good luck !

  • 35 things I learnt at Game Developer Conference (GDC) 2018
  • System Design Interview Concepts – CAP Theorem

Learn C++

20.1 — Function Pointers

In lesson 12.7 -- Introduction to pointers , you learned that a pointer is a variable that holds the address of another variable. Function pointers are similar, except that instead of pointing to variables, they point to functions!

Consider the following function:

Identifier foo is the function’s name. But what type is the function? Functions have their own l-value function type -- in this case, a function type that returns an integer and takes no parameters. Much like variables, functions live at an assigned address in memory.

When a function is called (via the () operator), execution jumps to the address of the function being called:

At some point in your programming career (if you haven’t already), you’ll probably make a simple mistake:

Instead of calling function foo() and printing the return value, we’ve unintentionally sent function foo directly to std::cout. What happens in this case?

operator<< does not know how to output a function pointer (because there are an infinite number of possible function pointers). The standard says that in this case, foo should be converted to a bool (which operator<< does know how to print). And since the function pointer for foo is a non-void pointer, it should always evaluate to Boolean true . Thus, this should print:

Some compilers (e.g. Visual Studio) have a compiler extension that prints the address of the function instead:

If your platform doesn’t print the function’s address and you want it to, you may be able to force it to do so by converting the function to a void pointer and printing that:

This is implementation-defined behavior, so it may not work on all platforms.

Just like it is possible to declare a non-constant pointer to a normal variable, it’s also possible to declare a non-constant pointer to a function. In the rest of this lesson, we’ll examine these function pointers and their uses. Function pointers are a fairly advanced topic, and the rest of this lesson can be safely skipped or skimmed by those only looking for C++ basics.

Pointers to functions

The syntax for creating a non-const function pointer is one of the ugliest things you will ever see in C++:

In the above snippet, fcnPtr is a pointer to a function that has no parameters and returns an integer. fcnPtr can point to any function that matches this type.

The parentheses around *fcnPtr are necessary for precedence reasons, as int* fcnPtr() would be interpreted as a forward declaration for a function named fcnPtr that takes no parameters and returns a pointer to an integer.

To make a const function pointer, the const goes after the asterisk:

If you put the const before the int, then that would indicate the function being pointed to would return a const int.

The function pointer syntax can be hard to understand. The following articles demonstrates a method for parsing such declarations:

  • https://c-faq.com/decl/spiral.anderson.html
  • https://web.archive.org/web/20110818081319/http://ieng9.ucsd.edu/~cs30x/rt_lt.rule.html

Assigning a function to a function pointer

Function pointers can be initialized with a function (and non-const function pointers can be assigned a function). Like with pointers to variables, we can also use &foo to get a function pointer to foo.

One common mistake is to do this:

This tries to assign the return value from a call to function goo() (which has type int ) to fcnPtr (which is expecting a value of type int(*)() ), which isn’t what we want. We want fcnPtr to be assigned the address of function goo, not the return value from function goo(). So no parentheses are needed.

Note that the type (parameters and return type) of the function pointer must match the type of the function. Here are some examples of this:

Unlike fundamental types, C++ will implicitly convert a function into a function pointer if needed (so you don’t need to use the address-of operator (&) to get the function’s address). However, function pointers will not convert to void pointers, or vice-versa (though some compilers like Visual Studio may allow this anyway).

Function pointers can also be initialized or assigned the value nullptr:

Calling a function using a function pointer

The other primary thing you can do with a function pointer is use it to actually call the function. There are two ways to do this. The first is via explicit dereference:

The second way is via implicit dereference:

As you can see, the implicit dereference method looks just like a normal function call -- which is what you’d expect, since normal function names are pointers to functions anyway! However, some older compilers do not support the implicit dereference method, but all modern compilers should.

One interesting note: Default parameters won’t work for functions called through function pointers. Default parameters are resolved at compile-time (that is, if you don’t supply an argument for a defaulted parameter, the compiler substitutes one in for you when the code is compiled). However, function pointers are resolved at run-time. Consequently, default parameters cannot be resolved when making a function call with a function pointer. You’ll explicitly have to pass in values for any defaulted parameters in this case.

Also note that because function pointers can be set to nullptr, it’s a good idea to assert or conditionally test whether your function pointer is a null pointer before calling it. Just like with normal pointers, dereferencing a null function pointer leads to undefined behavior.

Passing functions as arguments to other functions

One of the most useful things to do with function pointers is pass a function as an argument to another function. Functions used as arguments to another function are sometimes called callback functions .

Consider a case where you are writing a function to perform a task (such as sorting an array), but you want the user to be able to define how a particular part of that task will be performed (such as whether the array is sorted in ascending or descending order). Let’s take a closer look at this problem as applied specifically to sorting, as an example that can be generalized to other similar problems.

Many comparison-based sorting algorithms work on a similar concept: the sorting algorithm iterates through a list of numbers, does comparisons on pairs of numbers, and reorders the numbers based on the results of those comparisons. Consequently, by varying the comparison, we can change the way the algorithm sorts without affecting the rest of the sorting code.

Here is our selection sort routine from a previous lesson:

Let’s replace that comparison with a function to do the comparison. Because our comparison function is going to compare two integers and return a boolean value to indicate whether the elements should be swapped, it will look something like this:

And here’s our selection sort routine using the ascending() function to do the comparison:

Now, in order to let the caller decide how the sorting will be done, instead of using our own hard-coded comparison function, we’ll allow the caller to provide their own sorting function! This is done via a function pointer.

Because the caller’s comparison function is going to compare two integers and return a boolean value, a pointer to such a function would look something like this:

So, we’ll allow the caller to pass our sort routine a pointer to their desired comparison function as the third parameter, and then we’ll use the caller’s function to do the comparison.

Here’s a full example of a selection sort that uses a function pointer parameter to do a user-defined comparison, along with an example of how to call it:

This program produces the result:

Is that cool or what? We’ve given the caller the ability to control how our selection sort does its job.

The caller can even define their own “strange” comparison functions:

The above snippet produces the following result:

As you can see, using a function pointer in this context provides a nice way to allow a caller to “hook” their own functionality into something you’ve previously written and tested, which helps facilitate code reuse! Previously, if you wanted to sort one array in descending order and another in ascending order, you’d need multiple versions of the sort routine. Now you can have one version that can sort any way the caller desires!

Note: If a function parameter is of a function type, it will be converted to a pointer to the function type. This means:

can be equivalently written as:

This only works for function parameters, and so is of somewhat limited use. On a non-function parameter, the latter is interpreted as a forward declaration:

Providing default functions

If you’re going to allow the caller to pass in a function as a parameter, it can often be useful to provide some standard functions for the caller to use for their convenience. For example, in the selection sort example above, providing the ascending() and descending() function along with the selectionSort() function would make the caller’s life easier, as they wouldn’t have to rewrite ascending() or descending() every time they want to use them.

You can even set one of these as a default parameter:

In this case, as long as the user calls selectionSort normally (not through a function pointer), the comparisonFcn parameter will default to ascending. You will need to make sure that the ascending function is declared prior to this point, otherwise the compiler will complain it doesn’t know what ascending is.

Making function pointers prettier with type aliases

Let’s face it -- the syntax for pointers to functions is ugly. However, type aliases can be used to make pointers to functions look more like regular variables:

This defines a type alias called “ValidateFunction” that is a pointer to a function that takes two ints and returns a bool.

Now instead of doing this:

You can do this:

Using std::function

An alternate method of defining and storing function pointers is to use std::function, which is part of the standard library <functional> header. To define a function pointer using this method, declare a std::function object like so:

As you see, both the return type and parameters go inside angled brackets, with the parameters inside parentheses. If there are no parameters, the parentheses can be left empty.

Updating our earlier example with std::function:

Type aliasing std::function can be helpful for readability:

Also note that std::function only allows calling the function via implicit dereference (e.g. fcnPtr() ), not explicit dereference (e.g. (*fcnPtr)() ).

When defining a type alias, we must explicitly specify any template arguments. We can’t use CTAD in this case since there is no initializer to deduce the template arguments from.

Type inference for function pointers

Much like the auto keyword can be used to infer the type of normal variables, the auto keyword can also infer the type of a function pointer.

This works exactly like you’d expect, and the syntax is very clean. The downside is, of course, that all of the details about the function’s parameters types and return type are hidden, so it’s easier to make a mistake when making a call with the function, or using its return value.

Function pointers are useful primarily when you want to store functions in an array (or other structure), or when you need to pass a function to another function. Because the native syntax to declare function pointers is ugly and error prone, we recommend using std::function. In places where a function pointer type is only used once (e.g. a single parameter or return value), std::function can be used directly. In places where a function pointer type is used multiple times, a type alias to a std::function is a better choice (to prevent repeating yourself).

  • In this quiz, we’re going to write a version of our basic calculator using function pointers.

1a) Create a short program asking the user for two integer inputs and a mathematical operation (‘+’, ‘-‘, ‘*’, ‘/’). Ensure the user enters a valid operation.

Show Solution

1b) Write functions named add(), subtract(), multiply(), and divide(). These should take two integer parameters and return an integer.

1c) Create a type alias named ArithmeticFunction for a pointer to a function that takes two integer parameters and returns an integer. Use std::function, and include the appropriate header.

1d) Write a function named getArithmeticFunction() that takes an operator character and returns the appropriate function as a function pointer.

1e) Modify your main() function to call getArithmeticFunction(). Call the return value from that function with your inputs and print the result.

Here’s the full program:


  • Windows Programming
  • UNIX/Linux Programming
  • General C++ Programming
  • Tough function pointer error with assign

  Tough function pointer error with assignment operator

function pointer assignment error

  • C Data Types
  • C Operators
  • C Input and Output
  • C Control Flow
  • C Functions
  • C Preprocessors
  • C File Handling
  • C Cheatsheet
  • C Interview Questions

function pointer assignment error

  • Explore Our Geeks Community
  • Pointer Arithmetics in C with Examples
  • Applications of Pointers in C/C++
  • Passing Pointers to Functions in C
  • C - Pointer to Pointer (Double Pointer)
  • Chain of Pointers in C with Examples

Function Pointer in C

  • How to declare a pointer to a function?
  • Pointer to an Array | Array Pointer
  • Difference between constant pointer, pointers to constant, and constant pointers to constants
  • Pointer vs Array in C
  • NULL Pointer in C
  • Dangling, Void , Null and Wild Pointers
  • Near, Far and Huge Pointers in C
  • restrict keyword in C
  • Discuss(50+)

In C, like normal data pointers (int *, char *, etc), we can have pointers to functions. Following is a simple example that shows declaration and function call using function pointer.

Why do we need an extra bracket around function pointers like fun_ptr in above example? If we remove bracket, then the expression “void (*fun_ptr)(int)” becomes “void *fun_ptr(int)” which is declaration of a function that returns void pointer. See following post for details. How to declare a pointer to a function?

Following are some interesting facts about function pointers.

  1) Unlike normal pointers, a function pointer points to code, not data. Typically a function pointer stores the start of executable code.

2) Unlike normal pointers, we do not allocate de-allocate memory using function pointers.

  3) A function’s name can also be used to get functions’ address. For example, in the below program, we have removed address operator ‘&’ in assignment. We have also changed function call by removing *, the program still works.

  4) Like normal pointers, we can have an array of function pointers. Below example in point 5 shows syntax for array of pointers.

  5) Function pointer can be used in place of switch case. For example, in below program, user is asked for a choice between 0 and 2 to do different tasks.

  6) Like normal data pointers, a function pointer can be passed as an argument and can also be returned from a function. For example, consider the following C program where wrapper() receives a void fun() as parameter and calls the passed function.

This point in particular is very useful in C. In C, we can use function pointers to avoid code redundancy. For example a simple qsort() function can be used to sort arrays in ascending order or descending or by any other order in case of array of structures. Not only this, with function pointers and void pointers, it is possible to use qsort for any data type.

Similar to qsort(), we can write our own functions that can be used for any data type and can do different tasks without code redundancy. Below is an example search function that can be used for any data type. In fact we can use this search function to find close elements (below a threshold) by writing a customized compare function.

The above search function can be used for any data type by writing a separate customized compare().

  7) Many object oriented features in C++ are implemented using function pointers in C. For example virtual functions . Class methods are another example implemented using function pointers. Refer this book for more details.

Related Article: Pointers in C and C++ | Set 1 (Introduction, Arithmetic and Array)

References: http://www.cs.cmu.edu/~ab/15-123S11/AnnotatedNotes/Lecture14.pdf



Please write comments if you find anything incorrect, or you want to share more information about the topic discussed above.

Please Login to comment...

Similar read thumbnail

  • CPP-Functions
  • cpp-pointer

Please write us at contrib[email protected] to report any issue with the above content

Improve your Coding Skills with Practice


Pointers in C Explained – They're Not as Difficult as You Think

Pointers are arguably the most difficult feature of C to understand. But, they are one of the features which make C an excellent language.

In this article, we will go from the very basics of pointers to their usage with arrays, functions, and structure.

So relax, grab a coffee, and get ready to learn all about pointers.

A. Fundamentals

  • What exactly are pointers?
  • Definition and Notation
  • Some Special Pointers
  • Pointer Arithmetic

B. Arrays and Strings

  • Why pointers and arrays?
  • Array of Pointers
  • Pointer to Array

C. Functions

  • Call by Value v/s Call by Reference
  • Pointers as Function Arguments
  • Pointers as Function Return
  • Pointer to Function
  • Array Of Pointers to Functions
  • Pointer to Function as an Argument

D. Structure

  • Pointer to Structure
  • Array of Structure
  • Pointer to Structure as an Argument

E. Pointer to Pointer

F. conclusion, a. definition, notation, types and arithmetic, 1. what exactly are pointers.

Before we get to the definition of pointers, let us understand what happens when we write the following code:

What exactly are pointers?

A block of memory is reserved by the compiler to hold an int value. The name of this block is digit and the value stored in this block is 42 .

Now, to remember the block, it is assigned with an address or a location number (say, 24650).

The value of the location number is not important for us, as it is a random value. But, we can access this address using the & (ampersand) or address of operator.

We can get the value of the variable digit from its address using another operator * (asterisk), called the indirection or dereferencing or value at address operator.

2. Definition and Notation

The address of a variable can be stored in another variable known as a pointer variable. The syntax for storing a variable's address to a pointer is:

For our digit variable, this can be written like this:

or like this:

This can be read as - A pointer to int (integer) addressOfDigit stores the address of(&) digit variable.

Few points to understand:

dataType – We need to tell the computer what the data type of the variable is whose address we are going to store. Here, int was the data type of digit .

It does not mean that addressOfDigit will store a value of type int . An integer pointer (like addressOfDigit ) can only store the address of variables of integer type.

* – A pointer variable is a special variable in the sense that it is used to store an address of another variable. To differentiate it from other variables that do not store an address, we use * as a symbol in the declaration.

Here, we can assign the address of variable1 and variable2 to the integer pointer addressOfVariables but not to variable3 since it is of type char . We will need a character pointer variable to store its address.

We can use our addressOfDigit pointer variable to print the address and the value of digit as below:

Here, *addressOfDigit can  be read as the value at the address stored in addressOfDigit .

Notice we used %d as the format identifier for addressOfDigit . Well, this is not completely correct. The correct identifier would be %p .

Using %p , the address is displayed as a hexadecimal value. But the memory address can be displayed in integers as well as octal values. Still, since it is not an entirely correct way, a warning is shown.

The output according to the compiler I'm using is the following:

This is the warning shown when you use  %d - " warning: format '%d' expects argument of type 'int', but argument 2 has type 'int *' ".

3. Some Special Pointers

The wild pointer.

When we defined our character pointer alphabetAddress , we did not initialize it.

Such pointers are known as wild pointers . They store a garbage value (that is, memory address) of a byte that we don't know is reserved or not (remember int digit = 42; , we reserved a memory address when we declared it).

Suppose we dereference a wild pointer and assign a value to the memory address it is pointing at. This will lead to unexpected behaviour since we will write data at a  memory block that may be free or reserved.

Null Pointer

To make sure that we do not have a wild pointer, we can initialize a pointer with a NULL value, making it a null pointer .

A null pointer points at nothing, or at a memory address that users can not access.

Void Pointer

A void pointer can be used to point at a variable of any data type. It can be reused to point at any data type we want to. It is declared like this:

Since they are very general in nature, they are also known as generic pointers .

With their flexibility, void pointers also bring some constraints. Void pointers cannot be dereferenced as any other pointer. Appropriate typecasting is necessary.

Similarly, void pointers need to be typecasted for performing arithmetic operations.

Void pointers are of great use in C. Library functions malloc() and calloc() which dynamically allocate memory return void pointers. qsort() , an inbuilt sorting function in C, has a function as its argument which itself takes void pointers as its argument.

Dangling Pointer

A dangling pointer points to a memory address which used to hold a variable. Since the address it points at is no longer reserved, using it will lead to unexpected results.

Though the memory has been deallocated by free(ptr) , the pointer to integer ptr still points to that unreserved memory address.

4. Pointer Arithmetic

We know by now that pointers are not like any other variable. They do not store any value but the address of memory blocks.

So it should be quite clear that not all arithmetic operations would be valid with them. Would multiplying or dividing two pointers ( having addresses ) make sense?

Pointers have few but immensely useful valid operations:

  • You can assign the value of one pointer to another only if they are of the same type (unless they're typecasted or one of them is void * ).

2.   You can only add or subtract integers to pointers.

When you add (or subtract) an integer (say n) to a pointer, you are not actually adding (or subtracting) n bytes to the pointer value. You are actually adding (or subtracting) n- times the size of the data type of the variable being pointed bytes.

The value stored in newAddress will not be 103, rather 112 .

3.   Subtraction and comparison of pointers is valid only if both are members of the same array. The subtraction of pointers gives the number of elements separating them.

4.  You can assign or compare a pointer with NULL .

The only exception to the above rules is that the address of the first memory block after the last element of an array follows pointer arithmetic.

Pointer and arrays exist together. These valid manipulations of pointers are immensely useful with arrays, which will be discussed in the next section.

1. Why pointers and arrays?

In C, pointers and arrays have quite a strong relationship.

The reason they should be discussed together is because what you can achieve with array notation ( arrayName[index] ) can also be achieved with pointers, but generally faster.

2. 1-D Arrays

Let us look at what happens when we write int myArray[5]; .

Five consecutive blocks of memory starting from myArray[0] to myArray[4] are created with garbage values in them. Each of the blocks is of size 4 bytes.

Thus, if the address of myArray[0] is 100 (say), the address of the rest of the blocks would be 104 , 108 , 112 , and 116 .

Have a look at the following code:

So, &prime , prime , and &prime[0] all give the same address, right? Well, wait and read because you are in for a surprise (and maybe some confusion).

Let's try to increment each of &prime , prime , and &prime[0] by 1.

Wait! How come &prime + 1 results in something different than the other two? And why are prime + 1 and &prime[0] + 1 still equal? Let's answer these questions.

prime and &prime[0] both point to the 0th element of the array prime . Thus, the name of an array is itself a pointer to the 0th element of the array .

Here, both point to the first element of size 4 bytes. When you add 1 to them, they now point to the 1st element in the array. Therefore this results in an increase in the address by 4.

&prime , on the other hand, is a pointer to an int array of size 5 . It stores the base address of the array prime[5] , which is equal to the address of the first element. However, an increase by 1 to it results in an address with an increase of 5 x 4 = 20 bytes.

In short, arrayName and &arrayName[0] point to the 0th element whereas &arrayName points to the whole array.

We can access the array elements using subscripted variables like this:

We can do the same using pointers which are always faster than using subscripts.

Both methods give the output:

Thus, &arrayName[i] and arrayName[i] are the same as arrayName + i and   *(arrayName + i) , respectively.

3. 2-D Arrays

Two-dimensional arrays are an array of arrays.

Here, marks can be thought of as an array of 5 elements, each of which is a one-dimensional array containing 3 integers. Let us work through a series of programs to understand different subscripted expressions.

Like 1-D arrays, &marks points to the whole 2-D array, marks[5][3] . Thus, incrementing to it by 1 ( = 5 arrays X 3 integers each X 4 bytes = 60) results in an increment by 60 bytes.

If marks was a 1-D array, marks and &marks[0] would have pointed to the 0th element. For a 2-D array, elements are now 1-D arrays . Hence, marks and &marks[0] point to the 0th array (element), and the addition of 1 point to the 1st array.

And now comes the difference. For a 1-D array, marks[0] would give the value of the 0th element. An increment by 1 would increase the value by 1.

But, in a 2-D array, marks[0] points to the 0th element of the 0th array. Similarly, marks[1] points to the 0th element of the 1st array. An increment by 1 would point to the 1st element in the 1st array.

This is the new part. marks[i][j] gives the value of the jth element of the ith array. An increment to it changes the value stored at marks[i][j] . Now, let us try to write marks[i][j] in terms of pointers.

We know marks[i] + j would point to the ith element of the jth array from our previous discussion. Dereferencing it would mean the value at that address. Thus, marks[i][j] is the same as   *(marks[i] + j) .

From our discussion on 1-D arrays, marks[i] is the same as *(marks + i) . Thus, marks[i][j] can be written as *(*(marks + i) + j) in terms of pointers.

Here is a summary of notations comparing 1-D and 2-D arrays.

A string is a one-dimensional array of characters terminated by a null(\0) . When we write char name[] = "Srijan"; , each character occupies one byte of memory with the last one always being \0 .

Similar to the arrays we have seen, name and &name[0] points to the 0th character in the string, while &name points to the whole string. Also, name[i] can be written as *(name + i) .

A two-dimensional array of characters or an array of strings can also be accessed and manipulated as discussed before.

5. Array of Pointers

Like an array of int s and an array of char s, there is an array of pointers as well. Such an array would simply be a collection of addresses. Those addresses could point to individual variables or another array as well.

The syntax for declaring a pointer array is the following:

Following the operators precedence , the first example can be read as -   example1 is an array( [] ) of 5 pointers to int . Similarly, example2 is an array of 8 pointers to char .

We can store the two-dimensional array to string top using a pointer array and save memory as well.

top will contain the base addresses of all the respective names. The base address of "Liverpool" will be stored in top[0] , "Man City" in top[1] , and so on.

In the earlier declaration, we required 90 bytes to store the names. Here, we only require ( 58 (sum of bytes of names) + 12 ( bytes required to store the address in the array) ) 70 bytes.

The manipulation of strings or integers becomes a lot easier when using an array of pointers.

If we try to put "Leicester" ahead of "Chelsea" , we just need to switch the values of top[3] and top[4] like below:

Without pointers, we would have to exchange every character of the strings, which would have taken more time. That's why strings are generally declared using pointers.

6. Pointer to Array

Like "pointer to int " or "pointer to char ", we have pointer to array as well. This pointer points to whole array rather than its elements.

Remember we discussed how &arrayName points to the whole array? Well, it is a pointer to array.

A pointer to array can be declared like this:

Notice the parentheses. Without them, these would be an array of pointers. The first example can be read as - ptr1 is a pointer to an array of 5 int (integers) .

When we dereference a pointer, it gives the value at that address. Similarly, by dereferencing a pointer to array, we get the array and the name of the array points to the base address. We can confirm that *pointerToGoals gives the array goals if we find its size.

If we dereference it again, we will get the value stored in that address. We can print all the elements using pointerToGoals .

Pointers and pointer to arrays are quite useful when paired up with functions. Coming up in the next section!

1. Call by Value vs Call by Reference

Have a look at the program below:

The function multiply() takes two int arguments and returns their product as int .

In the function call multiply(x,y) , we passed the value of x and y ( of main() ), which are actual arguments , to multiply() .

The values of the actual arguments are passed or copied to the formal arguments x and y ( of multiply() ). The x and y of multiply() are different from those of main() . This can be verified by printing their addresses.

Since we created stored values in a new location, it costs us memory. Wouldn't it be better if we could perform the same task without wasting space?

Call by reference helps us achieve this. We pass the address or reference of the variables to the function which does not create a copy. Using the dereferencing operator * , we can access the value stored at those addresses.

We can rewrite the above program using call by reference as well.

2. Pointers as Function Arguments

In this section, we will look at various programs where we give int , char , arrays and strings as arguments using pointers.

We created four functions, add() , subtract() , multiply() and divide() to perform arithmetic operations on the two numbers a and b .

The address of a and b was passed to the functions. Inside the function using * we accessed the values and printed the result.

Similarly, we can give arrays as arguments using a pointer to its first element.

Since the name of an array itself is a pointer to the first element, we send that as an argument to the function greatestOfAll() . In the function, we traverse through the array using loop and pointer.

Here, we pass in the string name to wish() using a pointer and print the message.

3. Pointers as Function Return

The function multiply() takes two pointers to int . It returns a pointer to int as well which stores the address where the product is stored.

It is very easy to think that the output would be 15. But it is not!

When multiply() is called, the execution of main() pauses and memory is now allocated for the execution of multiply() . After its execution is completed, the memory allocated to multiply() is deallocated.

Therefore, though c ( local to main() ) stores the address of the product, the data there is not guaranteed since that memory has been deallocated.

So does that mean pointers cannot be returned by a function? No!

We can do two things. Either store the address in the heap or global section or declare the variable to be static so that their values persist.

Static variables can simply be created by using the keyword static before data type while declaring the variable.

To store addresses in heap, we can use library functions malloc() and calloc() which allocate memory dynamically.

The following programs will explain both the methods. Both methods return the output as 15.

4. Pointer to Function

Like pointer to different data types, we also have a pointer to function as well.

A pointer to function or function pointer stores the address of the function. Though it doesn't point to any data. It points to the first instruction in the function.

The syntax for declaring a pointer to function is:

The below example will make it clearer.

The declaration for the pointer p to function multiply() can be read as ( following operator precedence ) - p is a pointer to function with two int eger pointers ( or two pointers to int ) as parameters and returning a pointer to int .

Since the name of the function is also a pointer to the function, the use of & is not necessary. Also removing * from the function call doesn't affect the program.

5. Array of Pointers to Functions

We have already seen how to create an array of pointers to int , char , and so on. Similarly, we can create an array of pointers to function.

In this array, every element will store an address of a function, where all the functions are of the same type. That is, they have the same type and number of parameters and return types.

We will modify a program discussed earlier in this section. We will store the addresses of add() , subtract() , multiply() and divide() in an array make a function call through subscript.

The declaration here can be read as - p is an array of pointer to functions with two float pointers as parameters and returning void .

6. Pointer to Function as an Argument

Like any other pointer, function pointers can also be passed to another function, therefore known as a callback function or called function . The function to which it is passed is known as a calling function .

A better way to understand would be to look at qsort() , which is an inbuilt function in C. It is used to sort an array of integers, strings, structures, and so on. The declaration for qsort() is:

qsort() takes four arguments:

  • a void pointer to the start of an array
  • number of elements
  • size of each element
  • a function pointer that takes in two void pointers as arguments and returns an int

The function pointer points to a comparison function that returns an integer that is greater than, equal to, or less than zero if the first argument is respectively greater than, equal to, or less than the second argument.

The following program showcases its usage:

Since a function name is itself a pointer, we can write compareIntegers as the fourth argument.

1. Pointer to Structure

Like integer pointers, array pointers, and function pointers, we have pointer to structures or structure pointers as well.

Here, we have declared a pointer ptrStudent of type struct records . We have assigned the address of student to ptrStudent .

ptrStudent stores the base address of student , which is the base address of the first member of the structure. Incrementing by 1 would increase the address by sizeof(student) bytes.

We can access the members of student using ptrStudent in two ways. Using our old friend * or using -> ( infix or arrow operator ).

With * , we will continue to use the . ( dot operator) whereas with -> we won't need the dot operator.

Similarly, we can access and modify other members as well. Note that the brackets are necessary while using * since the dot operator( . ) has higher precedence over * .

2. Array Of Structure

We can create an array of type struct records and use a pointer to access the elements and their members.

Note that ptrStudent1 is a pointer to student[0] whereas ptrStudent2 is a pointer to the whole array of  10 struct records . Adding 1 to ptrStudent1 would point to student[1] .

We can use ptrStudent1 with a loop to traverse through the elements and their members.

3. Pointer to Structure as an Argument

We can also pass the address of a structure variable to a function.

Note that the structure struct records is declared outside main() . This is to ensure that it is available globally and printRecords() can use it.

If the structure is defined inside main() , its scope will be limited to main() . Also structure must be declared before the function declaration as well.

Like structures, we can have pointers to unions and can access members using the arrow operator ( -> ).

So far we have looked at pointer to various primitive data types, arrays, strings, functions, structures, and unions.

The automatic question that comes to the mind is – what about pointer to pointer?

Well, good news for you! They too exist.

To store the address of int variable var , we have the pointer to int ptr_var . We would need another pointer to store the address of ptr_var .

Since ptr_var is of type int * , to store its address we would have to create a pointer to int * . The code below shows how this can be done.

We can use ptr_ptrvar to access the address of ptr_var and use double dereferencing to access var.

It is not required to use brackets when dereferencing ptr_ptrvar . But it is a good practice to use them. We can create another pointer ptr_ptrptrvar , which will store the address of ptr_ptrvar .

Since ptr_ptrvar is of type int** , the declaration for ptr_ptrptrvar will be

We can again access ptr_ptrvar , ptr_var and var using ptr_ptrptrvar .

If we change the value at any of the pointer(s) using ptr_ptrptrvar or ptr_ptrvar , the pointer(s) will stop pointing to the variable.

Phew! Yeah, we're finished. We started from pointers and ended with pointers (in a way). Don't they say that the curve of learning is a circle!

Try to recap all the sub-topics that you read. If you can recollect them, well done! Read the ones you can't remember again.

This article is done, but you shouldn't be done with pointers. Play with them. Next, you can look into Dynamic Memory Allocation to get to know pointers better .

Stay home, stay safe.

Sachin. Cricket. Dhoni. De Villiers. Buttler. In that order.

If you read this far, thank the author to show them you care. Say Thanks

Learn to code for free. freeCodeCamp's open source curriculum has helped more than 40,000 people get jobs as developers. Get started

Assignment #

Value assignment has the following syntax: (const|var) identifier[: type] = value .

  • const indicates that identifier is a constant that stores an immutable value.
  • var indicates that identifier is a variable that stores a mutable value.
  • : type is a type annotation for identifier , and may be omitted if the data type of value can be inferred.

Constants and variables must have a value. If no known value can be given, the undefined value, which coerces to any type, may be used as long as a type annotation is provided.

Where possible, const values are preferred over var values.

Arrays are denoted by [N]T , where N is the number of elements in the array and T is the type of those elements (i.e., the array’s child type).

For array literals, N may be replaced by _ to infer the size of the array.

To get the size of an array, simply access the array’s len field.

Zig’s if statements only accept bool values (i.e. true or false ). There is no concept of truthy or falsy values.

Here, we will introduce testing. Save the below code and compile + run it with zig test file-name.zig . We will be using the expect function from the standard library, which will cause the test to fail if it’s given the value false . When a test fails, the error and stack trace will be shown.

If statements also work as expressions.

Zig’s while loop has three parts - a condition, a block and a continue expression.

Without a continue expression.

With a continue expression.

With a continue .

With a break .

For loops are used to iterate over arrays (and other types, to be discussed later). For loops follow this syntax. Like while, for loops can use break and continue . Here, we’ve had to assign values to _ , as Zig does not allow us to have unused values.

Functions #

All function arguments are immutable - if a copy is desired the user must explicitly make one. Unlike variables, which are snake_case, functions are camelCase. Here’s an example of declaring and calling a simple function.

Recursion is allowed:

When recursion happens, the compiler is no longer able to work out the maximum stack size, which may result in unsafe behaviour - a stack overflow. Details on how to achieve safe recursion will be covered in future.

Values can be ignored using _ instead of a variable or const declaration. This does not work at the global scope (i.e. it only works inside functions and blocks) and is useful for ignoring the values returned from functions if you do not need them.

Defer is used to execute a statement while exiting the current block.

When there are multiple defers in a single block, they are executed in reverse order.

An error set is like an enum (details on Zig’s enums later), where each error in the set is a value. There are no exceptions in Zig; errors are values. Let’s create an error set.

Error sets coerce to their supersets.

An error set type and another type can be combined with the ! operator to form an error union type. Values of these types may be an error value or a value of the other type.

Let’s create a value of an error union type. Here catch is used, which is followed by an expression which is evaluated when the value before it is an error. The catch here is used to provide a fallback value, but could instead be a noreturn - the type of return , while (true) and others.

Functions often return error unions. Here’s one using a catch, where the |err| syntax receives the value of the error. This is called payload capturing , and is used similarly in many places. We’ll talk about it in more detail later in the chapter. Side note: some languages use similar syntax for lambdas - this is not true for Zig.

try x is a shortcut for x catch |err| return err , and is commonly used where handling an error isn’t appropriate. Zig’s try and catch are unrelated to try-catch in other languages.

errdefer works like defer , but only executing when the function is returned from with an error inside of the errdefer ’s block.

Error unions returned from a function can have their error sets inferred by not having an explicit error set. This inferred error set contains all possible errors that the function may return.

Error sets can be merged.

anyerror is the global error set, which due to being the superset of all error sets, can have an error from any set coerced to it. Its usage should be generally avoided.

Zig’s switch works as both a statement and an expression. The types of all branches must coerce to the type which is being switched upon. All possible values must have an associated branch - values cannot be left out. Cases cannot fall through to other branches.

An example of a switch statement. The else is required to satisfy the exhaustiveness of this switch.

Here is the former, but as a switch expression.

Runtime Safety #

Zig provides a level of safety, where problems may be found during execution. Safety can be left on, or turned off. Zig has many cases of so-called detectable illegal behaviour , meaning that illegal behaviour will be caught (causing a panic) with safety on, but will result in undefined behaviour with safety off. Users are strongly recommended to develop and test their software with safety on, despite its speed penalties.

For example, runtime safety protects you from out of bounds indices.

The user may disable runtime safety for the current block using the built-in function @setRuntimeSafety .

Safety is off for some build modes (to be discussed later).

Unreachable #

unreachable is an assertion to the compiler that this statement will not be reached. It can tell the compiler that a branch is impossible, which the optimiser can then take advantage of. Reaching an unreachable is detectable illegal behaviour.

As it is of the type noreturn , it is compatible with all other types. Here it coerces to u32.

Here is an unreachable being used in a switch.

Normal pointers in Zig cannot have 0 or null as a value. They follow the syntax *T , where T is the child type.

Referencing is done with &variable , and dereferencing is done with variable.* .

Trying to set a *T to the value 0 is detectable illegal behaviour.

Zig also has const pointers, which cannot be used to modify the referenced data. Referencing a const variable will yield a const pointer.

A *T coerces to a *const T .

Pointer sized integers #

usize and isize are given as unsigned and signed integers which are the same size as pointers.

Many-Item Pointers #

Sometimes, you may have a pointer to an unknown amount of elements. [*]T is the solution for this, which works like *T but also supports indexing syntax, pointer arithmetic, and slicing. Unlike *T , it cannot point to a type which does not have a known size. *T coerces to [*]T .

These many pointers may point to any amount of elements, including 0 and 1.

Slices can be thought of as a pair of [*]T (the pointer to the data) and a usize (the element count). Their syntax is []T , with T being the child type. Slices are used heavily throughout Zig for when you need to operate on arbitrary amounts of data. Slices have the same attributes as pointers, meaning that there also exists const slices. For loops also operate over slices. String literals in Zig coerce to []const u8 .

Here, the syntax x[n..m] is used to create a slice from an array. This is called slicing , and creates a slice of the elements starting at x[n] and ending at x[m - 1] . This example uses a const slice, as the values to which the slice points need not be modified.

When these n and m values are both known at compile time, slicing will actually produce a pointer to an array. This is not an issue as a pointer to an array i.e. *[N]T will coerce to a []T .

The syntax x[n..] can also be used when you want to slice to the end.

Types that may be sliced are arrays, many pointers and slices.

Zig’s enums allow you to define types with a restricted set of named values.

Let’s declare an enum.

Enums types may have specified (integer) tag types.

Enum’s ordinal values start at 0. They can be accessed with the built-in function @enumToInt .

Values can be overridden, with the next values continuing from there.

Methods can be given to enums. These act as namespaced functions that can be called with dot syntax.

Enums can also be given var and const declarations. These act as namespaced globals, and their values are unrelated and unattached to instances of the enum type.

Structs are Zig’s most common kind of composite data type, allowing you to define types that can store a fixed set of named fields. Zig gives no guarantees about the in-memory order of fields in a struct or its size. Like arrays, structs are also neatly constructed with T{} syntax. Here is an example of declaring and filling a struct.

All fields must be given a value.

Fields may be given defaults:

Like enums, structs may also contain functions and declarations.

Structs have the unique property that when given a pointer to a struct, one level of dereferencing is done automatically when accessing fields. Notice how, in this example, self.x and self.y are accessed in the swap function without needing to dereference the self pointer.

Zig’s unions allow you to define types which store one value of many possible typed fields; only one field may be active at one time.

Bare union types do not have a guaranteed memory layout. Because of this, bare unions cannot be used to reinterpret memory. Accessing a field in a union which is not active is detectable illegal behaviour.

Tagged unions are unions which use an enum to detect which field is active. Here we make use of payload capturing again, to switch on the tag type of a union while also capturing the value it contains. Here we use a pointer capture ; captured values are immutable, but with the |*value| syntax, we can capture a pointer to the values instead of the values themselves. This allows us to use dereferencing to mutate the original value.

The tag type of a tagged union can also be inferred. This is equivalent to the Tagged type above.

void member types can have their type omitted from the syntax. Here, none is of type void .

Integer Rules #

Zig supports hex, octal and binary integer literals.

Underscores may also be placed between digits as a visual separator.

“Integer Widening” is allowed, which means that integers of a type may coerce to an integer of another type, providing that the new type can fit all of the values that the old type can.

If you have a value stored in an integer that cannot coerce to the type that you want, @intCast may be used to explicitly convert from one type to the other. If the value given is out of the range of the destination type, this is detectable illegal behaviour.

Integers, by default, are not allowed to overflow. Overflows are detectable illegal behaviour. Sometimes, being able to overflow integers in a well-defined manner is a wanted behaviour. For this use case, Zig provides overflow operators.

Zig’s floats are strictly IEEE compliant unless @setFloatMode(.Optimized) is used, which is equivalent to GCC’s -ffast-math . Floats coerce to larger float types.

Floats support multiple kinds of literal.

Underscores may also be placed between digits.

Integers and floats may be converted using the built-in functions @floatFromInt and @intFromFloat . @floatFromInt is always safe, whereas @intFromFloat is detectable illegal behaviour if the float value cannot fit in the integer destination type.

Labelled Blocks #

Blocks in Zig are expressions and can be given labels, which are used to yield values. Here, we are using a label called blk . Blocks yield values, meaning they can be used in place of a value. The value of an empty block {} is a value of the type void .

This can be seen as being equivalent to C’s i++ .

Labelled Loops #

Loops can be given labels, allowing you to break and continue to outer loops.

Loops as expressions #

Like return , break accepts a value. This can be used to yield a value from a loop. Loops in Zig also have an else branch, which is evaluated when the loop is not exited with a break .

Optionals #

Optionals use the syntax ?T and are used to store the data null , or a value of type T .

Optionals support the orelse expression, which acts when the optional is null . This unwraps the optional to its child type.

.? is a shorthand for orelse unreachable . This is used for when you know it is impossible for an optional value to be null, and using this to unwrap a null value is detectable illegal behaviour.

Payload capturing works in many places for optionals, meaning that in the event that it is non-null, we can “capture” its non-null value.

Here we use an if optional payload capture; a and b are equivalent here. if (b) |value| captures the value of b (in the cases where b is not null), and makes it available as value . As in the union example, the captured value is immutable, but we can still use a pointer capture to modify the value stored in b .

And with while :

Optional pointer and optional slice types do not take up any extra memory compared to non-optional ones. This is because internally they use the 0 value of the pointer for null .

This is how null pointers in Zig work - they must be unwrapped to a non-optional before dereferencing, which stops null pointer dereferences from happening accidentally.

Blocks of code may be forcibly executed at compile time using the comptime keyword. In this example, the variables x and y are equivalent.

Integer literals are of the type comptime_int . These are special in that they have no size (they cannot be used at runtime!), and they have arbitrary precision. comptime_int values coerce to any integer type that can hold them. They also coerce to floats. Character literals are of this type.

comptime_float is also available, which internally is an f128 . These cannot be coerced to integers, even if they hold an integer value.

Types in Zig are values of the type type . These are available at compile time. We have previously encountered them by checking @TypeOf and comparing with other types, but we can do more.

Function parameters in Zig can be tagged as being comptime . This means that the value passed to that function parameter must be known at compile time. Let’s make a function that returns a type. Notice how this function is PascalCase, as it returns a type.

We can reflect upon types using the built-in @typeInfo , which takes in a type and returns a tagged union. This tagged union type can be found in std.builtin.TypeInfo (info on how to make use of imports and std later).

We can use the @Type function to create a type from a @typeInfo . @Type is implemented for most types but is notably unimplemented for enums, unions, functions, and structs.

Here anonymous struct syntax is used with .{} , because the T in T{} can be inferred. Anonymous structs will be covered in detail later. In this example we will get a compile error if the Int tag isn’t set.

Returning a struct type is how you make generic data structures in Zig. The usage of @This is required here, which gets the type of the innermost struct, union, or enum. Here std.mem.eql is also used which compares two slices.

The types of function parameters can also be inferred by using anytype in place of a type. @TypeOf can then be used on the parameter.

Comptime also introduces the operators ++ and ** for concatenating and repeating arrays and slices. These operators do not work at runtime.

Payload Captures #

Payload captures use the syntax |value| and appear in many places, some of which we’ve seen already. Wherever they appear, they are used to “capture” the value from something.

With if statements and optionals.

With if statements and error unions. The else with the error capture is required here.

With while loops and optionals. This may have an else block.

With while loops and error unions. The else with the error capture is required here.

Switch cases on tagged unions.

As we saw in the Union and Optional sections above, values captured with the |val| syntax are immutable (similar to function arguments), but we can use pointer capture to modify the original values. This captures the values as pointers that are themselves still immutable, but because the value is now a pointer, we can modify the original value by dereferencing it:

Inline Loops #

inline loops are unrolled, and allow some things to happen that only work at compile time. Here we use a for , but a while works similarly.

Using these for performance reasons is inadvisable unless you’ve tested that explicitly unrolling is faster; the compiler tends to make better decisions here than you.

opaque types in Zig have an unknown (albeit non-zero) size and alignment. Because of this these data types cannot be stored directly. These are used to maintain type safety with pointers to types that we don’t have information about.

Opaque types may have declarations in their definitions (the same as structs, enums and unions).

The typical usecase of opaque is to maintain type safety when interoperating with C code that does not expose complete type information.

Anonymous Structs #

The struct type may be omitted from a struct literal. These literals may coerce to other struct types.

Anonymous structs may be completely anonymous i.e. without being coerced to another struct type.

Anonymous structs without field names may be created and are referred to as tuples . These have many of the properties that arrays do; tuples can be iterated over, indexed, can be used with the ++ and ** operators, and have a len field. Internally, these have numbered field names starting at "0" , which may be accessed with the special syntax @"0" which acts as an escape for the syntax - things inside @"" are always recognised as identifiers.

An inline loop must be used to iterate over the tuple here, as the type of each tuple field may differ.

Sentinel Termination #

Arrays, slices and many pointers may be terminated by a value of their child type. This is known as sentinel termination. These follow the syntax [N:t]T , [:t]T , and [*:t]T , where t is a value of the child type T .

An example of a sentinel terminated array. The built-in @ptrCast is used to perform an unsafe type conversion. This shows us that the last element of the array is followed by a 0 byte.

The types of string literals is *const [N:0]u8 , where N is the length of the string. This allows string literals to coerce to sentinel terminated slices, and sentinel terminated many pointers. Note: string literals are UTF-8 encoded.

[*:0]u8 and [*:0]const u8 perfectly model C’s strings.

Sentinel terminated types coerce to their non-sentinel-terminated counterparts.

Sentinel terminated slicing is provided which can be used to create a sentinel terminated slice with the syntax x[n..m:t] , where t is the terminator value. Doing this is an assertion from the programmer that the memory is terminated where it should be - getting this wrong is detectable illegal behaviour.

Zig provides vector types for SIMD. These are not to be conflated with vectors in a mathematical sense, or vectors like C++’s std::vector (for this, see “Arraylist” in chapter 2). Vectors may be created using the @Type built-in we used earlier, and std.meta.Vector provides a shorthand for this.

Vectors can only have child types of booleans, integers, floats and pointers.

Operations between vectors with the same child type and length can take place. These operations are performed on each of the values in the vector. std.meta.eql is used here to check for equality between two vectors (also useful for other types like structs).

Vectors are indexable.

The built-in function @splat may be used to construct a vector where all of the values are the same. Here we use it to multiply a vector by a scalar.

Vectors do not have a len field like arrays, but may still be looped over.

Vectors coerce to their respective arrays.

It is worth noting that using explicit vectors may result in slower software if you do not make the right decisions - the compiler’s auto-vectorisation is fairly smart as-is.

The built-in function @import takes in a file, and gives you a struct type based on that file. All declarations labelled as pub (for public) will end up in this struct type, ready for use.

@import("std") is a special case in the compiler, and gives you access to the standard library. Other @import s will take in a file path, or a package name (more on packages in a later chapter).

We will explore more of the standard library in later chapters.

End Of Chapter 1 #

In the next chapter we will cover standard patterns, including many useful areas of the standard library.

Feedback and PRs are welcome.


  1. Function pointer in c, a detail guide

    function pointer assignment error

  2. Pointer Expressions in C with Examples

    function pointer assignment error

  3. Solved Pointer arithmetic and expressions Pointer assignment

    function pointer assignment error

  4. Resolving function pointers with static analysis

    function pointer assignment error

  5. Unexpected C function pointer definition syntax error in struct

    function pointer assignment error

  6. Function Pointers in C

    function pointer assignment error


  1. 0x0F. C

  2. Session 1

  3. MODULE 5

  4. important questions on pointers

  5. Extra Problems 1 Pointers double pointers tracing inside function

  6. array with pointer passing array to function passing by value vs refernce vs pointer


  1. c++

    Jan 10, 2011 at 10:20 It's the error message that's wrong. Which version of g++ are you using? With mine (4.4) I get: "error: assignment of function 'int (* foo (bool)) (int)'" and "error: cannot convert 'int (* (*) (bool)) (int)' to 'int (* (bool)) (int) in assignment" which I think is correct.

  2. Why cant i assign a function to function pointer?

    Why cant i assign a function to function pointer? Ask Question Asked 4 years ago Modified 4 years ago Viewed 702 times 3 While doing my homework i decided to use function pointer as a callback function. I am not going to post the whole homework here as it is too large. Here i wrote small test code to illustrate the error i am getting.

  3. c

    2 Answers Sorted by: 9 The following: int (*test_ptr) (); takes an unspecified number of parameters, not zero parameters. For the latter, write int (*test_ptr) (void); P.S. Calling a two-argument function with zero arguments leads to undefined behaviour. Share Follow answered Mar 27, 2013 at 8:09

  4. Function Pointers Error In C

    int (*p)() = fun declares a pointer to a function that returns int, then assigns the address of the function fun to that pointer.(*p)() calls whatever function p is pointing to. Problems with your code: fun and main should have a return type. Your compiler might not demand it and assume they return ints, but you should give them one nonetheless.. You need to declare fun above main, or use a ...

  5. Function pointer assignment cause error after creating DLL

    I written a class for my application. in this class there is static function pointer that used for communication over network socket. at startup, i assign this function pointer to a function outside of the class and work fine without any problem. now i created DLL from my class, but when i assign this pointer to my function, i got "unresolved ex...

  6. c

    A NULL pointer assignment is a runtime error It occurs due to various reasons one is that your program has tried to access an illegal memory location. Illegal location means either the location is in the operating systems address space or in the other processes memory space.

  7. C Pointer Assignment in a function

    Inside test the variable pt1 is a discrete pointer in its own right. That is to say it is not merely an alias for p1, but rather a copy that exists only for the lifetime of the call.. Thus whatever assignment you make to it is only exits for the duration of that call and is not propagated outside of it.

  8. Pointer declaration

    C++ language Declarations Declares a variable of a pointer or pointer-to-member type. Syntax A pointer declaration is any simple declaration whose declarator has the form 1) Pointer declarator: the declaration S* D; declares D as a pointer to the type determined by decl-specifier-seq S.

  9. Pointer declaration

    Pointer is a type of an object that refers to a function or an object of another type, possibly adding qualifiers. Pointer may also refer to nothing, which is indicated by the special null pointer value. The attr-spec-seq(C23) is an optional list of attributes, applied to the declared pointer.

  10. std::function

    Instances of std::function can store, copy, and invoke any CopyConstructibleCallabletarget -- functions (via pointers thereto), lambda expressions, bind expressions, or other function objects, as well as pointers to member functions and pointers to data members. The stored callable object is called the target of std::function.

  11. Documentation

    QUESTION I wish to assign a function to a function pointer: void foo (void); void (*func_ptr) (void); func_ptr = foo; void foo ( void ) { ; } void main (void) { func_ptr (); } but my code generates an error stating (like this from C166, other compilers will give similar errors): error C53: redefinition of 'func_ptr': different basic types

  12. Top 20 C pointer mistakes and how to fix them

    Mistake # 1: Omitting the pointer "*" character when declaring multiple pointers in same declaration Consider the following declaration: int* p1, p2; It declares an integer pointer p1 and an integer p2. More often than not, the intent is to declare two integer pointers.

  13. 20.1

    1 Tip Some compilers (e.g. Visual Studio) have a compiler extension that prints the address of the function instead: 0x002717f0 If your platform doesn't print the function's address and you want it to, you may be able to force it to do so by converting the function to a void pointer and printing that:

  14. 12.7: Function Pointer in C

    Following are some interesting facts about function pointers. 1) Unlike normal pointers, a function pointer points to code, not data. Typically a function pointer stores the start of executable code. 2) Unlike normal pointers, we do not allocate de-allocate memory using function pointers. 3) A function's name can also be used to get functions ...

  15. Tough function pointer error with assign

    Professor said this about the error: "You can't assign anything other than a primitive data type (and string using the string library) with the = statement." So I changed the function to the below. Now I get a gpf.

  16. Function Pointer in C

    Following are some interesting facts about function pointers. 1) Unlike normal pointers, a function pointer points to code, not data. Typically a function pointer stores the start of executable code. 2) Unlike normal pointers, we do not allocate de-allocate memory using function pointers.

  17. What is wrong with a function pointer?

    If the pointer is valid, everything works fine. If the pointer is not assigned, then it does not work, unless I comment out the line: v = radio->read_interrupt(); but I have checked with debugger, that the null pointer protection works and the pointer never gets called. By "does not work" I mean the function.

  18. Solved In C, what's wrong with this function when gcc

    Engineering. Computer Science. Computer Science questions and answers. In C, what's wrong with this function when gcc compiler saying "error: assignment from incompatible pointer type [-Werror=incompatible-pointer-types] p = p->next; "?

  19. Null Pointer Assignment Errors Explained

    The Null Pointer Assignment error is generated in programs that corrupt the bottom of the data segment in such a fashion as to indicate a high probability that an improperly-initialized pointer has been used. The error can only be generated in the small and medium memory models. 2. What causes a Null Pointer Assignment error?

  20. Pointers in C Explained

    4. Pointer to Function. Like pointer to different data types, we also have a pointer to function as well. A pointer to function or function pointer stores the address of the function. Though it doesn't point to any data. It points to the first instruction in the function. The syntax for declaring a pointer to function is:

  21. Chapter 1

    Here, we will introduce testing. Save the below code and compile + run it with zig test file-name.zig. We will be using the expect function from the standard library, which will cause the test to fail if it's given the value false. When a test fails, the error and stack trace will be shown.