Pointers

Go To Last Post
19 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

I have been reading up on pointers (and a few YouTube videos) and I just don't get them at all. I don't understand what the point is (no pun intended). I get that they "point" to a memory location, but I don't understand why or when you would do that, or what that even means if I am honest. Everything I have read or seen has explained it in more or less the same way, but it just doesn't click with me what they do.

Would anyone care to have crack at trying to explain them to me?

Thanks

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:
but I don't understand why or when you would do that
When you have a situation where you want to change what it points to. e.g., you have two strings that might be used depending on the situation. Use a pointer to point to the one you want now:

char string1[] = "String 1";
char string2[] = "String 2";
char *currentString;

if (conditionMet)
{
   currentString = string1;
}
else
{
   currentString = string2;
}
...
print(currentString);

Or if you want to send a value into a function and have the function change it:

void IncrementMyValue1(int aValue)
{
   //This doesn't work since aValue is a local variable
   //and contains only a copy of the value being sent in
   value++;
}

void IncrementMyValue2(int *aValue)
{
   //This changes the value of the variable that
   //is being sent in (pointed to by aValue)
   (*aValue)++;
}

int value = 1;
IncrementMyValue1(value); //value is still 1 after this call
IncrementMyValue2(&value); //value is 2 after this call

Or to save time/memory. If you have a variable that is a large struct that takes up, say, 40 bytes and you want to send it into a function. Sending the struct in means that all 40 bytes will need to be copied taking time and memory. Send in a pointer to that struct, and you only need 2 bytes (at least on an AVR). Make the function parameter const to avoid the function changing the variable when you don't want it to.

Regards,
Steve A.

The Board helps those that help themselves.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You may find it useful to Google for:

"pass by value" "pass by reference"

The latter is done using pointers.

Oh and you may have used arrays in C. They are really just using pointers. A collection of elements in memory with a pointer to the Nth element calculated as an offset from the base address.

BTW I don't see anything GCC specific about this (in fact nothing specific to AVR) but I'll move this to the AVR Forum.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

BTW I don't see anything GCC specific about this (in fact nothing specific to AVR) but I'll move this to the AVR Forum.

Sorry I wasn´t sure which was the correct area to post. Thanks for letting me know for the future

So does the first example, basically convert currentString to equal "string 1"? So that print command at the end would print, "String 1" (without the quotation marks and assuming the condition is met)? In other words (like Clawson said) String1[] could be written as an array like,

char string1[9] = {s, t, r, i, n, g, , 1};

And all the pointer *currentString does is merge all the values together to one value. Instead of being 9 separate values, it´s just one value, string 1. Or am I miles away with that conclusion.

The second example I don´t get at all I am afraid. Thanks for trying though.

I'll google the things suggested by Clawson now to see if they help.

Thank you

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

When you use string1[3] in your example the C compiler accesses *(string1 + 3). "string1" in this is the base address in memory where the data is stored. By the way characters in C are delimited with single quotes so:

char string1[9] = {'s', 't', 'r', 'i', 'n', 'g', ??? , '1'}; 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0
char string1[9] = {s, t, r, i, n, g, , 1}; 

Should through out a compiler error.

What you want is:

char string1[9] = {'s', 't', 'r', 'i', 'n', 'g', ' ', '1', 0};

Which is the same as:

char string1[9] = "string 1";
and
char string1[9] = {0x73, ox74, 0x72, 0x69, 0x6E, 0x67, 0x20, 0x31, 0x00};

The first example changes currentString to point to string1. It doesn't do anything to the contents of string1.

A Pointer is a variable whose value represents an address in memory.

So setting currentString = string1 changes the value of currentString to be the address of string1. Then you can use currentString the same as you would use string1 to pass the string to a function or to otherwise access it's contents. The usefulness of pointers is that you can programmatically change what you are referencing without moving lots of data around (2 bytes vs 9 bytes in this case).

In the second example, when you pass a variable (aValue) to a function, a COPY of the contents of the variable is passed to the function. Anything the function does to that value has no effect on the original variable. It's like making a photocopy of a form and then marking up the copy, the original is unaffected.

By passing a pointer to the variable you are giving the function access to the variable itself, not a copy of it's data. In this case if you mark up the form, it's the original you are marking up.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

This is why they make you learn some sort of assembly language, even if it is for an imaginary cpu, when you take a programming class. In assembler, you are worried about loading variables a byte at a time into registers, adding the registers, saving the results from the registers to ram variables. Phew. Very Lo Level. When John W Forrester and his buds at IBM invented fortran in the 50s, they were sick of assembler. They wanted to solve equations. Y=mx+b I=PRT Non of this what 4 registers hold the y value?
When you write a subroutine to print out a string, you pass it a pointer to the string. The address of the string. You pick up the chars one by one and print em out and increment the pointer so it points to the next character. You don't need to know about this if you think of the string as an array. All old fortran programmers view every data structure as an array. Just load data[ndx] and print it out and increment index. If you run out of flash, look at the assembler that the compiler outputs. It takes extra instructions to calc the offset of the array index and add it to the base address of the array, so the array based version is usualayy a little fatter. Like this message. Sorry.

