understanding pointers was a little hard for me at first, I needed a visual representation of the memory chipset (RAM).
the RAM is a continuous set of "banks" with values. to access a value, you are using variable names (it's easier for human), but the CPU uses memory addresses.
Each memory bank has a value and an address.
Code:
Memory value...: 0x07 | 0x20 | 0x56 | 0x00 | 0x00 | ...... | 0x53 | 0x68 | 0x6F | 0x70 | 0x70 | 0x69 | 0x6E | 0x67 | 0x00 | 0x00 |
Memory address : 0x00 | 0x01 | 0x02 | 0x03 | 0x04 | ...... | 0x145823 | 0x145824 | 0x145825| 0x145826|0x145827|0x145828|0x145829|0x14582A|0x14582B |0x14582C |etc.
^
this memory bank has address 0x01 and value 0x20
Like you know, a letter is not stored as a letter, but as a value. you use the
ASCII Table to convert a value to its corresponding letter.
strings are actually pointers to the memory.
When you create a new array :
char name[8] = "Shopping";
it assigned the start of "name" to a memory address, in the example above, at "0x145823", and fill one letter's value per adjacent memory bank. (one letter per bank)
you made an array with size of "8", but you can store shorter text in it. sometime you don't know how big a string can be.
For example, creating an array to store a path to a file , you need to allow the end user to have a big path, path[255]; but it can store shorter strings too like "c:\file.txt", the array is still 255 characters long even if the string it contains is shorter.
Creating
char name[8] = "test";
it also reserves 8 bytes to "name", but it doesn't mean it contains 8 letters. it just means creating a new variable will not overlap. a new variable will always be created at least 8 byte after "name" position in memory.
When you run a command
printf(name);
It doesn't print the full array, because if you reserved a big array (name[255]), you don't want the print command to print 255 letters if you only use it to write "test" in it. you'll have lot of spaces after that word and before the next word to print.
to determine the end of a string starting at a specific memory position, the printf command uses a "terminating character", the null character \0 (or 0x00 in hexadecimal).
you need to store "test\0", or "Shopping\0".
so, when you run
printf(name);
the CPU first locate the memory position of that variable. A string is an array of "char" (8bytes) values, so it's pointing to the first memory address used by your array. it's located at "145823" in the example I wrote above.
It then read each successive "bank's value" in order without knowing in advance how big the array or the string are, and convert each value into letter using the ASCII table, until it find the end of the string (0x00).
0x53 -> S
0x68 -> h
etc.
until it finds a "0x00" located at memory address 0x14582B
If you define your string as name[8], then the address 0x14582B could be assigned to another variable name (int nbr=41; ) for example.
if the memory 0x14582B contains the value 0x29 (41 in hex), then printf would convert it into "ASCII" letter and display it as ")".
by chance, you had the next memory bank with value 0x00, or else you would had a bigger string displayed on your result, or even a crash (if you try to display ASCII letter from non ascii values for example).
To fix this problem you need to create the array one byte bigger than your text.
char name[9]="Shopping"; // 8 letters, array size 9
The compiler should add the terminating null character automatically at 9th position (Shopping\0).
If your compiler does not add the terminating character automatically, you can have a buffer overflow bug.
char name[9]="Shopping";
name[9]=0; // force 0x00 manually on the 9th position. prevent buffer overflow ! Should be unneeded if the compiler takes care of it.
Sorry, it was a big text, maybe hard to understand.
I tried to make it as clear as I could, but I'm not always the best to make explanations
I didn't really explained the pointers, just how strings are stored, and the importance of the end character.
for now, remember that "pointer" is used to work with memory address.
you can access a memory address if needed. it'll be useful later, when you'll have to make bigger programs.
if you are ready for more headache :
When you use a variable, you let the CPU locate the memory bank for you, and read/write the value in that bank.
When you use a pointer, you access a bank number yourself, and read/write the value in that bank.
to work with pointers, you often use "*" character.
the "*" was the problem for me.
I didn't understood when it meant "pointer" and when it meant "value".
to
define a pointer, you use *
to access a pointer's value, you use *
to access a pointer's memory address, you don't use anything
to access the variable's memory address, you use &
3 examples:
int *ptr=0x145823; // address of a pointer, you define a memory. "ptr" is not a value, but a memory location. When you define a variable, use the * between the type and the variable name to specify that you want it as a pointer. int* ptr, int * ptr, or int *ptr are all identical.
u8 var=41; // you let the CPU choose where to store "var", and it sets that memory bank to value 41. note that u8 or char are (usually) identical. it's a "8bits" long data, 1 byte, 1 memory bank. You can also use "char var=41;" because "char" does not mean you'll store an ASCII letter but that the variable should have the same size than the size 1 character takes in memory and store unsigned integers.
ptr=0x145825; // change the memory address of the pointer. ptr is a pointer now, you can assign a different memory address.
*ptr=var; // set value 41 in memory address 0x145825. when
NOT defining a variable, if you use * it access the value of the memory bank number, not the memory address.
int mem; // create a variable. let the CPU choose where it's stored in the memory.
mem=&var; // store the address address of "var" in mem's value.
printf(mem); // will print the address of var. we don't know the address of "var", we defined var as "int" and let the CPU choose where it's stored in the RAM.
var=12;
ptr=&var; // set the memory address of the "var" variable into the pointer "ptr". ptr and var have both the same memory address now.
*ptr=50; // set the value of the bank located at the memory address of the ptr to 50. var and ptr sharing the same bank, editing the "value of ptr" is like editing the "value of var variable".
printf(var); // prints 50. var has been edited using the pointer to its address.