Null Pointer Assignment C++

Pointers

In earlier chapters, variables have been explained as locations in the computer's memory which can be accessed by their identifier (their name). This way, the program does not need to care about the physical address of the data in memory; it simply uses the identifier whenever it needs to refer to the variable.

For a C++ program, the memory of a computer is like a succession of memory cells, each one byte in size, and each with a unique address. These single-byte memory cells are ordered in a way that allows data representations larger than one byte to occupy memory cells that have consecutive addresses.

This way, each cell can be easily located in the memory by means of its unique address. For example, the memory cell with the address always follows immediately after the cell with address and precedes the one with , and is exactly one thousand cells after and exactly one thousand cells before .

When a variable is declared, the memory needed to store its value is assigned a specific location in memory (its memory address). Generally, C++ programs do not actively decide the exact memory addresses where its variables are stored. Fortunately, that task is left to the environment where the program is run - generally, an operating system that decides the particular memory locations on runtime. However, it may be useful for a program to be able to obtain the address of a variable during runtime in order to access data cells that are at a certain position relative to it.

Address-of operator (&)

The address of a variable can be obtained by preceding the name of a variable with an ampersand sign (), known as address-of operator. For example:



This would assign the address of variable to ; by preceding the name of the variable with the address-of operator (), we are no longer assigning the content of the variable itself to , but its address.

The actual address of a variable in memory cannot be known before runtime, but let's assume, in order to help clarify some concepts, that is placed during runtime in the memory address .

In this case, consider the following code fragment:



The values contained in each variable after the execution of this are shown in the following diagram:



First, we have assigned the value to (a variable whose address in memory we assumed to be ).

The second statement assigns the address of , which we have assumed to be .

Finally, the third statement, assigns the value contained in to . This is a standard assignment operation, as already done many times in earlier chapters.

The main difference between the second and third statements is the appearance of the address-of operator ().

The variable that stores the address of another variable (like in the previous example) is what in C++ is called a pointer. Pointers are a very powerful feature of the language that has many uses in lower level programming. A bit later, we will see how to declare and use pointers.

Dereference operator (*)

As just seen, a variable which stores the address of another variable is called a pointer. Pointers are said to "point to" the variable whose address they store.

An interesting property of pointers is that they can be used to access the variable they point to directly. This is done by preceding the pointer name with the dereference operator (). The operator itself can be read as "value pointed to by".

Therefore, following with the values of the previous example, the following statement:



This could be read as: " equal to value pointed to by ", and the statement would actually assign the value to , since is , and the value pointed to by (following the example above) would be .


It is important to clearly differentiate that refers to the value , while (with an asterisk preceding the identifier) refers to the value stored at address , which in this case is . Notice the difference of including or not including the dereference operator (I have added an explanatory comment of how each of these two expressions could be read):



The reference and dereference operators are thus complementary:
  • is the address-of operator, and can be read simply as "address of"
  • is the dereference operator, and can be read as "value pointed to by"

Thus, they have sort of opposite meanings: An address obtained with can be dereferenced with .

Earlier, we performed the following two assignment operations:



Right after these two statements, all of the following expressions would give true as result:



The first expression is quite clear, considering that the assignment operation performed on was . The second one uses the address-of operator (), which returns the address of , which we assumed it to have a value of . The third one is somewhat obvious, since the second expression was true and the assignment operation performed on was . The fourth expression uses the dereference operator () that can be read as "value pointed to by", and the value pointed to by is indeed .

So, after all that, you may also infer that for as long as the address pointed to by remains unchanged, the following expression will also be true:



Declaring pointers

Due to the ability of a pointer to directly refer to the value that it points to, a pointer has different properties when it points to a than when it points to an or a . Once dereferenced, the type needs to be known. And for that, the declaration of a pointer needs to include the data type the pointer is going to point to.

The declaration of pointers follows this syntax:



where is the data type pointed to by the pointer. This type is not the type of the pointer itself, but the type of the data the pointer points to. For example:



These are three declarations of pointers. Each one is intended to point to a different data type, but, in fact, all of them are pointers and all of them are likely going to occupy the same amount of space in memory (the size in memory of a pointer depends on the platform where the program runs). Nevertheless, the data to which they point to do not occupy the same amount of space nor are of the same type: the first one points to an , the second one to a , and the last one to a . Therefore, although these three example variables are all of them pointers, they actually have different types: , , and respectively, depending on the type they point to.

Note that the asterisk () used when declaring a pointer only means that it is a pointer (it is part of its type compound specifier), and should not be confused with the dereference operator seen a bit earlier, but which is also written with an asterisk (). They are simply two different things represented with the same sign.

Let's see an example on pointers:



Notice that even though neither nor are directly set any value in the program, both end up with a value set indirectly through the use of . This is how it happens:

First, is assigned the address of firstvalue using the address-of operator (). Then, the value pointed to by is assigned a value of . Because, at this moment, is pointing to the memory location of , this in fact modifies the value of .

In order to demonstrate that a pointer may point to different variables during its lifetime in a program, the example repeats the process with and that same pointer, .

Here is an example a little bit more elaborated:



Each assignment operation includes a comment on how each line could be read: i.e., replacing ampersands () by "address of", and asterisks () by "value pointed to by".

Notice that there are expressions with pointers and , both with and without the dereference operator (). The meaning of an expression using the dereference operator (*) is very different from one that does not. When this operator precedes the pointer name, the expression refers to the value being pointed, while when a pointer name appears without this operator, it refers to the value of the pointer itself (i.e., the address of what the pointer is pointing to).

Another thing that may call your attention is the line:



This declares the two pointers used in the previous example. But notice that there is an asterisk () for each pointer, in order for both to have type (pointer to ). This is required due to the precedence rules. Note that if, instead, the code was:



would indeed be of type , but would be of type . Spaces do not matter at all for this purpose. But anyway, simply remembering to put one asterisk per pointer is enough for most pointer users interested in declaring multiple pointers per statement. Or even better: use a different statement for each variable.

Pointers and arrays

The concept of arrays is related to that of pointers. In fact, arrays work very much like pointers to their first elements, and, actually, an array can always be implicitly converted to the pointer of the proper type. For example, consider these two declarations:



The following assignment operation would be valid:



After that, and would be equivalent and would have very similar properties. The main difference being that can be assigned a different address, whereas can never be assigned anything, and will always represent the same block of 20 elements of type . Therefore, the following assignment would not be valid:



Let's see an example that mixes arrays and pointers:



Pointers and arrays support the same set of operations, with the same meaning for both. The main difference being that pointers can be assigned new addresses, while arrays cannot.

In the chapter about arrays, brackets () were explained as specifying the index of an element of the array. Well, in fact these brackets are a dereferencing operator known as offset operator. They dereference the variable they follow just as does, but they also add the number between brackets to the address being dereferenced. For example:



These two expressions are equivalent and valid, not only if is a pointer, but also if is an array. Remember that if an array, its name can be used just like a pointer to its first element.

Pointer initialization

Pointers can be initialized to point to specific locations at the very moment they are defined:



The resulting state of variables after this code is the same as after:



When pointers are initialized, what is initialized is the address they point to (i.e., ), never the value being pointed (i.e., ). Therefore, the code above shall not be confused with:



Which anyway would not make much sense (and is not valid code).

The asterisk () in the pointer declaration (line 2) only indicates that it is a pointer, it is not the dereference operator (as in line 3). Both things just happen to use the same sign: . As always, spaces are not relevant, and never change the meaning of an expression.

Pointers can be initialized either to the address of a variable (such as in the case above), or to the value of another pointer (or array):



Pointer arithmetics

To conduct arithmetical operations on pointers is a little different than to conduct them on regular integer types. To begin with, only addition and subtraction operations are allowed; the others make no sense in the world of pointers. But both addition and subtraction have a slightly different behavior with pointers, according to the size of the data type to which they point.

When fundamental data types were introduced, we saw that types have different sizes. For example: always has a size of 1 byte, is generally larger than that, and and are even larger; the exact size of these being dependent on the system. For example, let's imagine that in a given system, takes 1 byte, takes 2 bytes, and takes 4.

Suppose now that we define three pointers in this compiler:



and that we know that they point to the memory locations , , and , respectively.

Therefore, if we write:



, as one would expect, would contain the value 1001. But not so obviously, would contain the value 2002, and would contain 3004, even though they have each been incremented only once. The reason is that, when adding one to a pointer, the pointer is made to point to the following element of the same type, and, therefore, the size in bytes of the type it points to is added to the pointer.


This is applicable both when adding and subtracting any number to a pointer. It would happen exactly the same if we wrote:



Regarding the increment () and decrement () operators, they both can be used as either prefix or suffix of an expression, with a slight difference in behavior: as a prefix, the increment happens before the expression is evaluated, and as a suffix, the increment happens after the expression is evaluated. This also applies to expressions incrementing and decrementing pointers, which can become part of more complicated expressions that also include dereference operators (). Remembering operator precedence rules, we can recall that postfix operators, such as increment and decrement, have higher precedence than prefix operators, such as the dereference operator (). Therefore, the following expression:



is equivalent to . And what it does is to increase the value of (so it now points to the next element), but because is used as postfix, the whole expression is evaluated as the value pointed originally by the pointer (the address it pointed to before being incremented).

Essentially, these are the four possible combinations of the dereference operator with both the prefix and suffix versions of the increment operator (the same being applicable also to the decrement operator):



A typical -but not so simple- statement involving these operators is:



Because has a higher precedence than , both and are incremented, but because both increment operators () are used as postfix and not prefix, the value assigned to is before both and are incremented. And then both are incremented. It would be roughly equivalent to:



Like always, parentheses reduce confusion by adding legibility to expressions.

Pointers and const

Pointers can be used to access a variable by its address, and this access may include modifying the value pointed. But it is also possible to declare pointers that can access the pointed value to read it, but not to modify it. For this, it is enough with qualifying the type pointed to by the pointer as . For example:



Here points to a variable, but points to it in a -qualified manner, meaning that it can read the value pointed, but it cannot modify it. Note also, that the expression is of type , but this is assigned to a pointer of type . This is allowed: a pointer to non-const can be implicitly converted to a pointer to const. But not the other way around! As a safety feature, pointers to are not implicitly convertible to pointers to non-.

One of the use cases of pointers to elements is as function parameters: a function that takes a pointer to non- as parameter can modify the value passed as argument, while a function that takes a pointer to as parameter cannot.



Note that uses pointers that point to constant elements. These pointers point to constant content they cannot modify, but they are not constant themselves: i.e., the pointers can still be incremented or assigned different addresses, although they cannot modify the content they point to.

And this is where a second dimension to constness is added to pointers: Pointers can also be themselves const. And this is specified by appending const to the pointed type (after the asterisk):



The syntax with and pointers is definitely tricky, and recognizing the cases that best suit each use tends to require some experience. In any case, it is important to get constness with pointers (and references) right sooner rather than later, but you should not worry too much about grasping everything if this is the first time you are exposed to the mix of and pointers. More use cases will show up in coming chapters.

To add a little bit more confusion to the syntax of with pointers, the qualifier can either precede or follow the pointed type, with the exact same meaning:



As with the spaces surrounding the asterisk, the order of const in this case is simply a matter of style. This chapter uses a prefix , as for historical reasons this seems to be more extended, but both are exactly equivalent. The merits of each style are still intensely debated on the internet.

Pointers and string literals

As pointed earlier, string literals are arrays containing null-terminated character sequences. In earlier sections, string literals have been used to be directly inserted into , to initialize strings and to initialize arrays of characters.

But they can also be accessed directly. String literals are arrays of the proper array type to contain all its characters plus the terminating null-character, with each of the elements being of type (as literals, they can never be modified). For example:



This declares an array with the literal representation for , and then a pointer to its first element is assigned to . If we imagine that is stored at the memory locations that start at address 1702, we can represent the previous declaration as:


Note that here is a pointer and contains the value 1702, and not , nor , although 1702 indeed is the address of both of these.

The pointer points to a sequence of characters. And because pointers and arrays behave essentially in the same way in expressions, can be used to access the characters in the same way arrays of null-terminated character sequences are. For example:



Both expressions have a value of (the fifth element of the array).

Pointers to pointers

C++ allows the use of pointers that point to pointers, that these, in its turn, point to data (or even to other pointers). The syntax simply requires an asterisk () for each level of indirection in the declaration of the pointer:



This, assuming the randomly chosen memory locations for each variable of , , and , could be represented as:


With the value of each variable represented inside its corresponding cell, and their respective addresses in memory represented by the value under them.

The new thing in this example is variable , which is a pointer to a pointer, and can be used in three different levels of indirection, each one of them would correspond to a different value:

  • is of type and a value of
  • is of type and a value of
  • is of type and a value of

void pointers

The type of pointer is a special type of pointer. In C++, represents the absence of type. Therefore, pointers are pointers that point to a value that has no type (and thus also an undetermined length and undetermined dereferencing properties).

This gives pointers a great flexibility, by being able to point to any data type, from an integer value or a float to a string of characters. In exchange, they have a great limitation: the data pointed to by them cannot be directly dereferenced (which is logical, since we have no type to dereference to), and for that reason, any address in a pointer needs to be transformed into some other pointer type that points to a concrete data type before being dereferenced.

One of its possible uses may be to pass generic parameters to a function. For example:



is an operator integrated in the C++ language that returns the size in bytes of its argument. For non-dynamic data types, this value is a constant. Therefore, for example, is 1, because has always a size of one byte.

Invalid pointers and null pointers

In principle, pointers are meant to point to valid addresses, such as the address of a variable or the address of an element in an array. But pointers can actually point to any address, including addresses that do not refer to any valid element. Typical examples of this are uninitialized pointers and pointers to nonexistent elements of an array:



Neither nor point to addresses known to contain a value, but none of the above statements causes an error. In C++, pointers are allowed to take any address value, no matter whether there actually is something at that address or not. What can cause an error is to dereference such a pointer (i.e., actually accessing the value they point to). Accessing such a pointer causes undefined behavior, ranging from an error during runtime to accessing some random value.

