Questions about uintn_t variables and casting in C?

Go To Last Post
14 posts / 0 new
Author
Message
#1
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

After doing some Googling on casting, I've read a couple places that say something like this: int8_t, int16_t, int32_t, int64_t described as char, short int, int, and long long int respectively.  What happened to "long?"  I always thought 16 bit was int and 32 bit was long?  I need to get this part straight before going on to the next question... type casting.  I've been putting the UL suffix after some of my low value literals when assigning the value to a uint32_t.  But if the variable is considered an int, what is the UL suffix doing (ex. uint32_t = 44UL;).

 

I thought I had this figured out to use "static_cast" operator for casting the uintn_t types, but then on another site I read that static_cast is ONLY used in C++.  Right now, I'm trying to stick with straight C.  I think this is at least partly where I'm getting confused.  A lot of sites tend to combine C and C++ and it isn't always clear to me which one applies.  Plus, the different versions(?) vary from one to the next.  Is it C99? that started using the "int32_t" types?  For a newbie like me it gets complicated.  I searched the tutorials for casting and didn't see any pertinent titles there ([TUT] casting).  I'm sure there's some guide somewhere that can break this down.  I'd appreciate a link if anyone can recommend a good source.
 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The size of int depends on the processor.  So an int on a pc may be 32 bits while on an avr uC it is 16 bits.

 

When in doubt, use the C preprocessor operator sizeof() that generates the number of bytes for the specified type.

 

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

In principle, the sizes of the integer types depend on the compiler.

A machine with a 37-bit word could have a 17-bit int.

Not pretty.

 

In practice, char, short, and int would all be 37 bits.

long would be either 37 or 74 bits.

long long would be 148 bits.

IIRC 111 bits is fewer than C allows.

 

On a machine with an 8-bit data word, e.g. an AVR,

char would be 8 bits, short 16, int 16 (32 and 24 are also practical, but unlikely) bits,

long 32 bits and long long 64.

 

Edit: Also, sizeof is not a preprocessor thing.

"Demons after money.
Whatever happened to the still beating heart of a virgin?
No one has any standards anymore." -- Giles

Last Edited: Thu. Aug 4, 2016 - 07:25 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The standard reference is the Kernigan and Richie book usually referred to as K&R
It was my understanding that intxx came via stdint.h and boolean via stdbool.h

Oops! The website ate my tags!

Last Edited: Thu. Aug 4, 2016 - 08:13 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

On the whole you should try to avoid the use of typecasts all together. They are always saying "this doesn't quite fit here, can you squeeze it in anyway?". Far better is to use the right type for the job in the first place (though I recognise there are places where their use is unavoidable).

 

As for uint8_t, int32_t etc. The whole reason that <stdint.h> was adopted in C99 to provide these was that when moving code from CPU to CPU there are often too many assumptions being made about how wide types are. A traditional view is:

 

char/signed char/unsigned char = 8 bits

short/unsigned short = 16 bits

int/unsigned int = 32 bits

long/unsigned long = 32 bits

long long/unsigned long long = 64 bits

 

But this veers off in both directions. Down at the AVR end of the world short=int=16 bits and up at the IA-64/AMD64 end of the world long can easily be 64 bits. So a PC programmer might have written:

int n;
for (n = 0; n < 123456; n++) ...

but that simply isn't going to work for an AVR programmer as int is 16 bit so has a range of -32768..+32767 and you can't put 123456 in it. For this reason <stdint.h> was adopted so that the programmer can write:

#include <stdint.h>
int32_t n;
for (n = 0; n < 123456; n++) ...

and that will work exactly as the author intended no matter what CPU it is build for. On the Intel the compiler will likely have "typedef signed int int32_t;" and the AVR will have "typedef signed long int32_t;" but as far as the author and you, the reader, are concerned none of that matters because you all know that an int32_t will always have a numeric range of -2,147,483,648 to +2,147,483,647 so as long as you are dealing with numbers in that range this is the variable type to use. If you knew your numbers would only range between -128..+127 then you'd obviously just use int8_t and if the range was -32,768..+32,767 you would use int16_t.

 

Oh and as for casts.. You are right that if you write in C  there is only one type of typecast and that's the name of a type in parentheses. Such as:

foo((unsigned int) value);

In this the compiler is being told that whatever type "value" may be, when it is passed to foo() it should be converted to "unsigned int". That may involve truncation or extension.

 

In C++ there are additional casts: static_cast and reinterpret_cast. Your C++ manual will tell you all about those ;-)

 

