1

In C, I have a struct with a member "frequency", which is a long unsigned int. The hardware this code is running on is 32 bits.

The struct has its value set at instantiation.

struct config_list configuration = {
    ...
    .frequency = (long unsigned int)5250000000u,
    ...
}

In my code, I pass this struct, via pointer, to a subroutine. Inside this subroutine, I don't seem to be able to get the right value, so to check, I put in this:

printf("Config frequency: %lu\n", ptr->frequency);
printf("Derefernced: %lu\n", (*ptr).frequency);

As those are the two ways I believe you would be able to access the struct data from the pointer.

However, in both cases the output I see is 955,032,704. This I recognize as just the first 32 bits of the frequency I set. My question is, why is my long unsigned int being cut to 32 bits? Isn't a "long" supposed to extend the range to 64 bits?

Zephyr
  • 337
  • 5
  • 23
  • 2
    The value `5250000000` is out of the range for a 32-bit unsigned integer. Where did you get the code from? It doesn't seem to be designed for a 32-bit system. – Some programmer dude Apr 03 '17 at 14:05
  • Use `long long` and `"%llu"` instead of `long`. Or to avoid any ambiguity, include `stdint.h` and define `frequency` as `uint64_t`. – r3mainer Apr 03 '17 at 14:05
  • `ptr->frequency` is the usual way for writing `(*ptr).frequency`. Both notations are strictly equivalent. – Jabberwocky Apr 03 '17 at 14:05
  • 1
    Perhaps you might include `stdint.h` and use the 64 bit type from there – infixed Apr 03 '17 at 14:08
  • @Someprogrammerdude I know that value is too long for an unsigned integer, but I thought a long integer is supposed to have a bigger range than a standard. Is that not the case? – Zephyr Apr 03 '17 at 14:11
  • @squeamishossifrage Ok, I'll try the long long thing. Could you explain, then, what exactly a single "long" does? If it's still 32 bits, what is the difference between a long int and just "int"? – Zephyr Apr 03 '17 at 14:12
  • 3
    Like almost all integers in C, the size of `long` depends on the compiler. GCC typically sets it to 64 bits on 64 bit systems, but 32 bits on 32 bit systems. The Microsoft Visual C(++) compiler have `long` be 32 bits on both 32 and 64 bit systems. If you want a larger type then use `long long` which is guaranteed to be at least 64 bits. Please see e.g. [this type reference](http://en.cppreference.com/w/c/language/arithmetic_types) for more information. – Some programmer dude Apr 03 '17 at 14:13
  • Just don't use the standard crap types. Use `uint64_t` instead, problem solved. – Lundin Apr 03 '17 at 14:15
  • Alright, this worked. I had assumed "long" makes something 64 bits, but apparently "long long" does that. "uint64_t" also worked right. I just needed to be more careful about how I'm using datatypes. – Zephyr Apr 03 '17 at 14:26
  • You are declaring your literal to be of type unsigned int (the u suffix), 5250000000ul will make it of type long unsigned. A long long is guaranteed to be at least 64bit (and therefore large enough for you number), but as other have said use uint64_t. this makes you intentions clear (If you wont the type to be 64bit) – fhtuft Apr 03 '17 at 14:32

1 Answers1

4

5250000000 is 0x1 38EC A480... it just peeks into the 33rd bit.

I would recommend that you use the following:

#include <stdio.h>
#include <stdint.h>    /* gives us uint64_t and UINT64_C() */
#include <inttypes.h>  /* gives us PRIu64 */

int main(void) {
    uint64_t d = UINT64_C(5250000000);

    printf("%"PRIu64"\n", d);

    return 0;
}
  • uint64_t is guaranteed to be a 64-bit unsigned, on any system.
  • UINT64_C will append the correct suffix (typically UL or ULL).
  • PRIu64 will specify the correct format string for a 64-bit unsigned.

On my 64-bit system, it looks like this after the pre-processor (gcc -E):

int main(void) {
     uint64_t d = 5250000000UL;

      printf("%""l" "u""\n", d);

       return 0;
}

And for a 32-bit build, it looks like this (gcc -E -m32):

int main(void) {
     uint64_t d = 5250000000ULL;

      printf("%""ll" "u""\n", d);

       return 0;
}
Attie
  • 6,690
  • 2
  • 24
  • 34
  • Or pedantically, you could write `uint64_t d = UINT64_C(5250000000);` – Lundin Apr 03 '17 at 14:16
  • This worked. My mistake was assuming "long" automatically meant 64 bits, when it's apparently platform dependent. Both this example of uint64_t and the other recommendation of using "long long" worked to give the correct value. – Zephyr Apr 03 '17 at 14:24
  • Don't forget you can always use `sizeof()` to see how bit a variable actually is. – Attie Apr 03 '17 at 14:28
  • @Lundin What advantage do you see with `uint64_t d = UINT64_C(5250000000);` vs `uint64_t d = 5250000000;`? – chux - Reinstate Monica Apr 03 '17 at 15:56
  • `UINT64_C()` adds the appropriate suffx (e.g: `UL` or `ULL` depending on system). – Attie Apr 03 '17 at 15:57
  • OK, Attie & @Lundin, I should have asked what advantage to using `uint64_t d = UINT64_C(5250000000);` vs `uint64_t d = 5250000000u;` (with the `u`) as OP coded. Adding the `L` or `LL` in itself is not an advantage. – chux - Reinstate Monica Apr 03 '17 at 16:00
  • @chux The only advantage is that there's no implicit conversion between the literal and the variable. `UINT64_C` picks the appropriate suffix `UL` or `ULL`. Pedantic, but might prevent some static analyser from moaning. Also helps maintenance, if the literal would be changed. In real production code you should of course not have "magic numbers" like this, so there it might end up behind a `#define`, which could potentially be used in several places. – Lundin Apr 04 '17 at 06:45
  • Artie, the [ref](http://stackoverflow.com/questions/43186729/reading-a-long-from-a-struct-on-32-bit-systemc/43186983?noredirect=1#comment73451704_43186983) addresses explicit `L`, `LU`, etc. and not the value use of `UINTn_C()`: _Macros for minimum-width integer constant_. – chux - Reinstate Monica Apr 04 '17 at 10:42