Imagecraft compiler user

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Sorry for being so thick, but I think it is starting to sink in a bit.

Quote:

The usefulness of pointers is that you can programmatically change what you are referencing without moving lots of data around (2 bytes vs 9 bytes in this case).

This I get, I think. So what you are saying here is that instead of making another variable of 9 bytes, you just make a 2 byte one that tells the AVR to look at the address where the original string is held, string1[] in this case, and use whatever data is there?

char string1[] = "String 1"; 
char string2[] = "String 2"; 
char *currentString; 

Therefore in this original code, char *currentString is only 2 bytes instead of 9?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Each byte in a data space(there are also program and eeprom address spaces - not to be confused) has its address (16-bit value).
For example:

    R0 register has the address 0x0000 R1 register has the address 0x0001
    ...
    SREG register has the address of 0x005F
    Same with some other byte that you can call My_data.
Lets place My_data at some address, lets say 0x1234:

.dseg
.org 0x1234
My_data:
.byte 1

Now, lets load 0x1234 value (it is called "address of My_data") into one of X, Y or Z registers.
Lets say it is Z this time:

.cseg
ldi ZL, low(My_data) ; load 0x34 to ZL
ldi ZH, high(My_data) ; load 0x12 to ZH

So now Z register can be used as a pointer to My_data as it holds "address of My_data".
When you execute:

ld R0, Z

it will place the content of My_data (the value stored under 0x1234) into R0 register.
When you execute:

st Z,R0

it will place the content of R0 register into My_data.

That is all the mystery of pointers.

No RSTDISBL, no fun!

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Here's a more concrete example of pointers. Perhaps one of the most widely used as most AVR programs have something like this. Let's say you have a function to output a single character to a UART or LCD such as uart_putchar(). The question is how do you now output "Hello world"? The solution is likely to involve pointers as follows:

void uart_putchar(char c) ;

void uart_putstring(char * str) {
  while (*str != 0) {
    uart_putchar(*str++) ;
  } 
} 

int main(void) {
  char text[] = { "Hello world"} ;
  uart_putstring(text) ;
} 

I left some bits out to keep it simple but the point here is that the first line of main puts the string of characters "Hello world" into RAM somewhere. Let's guess that it's location 0x123. Then the uart_putstring function is called passing "text". Well "text" is the base address of those characters in memory (0x123) so the uart_putstring function is entered with the str parameter (a pointer to char) being set to 0x123.

Now the while loop is entered. The first test is *str. That means go and get the char that is held in location "str", that is the character in location 0x123. So it picks up 'H'. It compares this to 0. It is not equal so it does the code inside the while loop. This makes a call to uart_putchar passing *str which we already know is 'H' and so the letter 'H' is output on the UART/LCD/whatever. On the end of *str this time is ++, that means that when it comes back from doing the uart_putchar a further operation is applied to "str", it is incremented. It was holding 0x123 so after this the pointer is incremented to 0x124 and it loops back to the while () test. This time it picks up *str, that is *0x124, the character held in location 0x124 which is the 'e' of "Hello".

The same as before occurs and this time the UART outputs 'e'. At the str++ the str pointer increments from 0x124 to 0x125. As you can see, str is slowly making its way through memory. *str this time will be the first 'l' of "Hello".

So it keeps on going outputting 'H', 'e', 'l', 'l', 'o', ' ', 'w'...

When will it ever end? Well 0x12D holds the final 'd' of "Hello world". After that the str pointer is incremented to 0x12E. Now you may not know this but C does something useful for us when we use "Hello world". It puts all those characters into memory for us but in addition it sets the very next byte after the last character to hold 0x00. This is why I wrote:

 while(*str != 0)

The fact is that 0x12E is bound to hold 0x00 as a marker to say "the string ends here". So *str when str==0x12E is 0x00. in this case the != test in the while is false. So it stops performing the loop and returns from uart_putchar having successfully output "Hello world" to the UART.

THAT is why pointers are useful.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Brutte, I am sorry but I don´t understand word of what you have said. Thanks though.

Thanks for your patience, I think I get it now. *str holds the location of one value which is just cycled through until we reach the zero termination bit (I already knew about that bit), so if we instead did,

    uart_putchar(*str+=2) ;

Then the LCD would output HLOWRD, i.e every other character, or does it have to be incremented by 1 each time?

Thanks again

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

You can do arithmetic on pointers like you demonstrate. However, with your example, you may skip over the terminating 0 and the program will do weird things.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