Of course C++ is also a super-set of C which means it's happy to accept the traditional (type) style of cast too.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Thanks everyone.  I came to the table with a very basic understanding that the whole intn_t type series is compiler dependent because of the various platforms.  I understood the purpose, but I didn't grasp the full  depth of it as in my little world of programming, I've had no need of anything more than long integers... and single floats for that matter.  With the ever-growing depth of architectures, I can see the limitations with int, long int, long long int. 

 

I DO TRY to never use type-casting.  Not necessarily because of good programming practices, but if I'm honest, an in depth understanding of the nuances of casting (rounding errors etc) was more or less unnecessary if I just used the right data type to begin with.  I chose that so as to push learning those nuances down the long list of things I've still to learn.  Priorities.  So, in practice, rightly or wrongly, a flag goes up in my mind anytime I add 3 (literal) to 1234567 (variable).  I know it isn't casting, but adding UL after the 3 (if unsigned) always seemed like a safe practice.  This is what precipitated the OP at least in part.  Cliff, I think your response illuminates the problem I'm having.  On the surface, it seems that int32_t is both int and long int.  I've been trying to project what I've read onto the AVR (just the AVR) and it wasn't making sense.  I knew using int32 would be a 32 bit number on the AVR.  Calling it an int or long (though I understood it to be a long on the AVR) was irrelevant except when using the UL suffix.  A rose by any other name.... is still 32 bit. :)

 

So, a follow up question, assuming I'm not far off on my thinking now, how and when should I be using UL or F suffixes on an AVR?  Have I been using it incorrectly (from the highlighted example)?

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

chipz wrote:
I know it isn't casting, but adding UL after the 3

If it's not casting what is it? I see little difference in:

3UL

or

(unsigned long)3

No doubt some pedant will be along in a moment to put us right!

chipz wrote:
On the surface, it seems that int32_t is both int and long int.

Don't think of it in those terms (on some architectures it might well be, on others not). Think of it as simply "a 32 bit signed variable". In other words a container guaranteed to have the range -2^31 .. +(2^31 -1).

 

The whole point of <stdint.h> is to take all the confusion out of the world. If you use it you need never think about (short, int, long) ever again, everything is just an 8 or 16 or 32 bit signed/unsigned. The only "base" type in C you will continue to use is "char" (the special case of "char" that is neither "signed" nor "unsigned") and that you just use when you are accessing characters (which have no particular implied width though it is likely 7 or 8 bit).

 

Use UL or F suffixes when you need them. Numeric literals in C are "signed int" unless you say otherwise or "double" if they include a '.', if you want to specify something (for AVR) that is greater than +32767 (or will be used in a calculation where intermediate results exceed that and want to void issues of sign extension) then add a "UL" (or cast up using (unsigned long)). If you want "decimals" to be treated as "float" not "double" then add a trailing 'F'. Note that most AVR C compilers have both 32 bit float and 32 bit double so there's a tendency to treat them interchangeably. But if the code is headed for some platform where the 32/64 bit distinction will be apparent you may want to be more careful about the float/double distinction. In the short term ".0" is probably enough of a suffix to indicate that what looks like an integer is really a float/double value.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The C Language specifies the rules for type promotion when evaluating expressions. e.g. value + 3UL
You don't need to cast value as well as the 3
It is convenient to cast the 3 with the UL rather than the cumbersome (unsigned long)
If the constant was 123456 it would automatically be parsed as a long because it has more than 16 bits.

David.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

C does not allow 7 bit chars.

At least 8 bits are required.

"Demons after money.
Whatever happened to the still beating heart of a virgin?
No one has any standards anymore." -- Giles

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

clawson wrote:
On the whole you should try to avoid the use of typecasts all together. They are always saying "this doesn't quite fit here, can you squeeze it in anyway?".

Always?  ALWAYS?!?

 

Not.

 

Avoid?  I guess.  Avoid all together?  Not.

 

You've participated in threads where the cause of the problem is a 16x16 multiply overflowing.  I'll wager a cold one that you responded with a type cast recommendation.

 

You are a FATFS user.  Most of the time yo use a type cast on the passed buffer, as it is a void pointer, right?

You can put lipstick on a pig, but it is still a pig.

I've never met a pig I didn't like, as long as you have some salt and pepper.

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

chipz wrote:
I know it isn't casting, but adding UL after the 3

clawson wrote:
If it's not casting what is it?

The UL is only used as a suffix to a literal numeric constant; casting can be used on any expression.

 

I see little difference in:

3UL

or

(unsigned long)3

I guess, strictly, the first says, "this is an unsigned long"; whereas the second says, "take this int, and treat it as an unsigned long" ?

 

But I can't think that it makes any practical difference to the programmer - maybe it's significant to compiler writers?

 