But, sometimes, a pointer really needs to explicitly point to nowhere, and not just an invalid address. For such cases, there exists a special value that any pointer type can take: the null pointer value. This value can be expressed in C++ in two ways: either with an integer value of zero, or with the keyword:



Here, both and are null pointers, meaning that they explicitly point to nowhere, and they both actually compare equal: all null pointers compare equal to other null pointers. It is also quite usual to see the defined constant be used in older code to refer to the null pointer value:



is defined in several headers of the standard library, and is defined as an alias of some null pointer constant value (such as or ).

Do not confuse null pointers with pointers! A null pointer is a value that any pointer can take to represent that it is pointing to "nowhere", while a pointer is a type of pointer that can point to somewhere without a specific type. One refers to the value stored in the pointer, and the other to the type of data it points to.

Pointers to functions

C++ allows operations with pointers to functions. The typical use of this is for passing a function as an argument to another function. Pointers to functions are declared with the same syntax as a regular function declaration, except that the name of the function is enclosed between parentheses () and an asterisk () is inserted before the name:



In the example above, is a pointer to a function that has two parameters of type . It is directly initialized to point to the function :


Pointer assignment.

How to I assign an address to a pointer.
I want to do something like this:

0x100100080 - I know this address is in memory and has an int value(object)(on the heap, probably).
The only way to access it is through that address.

Can I use new(0x100100080). What will this actually do. Will it erase the data in the address.


This too doesn't work.

Is there any other way to access it.

EDIT:
Tried both these.




They compile, but do not give the desired result.
you cannot directly assign an address to a pointer.
Is there any other way to get the data in the address? Maybe reference, function....????

I only have the address and I know what is at that address (int, object...)
How do they do this in debuggers?
Placement new, your second example, is the way. It will not modify the contents of the memory since int's default constructor does nothing.

However, how do you know this address?

gives me a compiler error: Cannot cast *void to int. .

Any of the above code that compiles does not work right.

However, how do you know this address?

I did a "new int" and assigned it to a pointer. Dereferenced the pointer and gave it a value. Took the address to where the pointer was pointing.
Now, assume, for some reason, my pointer does not exist, BUT the 'new' object created is there at the same address(I know it is there due to something else, but I cannot use that "something else", to get it back).
How do I get another pointer to it.
The object is like a memory leak right now, but I need it.
Responding to the original post:

Could you clarify what you are trying to do?

If you want to access (and change) the variable, as suggested by your comment:

"I know this address is in memory and has an int value(object)(on the heap, probably).
The only way to access it is through that address."

Then you just have to dereference the pointer:



The "integer" at 0x100100080 will now contain 123

Of course, if it wasn't an integer something could go horribly wrong.

Also note that not all memory is readable. If you try and change memory in a const segment, you will cause a memory violation.



you cannot directly assign an address to a pointer.


I disagree.

This code creates a pointer named and sets its value (i.e. the memory address it points to) to some value occupied by the object , and then to zero, and then to some number I typed in.

This is very common in embedded coding where physical components are hard-wired to specifically identified memory addresses.


Compiler error: invalid conversion from 'long int' to 'long int*'
Does Not Work.

The compiler thinks 0x100100080 is a value(long int) and not an address.
Moschops wrote:
p = (int*) 0x100100080;


This compiles, but the output is not the value of the int. The Output is '0'.

Yes, it is an embedded system.

EDIT:
Just figured it out. The addresses are on two different memory units (RAM's).
That is the reason why Moschops code was not changing my value in the address (it was changing something somewhere else with the same address, 0x100100080.)

Thank you for all the Help.

This compiles, but the output is not the value of the int. The Output is '0'.


If the output is zero, and you're reading it as an int from the correct memory location, and displaying it as an int, then the value of the int is zero.

If you think it isn't zero, you have either made a mistake in your code, or you are denying the clear evidence in front of you.

I note that the hex value 0x100100080 is the binary value

1000 0000 0000 1000 0000 0000 0100 0000 0

which takes 33 bits to represent. Can your system handle a pointer size of 33 bits? A 32 bit system, as a general rule of thumb, can't.
Hmmm...

Was the problem computer a 64-bit system?

The solution is a little unclear to me; what do you mean by "The addresses are on two different memory units (RAM's)."
Maybe the 2 memory chips he's got use 32-bit addresses so the most-significant bit tells which of the 2 chips to read/write to. And if he's storing it on the first one but is reading it from the second one (because of int overflow) he could be getting different results. Imagine you write to address of 33-bit-long "long int" but read from an address of 32-bit-long "int" so the 33-rd bit is 0 by default. For me that's the most logical case according to what dams said so far.
Topic archived. No new replies allowed.

One thought on “Null Pointer Assignment C++

Leave a Reply

Your email address will not be published. Required fields are marked *