Question

I've just started to learn C so please be kind. From what I've read so far regarding pointers:

int * test1; //this is a pointer which is basically an address to the process 
             //memory and usually has the size of 2 bytes (not necessarily, I know)
float test2; //this is an actual value and usually has the size of 4 bytes,
             //being of float type
test2 = 3.0; //this assigns 3 to `test2`

Now, what I don't completely understand:

*test1 = 3; //does this assign 3 at the address 
            //specified by `pointerValue`?
test1 = 3;  //this says that the pointer is basically pointing 
            //at the 3rd byte in process memory, 
            //which is somehow useless, since anything could be there
&test1; //this I really don't get, 
        //is it the pointer to the pointer? 
        //Meaning, the address at which the pointer address is kept?
        //Is it of any use?

Similarly:

*test2; //does this has any sense?
&test2; //is this the address at which the 'test2' value is found? 
        //If so, it's a pointer, which means that you can have pointers pointing 
        //both to the heap address space and stack address space. 
        //I ask because I've always been confused by people who speak about 
        //pointers only in the heap context.
Was it helpful?

Solution

Great question.

Your first block is correct. A pointer is a variable that holds the address of some data. The type of that pointer tells the code how to interpret the contents of the address being held by that pointer.

The construct:

*test1 = 3

Is called the deferencing of a pointer. That means, you can access the address that the pointer points to and read and write to it like a normal variable. Note:

int *test;
/*
 *    test is a pointer to an int - (int *)
 *   *test behaves like an int - (int)
 *
 * So you can thing of (*test) as a pesudo-variable which has the type 'int' 
 */

The above is just a mnemonic device that I use.

It is rare that you ever assign a numeric value to a pointer... maybe if you're developing for a specific environment which has some 'well-known' memory addresses, but at your level, I wouldn't worry to much about that.

Using

*test2

would ultimately result in an error. You'd be trying to deference something that is not a pointer, so you're likely to get some kind of system error as who knows where it is pointing.

&test1 and &test2 are, indeed, pointers to test1 and test2.

Pointers to pointers are very useful and a search of pointer to a pointer will lead you to some resources that are way better than I am.

OTHER TIPS

It looks like you've got the first part right.

An incidental thought: there are various conventions about where to put that * sign. I prefer mine nestled with the variable name, as in int *test1 while others prefer int* test1. I'm not sure how common it is to have it floating in the middle.

Another incidental thought: test2 = 3.0 assigns a floating-point 3 to test2. The same end could be achieved with test2=3, in which case the 3 is implicitly converted from an integer to a floating point number. The convention you have chosen is probably safer in terms of clarity, but is not strictly necessary.

Non-incidentals

*test1=3 does assign 3 to the address specified by test.

test1=3 is a line that has meaning, but which I consider meaningless. We do not know what is at memory location 3, if it is safe to touch it, or even if we are allowed to touch it.

That's why it's handy to use something like

int var=3;
int *pointy=&var;
*pointy=4;
//Now var==4.

The command &var returns the memory location of var and stores it in pointy so that we can later access it with *pointy.

But I could also do something like this:

int var[]={1,2,3};
int *pointy=&var;
int *offset=2;
*(pointy+offset)=4;
//Now var[2]==4.

And this is where you might legitimately see something like test1=3: pointers can be added and subtracted just like numbers, so you can store offsets like this.

&test1 is a pointer to a pointer, but that sounds kind of confusing to me. It's really the address in memory where the value of test1 is stored. And test1 just happens to store as its value the address of another variable. Once you start thinking of pointers in this way (address in memory, value stored there), they become easier to work with... or at least I think so.

I don't know if *test2 has "meaning", per se. In principle, it could have a use in that we might imagine that the * command will take the value of test2 to be some location in memory, and it will return the value it finds there. But since you define test2 as a float, it is difficult to predict where in memory we would end up, setting test2=3 will not move us to the third spot of anything (look up the IEEE754 specification to see why). But I would be surprised if a compiler would allow such thing.

Let's look at another quick example:

int var=3;
int pointy1=&var;
int pointy2=&pointy1;
*pointy1=4;  //Now var==4
**pointy2=5; //Now var==5

So you see that you can chain pointers together like this, as many in a row as you'd like. This might show up if you had an array of pointers which was filled with the addresses of many structures you'd created from dynamic memory, and those structures contained pointers to dynamically allocated things themselves. When the time comes to use a pointer to a pointer, you'll probably know it. For now, don't worry too much about them.

First let's add some confusion: the word "pointer" can refer to either a variable (or object) with a pointer type, or an expression with the pointer type. In most cases, when people talk about "pointers" they mean pointer variables.

A pointer can (must) point to a thing (An "object" in standards parlance). It can only point to the right kind of thing; a pointer to int is not supposed to point to a float object. A pointer can also be NULL; in that case there is no thing to point to.

A pointertype is also a type, and a pointer object is also an object. So it is allowable to construct a pointer to pointer: the pointer-to-pointer just stores the addres of the pointer object.

What a pointer can not be:

  • It cannot point to a value: p = &4; is impossible. 4 is a literal value, which is not stored in an object, and thus has no address.
  • the same goes for expressions: p = &(1+4); is impossible, because the expression "1+4" does not have a location.
  • the same goes for return value p = &sin(pi); is impossible; the return value is not an object and thus has no address.
  • variables marked as "register" (almost distinct now) cannot have an address.
  • you cannot take the address of a bitfield, basically because these can be smaller than character (or have a finer granularity), hence it would be possible that different bitmasks would have the same address.

There are some "exceptions" to the above skeletton (void pointers, casting, pointing one element beyond an array object) but for clarity these should be seen as refinements/amendments, IMHO.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top