No doubt some pedant will be along in a moment to put us right!

Dunno about "put you right" - but there's a possible suggestion ...

 

Top Tips:

  1. How to properly post source code - see: https://www.avrfreaks.net/comment... - also how to properly include images/pictures
  2. "Garbage" characters on a serial terminal are (almost?) invariably due to wrong baud rate - see: https://learn.sparkfun.com/tutorials/serial-communication
  3. Wrong baud rate is usually due to not running at the speed you thought; check by blinking a LED to see if you get the speed you expected
  4. Difference between a crystal, and a crystal oscillatorhttps://www.avrfreaks.net/comment...
  5. When your question is resolved, mark the solution: https://www.avrfreaks.net/comment...
  6. Beginner's "Getting Started" tips: https://www.avrfreaks.net/comment...
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

Okay.  The word "promotion" was missing from my vocabulary.  Thanks David.  That clears up a lot of my confusion on the topic.

 

I also didn't realize a decimal point would imply a double.  I always thought it was a float.  Thanks Clawson

  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

chipz wrote:
I also didn't realize a decimal point would imply a double. I always thought it was a float.

Nope that's why there is an 'F' suffix, not 'D' ;-)

 

Oh and note that in math.h the functions all tend to be "double". So you have sin(), tan() etc. If you want the "float" versions use sinf(), tanf() etc.

 

However note in the case of avr-gcc the math.h basically does:

/usr/lib/avr/include$ grep alias math.h 
#define cosf	cos		/**< The alias for cos().	*/
#define sinf	sin		/**< The alias for sin().	*/
#define tanf	tan		/**< The alias for tan().	*/
#define fabsf	fabs		/**< The alias for fabs().	*/
#define fmodf	fmod		/**< The alias for fmod().	*/
/** The alias for modf().
#define sqrtf	sqrt		/**< The alias for sqrt().	*/
#define cbrtf	cbrt		/**< The alias for cbrt().	*/
#define hypotf	hypot		/**< The alias for hypot().	*/
#define squaref	square		/**< The alias for square().	*/
#define floorf	floor		/**< The alias for floor().	*/
#define ceilf	ceil		/**< The alias for ceil().	*/
#define frexpf	frexp		/**< The alias for frexp().	*/
#define ldexpf	ldexp		/**< The alias for ldexp().	*/
#define expf	exp		/**< The alias for exp().	*/
#define coshf	cosh		/**< The alias for cosh().	*/
#define sinhf	sinh		/**< The alias for sinh().	*/
#define tanhf	tanh		/**< The alias for tanh().	*/
#define acosf	acos		/**< The alias for acos().	*/
#define asinf	asin		/**< The alias for asin().	*/
#define atanf	atan		/**< The alias for atan().	*/
#define atan2f	atan2		/**< The alias for atan2().	*/
#define logf	log		/**< The alias for log().	*/
#define log10f	log10		/**< The alias for log10().	*/
#define powf	pow		/**< The alias for pow().	*/
#define	isnanf	isnan		/**< The alias for isnan().	*/
#define isinff	isinf		/**< The alias for isinf().	*/
#define isfinitef isfinite	/**< The alias for isfinite().	*/
#define copysignf copysign	/**< The alias for copysign().	*/
#define signbitf signbit	/**< The alias for signbit().	*/
#define fdimf	fdim		/**< The alias for fdim().	*/
#define fmaf	fma		/**< The alias for fma().	*/
#define fmaxf	fmax		/**< The alias for fmax().	*/
#define fminf	fmin		/**< The alias for fmin().	*/
#define truncf	trunc		/**< The alias for trunc().	*/
#define roundf	round		/**< The alias for round().	*/
#define lroundf	lround		/**< The alias for lround().	*/
#define lrintf	lrint		/**< The alias for lrint().	*/

on the basis that sizeof(float)==sizeof(double).

Last Edited: Fri. Aug 5, 2016 - 09:12 AM
  • 1
  • 2
  • 3
  • 4
  • 5
Total votes: 0

The word "promotion" was missing from my vocabulary

That's actually a more complicated subject then at first it seems.

 

https://www.avrfreaks.net/comment/1254241#comment-1254241

 

Many other threads on the subject.

"Experience is what enables you to recognise a mistake the second time you make it."

"Good judgement comes from experience.  Experience comes from bad judgement."

"Wisdom is always wont to arrive late, and to be a little approximate on first possession."

"When you hear hoofbeats, think horses, not unicorns."

"Fast.  Cheap.  Good.  Pick two."

"We see a lot of arses on handlebars around here." - [J Ekdahl]