What really is the difference between data types when refering to memory?

Salviati Source

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

Answers

answered 2 years ago Steve Summit #1

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.)

  • The size of the pointed-to type (char, short, int, long, etc.) will determine how many bytes are accessed. (Depending on the hardware, it will also determine whether there are any alignment restrictions.)
  • Whether the pointed-to type is signed or unsigned will determine how the pointed-to memory is interpreted. (To be sure, a big part of the distinction is how you, or the rest of your code, interprets the values later. For example, whether you print something using %d or %u ends up making as much or more difference than whether you choose a signed or an unsigned type.)
  • Whether qualifiers like volatile are used may not end up making a difference at all.

answered 2 years ago cdcdcd #2

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.

comments powered by Disqus