Then the LCD would output HLOWRD, i.e every other character, or does it have to be incremented by 1 each time?

Conceptually yes except that it's not quite valid syntax. But I could have written:

    uart_putchar(*str++) ; 

as

    uart_putchar(*str) ; 
    str++;

or even:

    uart_putchar(*str) ; 
    str += 1;

(they are all logically identical) and as such if I wrote:

    uart_putchar(*str) ; 
    str += 2;

and then, indeed, it would output "Hlowrd". However there would be a slight problem because after the 'd' it would add 2 to str and point at the byte BEYOND the 0x00. So it might continue on printing nonsense until it happened to hit a 0x00 somewhere.

Note also you said:

 *str holds the location of one value 

That's a misunderstanding. "str" not "*str" holds the "location of one value". *str is the value from the location that str is currently holding (pointing to).

Note also that I use "char text[]" and "char * str". That meant that when you do str++ or "str += 1" or "str = str + 3" it moves on by one byte, noe byte or three bytes.

If however I'd done "int data[]" and "int * p" then p++ or "p = p + 3" then the value in p would move on by 2 bytes or 6 bytes. This is another feature of pointers. When you add N to them it means "move on by N of the kind of thing you point at". As an "int" is 2 bytes wide then p++ means move on by 2 bytes not just one and "p += 3" means move on by three int's which is six bytes.

Taking that a step further if I define:

typedef struct {
  int n;
  long l;
  char c;
} data_type;

Then one of these is 2+4+1 = 7 bytes. So if I then write:

data_type data[3] = {
 { 12345, 0xDEADBEEF, 'a'},
 { 22222, 0xBABEFACE, 'q'},
 { 103, 0xFFAA5500, 'z'}
};

data_type * p_data = &data[0];

int main(void) {
  P_data += 2;
}

Then p_data moves on by 14 bytes (two lots of sizeof(data_type). At that stage p_data->n = 103, p_data->l = 0xFFAA5500 and p_data->c = 'z'

Cliff

PS don't worry about the struct{} stuff and the -> operator at this stage though do note that -> is a deliberate symbol to look like it's pointing at something.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thank you for taking the time to write all of that. I need to read it through a few more times to digest it all, but I think that it makes sense now.

Thanks again, I do appreciate it.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

A Google of "C pointers tutorial" seems to give a lot..

As of January 15, 2018, Site fix-up work has begun! Now do your part and report any bugs or deficiencies here

No guarantees, but if we don't report problems they won't get much of  a chance to be fixed! Details/discussions at link given just above.

 

"Some questions have no answers."[C Baird] "There comes a point where the spoon-feeding has to stop and the independent thinking has to start." [C Lawson] "There are always ways to disagree, without being disagreeable."[E Weddington] "Words represent concepts. Use the wrong words, communicate the wrong concept." [J Morin] "Persistence only goes so far if you set yourself up for failure." [Kartman]

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

If your toolchain has a simulator, I'd work thru some of the example codes, looking at the *.map file to see where an array and pointer are placed in memory to begin with. Even works on whatever you run into on the 'net, for getting a concrete understanding.

1) Studio 4.18 build 716 (SP3)
2) WinAvr 20100110
3) PN, all on Doze XP... For Now
A) Avr Dragon ver. 1
B) Avr MKII ISP, 2009 model
C) MKII JTAGICE ver. 1

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

JohanEkdahl wrote:
A Google of "C pointers tutorial" seems to give a lot..

Correct, and as I said in my first post, I couldn't grasp it. If I looked again now, I probably would understand them now.

Quote:

If your toolchain has a simulator, I'd work thru some of the example codes, looking at the *.map file to see where an array and pointer are placed in memory to begin with. Even works on whatever you run into on the 'net, for getting a concrete understanding.

Good idea, I use Atmel Studio 6 and I am pretty sure that you can do the simulator thing on that. I definitely give that go.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

For an AVR, a pointer is a 16-bit SRAM variable that holds a SRAM address. They are used in C language more than anywhere else in the software world.

C is a software language that has low-level hardware access and high-level data structures. Low-level hardware access means that you can use C to directly read and change any bit in any SRAM location. High-level data structuring means that you can create complex structures of trees, queues, linked-lists and all the other algorythms that computer scientists (as opposed to computer programmers and system analysts) can dream up.

Pointers are the 'glue' that allows thes two computer elements to function together. Assembly programmers rarely use pointers (in the 16-bit var that holds only an address) format. C programmers use them on the AVR mostly to access text strings and buffers.
Pointers have been driving people crazy for 40 years. You are not the first person to be completely lost in trying to understand them.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Quote:

a 16-bit SRAM variable that holds a SRAM address.

Not entirely true. You can have pointers in any of RAM, flash or EEPROM and the pointer itself can be pointing to any of those memory spaces. That's quite a lot of combinations in fact.