1

I'm trying to improve my coding practice for embedded. The code below writes the value 0 to the memory location 0x80001000.

#define MemoryWrite(A,V) *(volatile unsigned long*)(A)=(V)
#define FLAG 2147487744 //0x80001000

uint32_t reset_value = 0;

int main(void)
{
    MemoryWrite(FLAG, reset_value);
    return 0;
}

I have two questions:

  1. In order to use the MemoryWrite macro I had to convert 0x80001000 to 2147487744. I think this makes the code unclear. Is there a way that I could do this just using the hex value?

  2. I defined reset_value as a uint32_t. What would it change if I used #define instead ?

Julien
  • 1,810
  • 1
  • 16
  • 34
riverrock
  • 141
  • 3
  • 12
  • 4
    Using macros like this is ugly, unnecessary and error-prone. Use an inline function and do it properly. – Paul R Aug 26 '16 at 11:00
  • 2
    Why can't you use the hex representation? – Oliver Charlesworth Aug 26 '16 at 11:00
  • 4
    "In order to use the MemoryWrite macro I had to convert 0x80001000 to 2147487744" Huh? That's nonsense. The only difference is the format used in the source code file, it will make no difference in the actual binary. – Lundin Aug 26 '16 at 11:00
  • 1
    What's certianly not good is the type mixing. `MemoryWrite` expects an `unsigned long`, not a `uint32_t`. I don't know which of the two is correct, I have no idea what your embedded system has at offset `0x80001000` – MSalters Aug 26 '16 at 11:01
  • 1
    Also the type concerns were already addressed in your previous question [here](http://stackoverflow.com/a/39146747/584518). – Lundin Aug 26 '16 at 11:01
  • 1
    What do you mean "convert `0x80001000` to `2147487744`"??? There is no conversion here! – barak manos Aug 26 '16 at 11:04
  • Maybe the subtle signedness of integer literals causing bugs again... 2147487744 could be of a large signed integer type (maybe long long int), while 0x80001000 could be of a smaller, unsigned type (maybe unsigned long). See [this](http://stackoverflow.com/questions/34182672/why-is-0-0x80000000). – Lundin Aug 26 '16 at 11:06
  • @Lundin That may be the case. – Rishikesh Raje Aug 26 '16 at 11:08
  • 2
    You said you had to use `2147487744 ` instead of `0x80001000`. Why? What happened when you used `0x80001000 `? – Steve Summit Aug 26 '16 at 11:24
  • When you use `uint_32_t reset_value = 0;`, the debugger will be able to resolve `reset_value` and show you its value in a watch window. If you use `#define RESET_VALUE 0` then the debugger may not be able to resolve `RESET_VALUE` to a value and then you will have to leave the debugger and search the code in the editor to find the value associated with RESET_VALUE. Worrying about which way is more efficient is a waste of time. The compiler will make it efficient enough either way. – kkrambo Aug 26 '16 at 13:18

2 Answers2

5
  1. In order to use the MemoryWrite macro I had to convert 0x80001000 to 2147487744. I think this is bad practice and unclear. Is there a way that I could do this just using the hex value?
#define FLAG 0x80001000u

When the u for unsigned is not present and because you have 4 bytes on a 32bit machine, the compiler assumes that the value is signed and 0x80001000 = -2147479552

  1. I'm not sure if defining reset_value as a uint32_t is good practice. Could I do this as a #define.

You can of course write #define RESET_VALUE 0. But the usefulness is not evident in such simple code. It depends if you want to modify the value in the future or not. The compiler will certainly optimize it in the same way for this 10 lines example.

Julien
  • 1,810
  • 1
  • 16
  • 34
  • It is not possible for `0x80001000` to have a negative value. The type will be the first type that can represent its value in the list [ `int`, `unsigned`, `long`, `unsigned long`, `long long`, `unsigned long long` ]. On a 32 bit machine with no padding bits, `0x80001000` will be either `unsigned int` or `unsigned long`. – Nisse Engström Sep 04 '16 at 12:18
  • @NisseEngström My interpretation is that for hexadecimal values you take the first type in the list in which the representation fits. "Hexadecimal constants don't have a sign or any inherent way to express a negative number" (source : http://stackoverflow.com/questions/4737798/unsigned-hexadecimal-constant-in-c) In our case this is `int` or `long`. Please share your reference link I would be happy to read it (I did not found it myself). – Julien Sep 04 '16 at 16:24
  • The C standard, §6.4.4.1 Integer Constants (point 5). The [final public draft](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) can be downloaded for free. -- Assuming an implementation with a 32 bit integer type, the value of `0x80001000` cannot be represented by the corresponding **signed** integer (the value would be negated), so the type of the constant has to be the **unsigned** version of the same type. – Nisse Engström Sep 06 '16 at 03:34
  • @NisseEngström I may start to agree with you. After some research and seeing reference from @Lundin in question comments. Could it be explained as the hex representation could fit into `unsigned int` but decimal could fit into `signed long int` (decimal constant always in a signed type) and then causing problems during pointer casting in `MemoryWrite` ? – Julien Sep 06 '16 at 08:39
3

You can use this,

#define FLAG 0x80001000ULL
...
MemoryWrite(FLAG, 0);
Rishikesh Raje
  • 8,556
  • 2
  • 16
  • 31
  • 1
    How exactly does that answer the question at hand??? You've basically copied the code from the question into your answer. – barak manos Aug 26 '16 at 11:05
  • It removes the `reset_value` variable. – Rishikesh Raje Aug 26 '16 at 11:07
  • 1
    Yes, you've also changed the text `2147487744 //0x80001000` to `0x80001000`... So??? – barak manos Aug 26 '16 at 11:07
  • Possibly the only difference between `21474877441` and `0x80001000` is that the first is a signed value and is probably a long long int and the second is an unsigned value and is a lower type. – Rishikesh Raje Aug 26 '16 at 11:17
  • I have added a `ULL` to make the two codes identical. – Rishikesh Raje Aug 26 '16 at 11:19
  • @RishikeshRaje, you are wrong, `21474877441` and `0x80001000` are of the same type and haves the same value, the first one in decimal notation and the second one in hexadecimal: [More info](http://stackoverflow.com/q/4737798/1606345), furthermore OP is comparing with an `unsigned long`, not with an `unsigned long long` – David Ranieri Aug 26 '16 at 11:58
  • @AlterMann I was thinking like you but we were wrong, Rishikesh Raje is completely right : http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf §6.4.4.1 Decimal and hexadecimal are not interpreted in the same way ! – Julien Sep 07 '16 at 06:06
  • @Julien, I know that an hex can not be represented in negative but thats not the point, there is an explicit assignment to an `unsigned` type, the notation does not affect the type nor the size as stated by him (the suffix ULL does). – David Ranieri Sep 07 '16 at 08:27
  • 1
    @AlterMann you said "21474877441 and 0x80001000 are of the same type and haves the same value". They are not of the same type and not of same size. Of course they have the same value but the first may be `signed long long int` while the second is `unsigned long int` (not same size) – Julien Sep 07 '16 at 08:49
  • Ooops, you are right, I didn't know this one. I have tested this on gcc and it gives me different sizes and types. Sorry @RishikeshRaje, and thank you Julien for pointing that! – David Ranieri Sep 07 '16 at 08:58
  • @AlterMann thank you for the GCC test. As you already have the GCC setup, could you confirm the main problem (different behavior with hex and dec notation) comes from the later type casting in `MemoryWrite()` ? – Julien Sep 07 '16 at 09:15
  • I mean I have tested sizeof(hex notation) and sizeof (dec notation) and as you said they are different, for the OP example I can't see any difference in the output nor in gdb, here a paste bin of the assembled output [using reset value as uint32](http://pastebin.com/9XdNiJQw) and [using plain 0](http://pastebin.com/jFH8WDi9) – David Ranieri Sep 07 '16 at 09:37