I take a course in machine oriented programming and I have some problem understanding data types when referring to a memory address. For example, if I want to access a piece of memory on position 0x40020000. I would have written it like this.
#define NAME_OF_THE_REGISTER *((unsigned int *)(0x40020000))
But what is the difference between using some other data type? For example unsigned short or unsigned char. I know that they have different range, but if I change this particular code to unsigned short, I can still access the memory and everything work fine.
In the "example" codes my university provides they seem to like switching between unsigned char, volatile unsigned char, unsigned int, volatile unsigned int and so on and I can not figure out why. Example
#define PORT 0x40020000 #define PORTNAME ((volatile unsigned int *) (PORT)) #define PORTOFFSET1 ((volatile unsigned short *) (PORT + 0x4)) //offset to access Output register
What is the advantages and flaws between using them?ctypes
Consider this memory layout:
100 01 02 03 04 104 05 06 ff ff 108 07 08 09 10
On a 32-bit, little-endian machine, the code
printf("%d\n", *(char *)104); printf("%d\n", *(short int *)104); printf("%d\n", *(int *)104); printf("%u\n", *(unsigned int *)104);
would likely print
5 1541 -63995 4294903301
(If it's not obvious, where those numbers come from is that they are the decimal representations of, respectively: 0x05, 0x0605, 0xffff0605 interpreted as a signed int, and 0xffff0605 interpreted as an unsigned int.)
So, yes, the type you cast the pointer to determines how the compiler is going to access and interpret the memory. (Strictly speaking, it determines how the compiler is going to generate code, and how the generated code is going to access and interpret the memory.)
long, etc.) will determine how many bytes are accessed. (Depending on the hardware, it will also determine whether there are any alignment restrictions.)
%uends up making as much or more difference than whether you choose a signed or an unsigned type.)
volatileare used may not end up making a difference at all.
The type informs the compiler of the size of the variable stored at said location. Therefore using the wrong type would result in a fetch (or move) for only a portion of the actual values (or an over read if larger) at the said memory location when derefrenced. So it it is important and hence why you might wish to use volatile - tells the compiler to leave this alone wherever it is encountered.
In the second example, the (macro) PORTOFFSET1 substitutes with a location that has an offset of 4 (for type short) from the address used in the PORTNAME #define. This suggests that both the memory locations PORTNAME and PORTOFFSET1 refer to a contiguous block of memory for which the first four bytes contain an int, and the following two a short.
Use of volatile is very important if you don't want the compiler to modify or even remove your definition where used.