3

I have 2 questions about C programming:

  1. For int and uint16_t, long and uint32_t, and so on. When should I use the u*_t types instead of int, long, and so on? I found it confusing to choose which one is best for my program.

  2. When do I need to cast type? I have the following statement in my program:

    long * src;
    long * dst;
    ...
    memcpy(dst, src, len); 
    

    My friend changes this to

    memcpy((char *)dst, (char *)src, len).
    

    This is just example I encountered. Generally, I am confused when cast is required?

Jonathan Leffler
  • 666,971
  • 126
  • 813
  • 1,185
  • 3
    This seems like a good question, but unfortunately is *way* too broad, and even somewhat opinion-based. – Drew McGowen Aug 13 '14 at 19:37
  • 1
    `int` and `uint16_t` aren't the same thing. `int` is usually (but _may not be_) a 32-bit signed integer, while `uint16_t` is guaranteed to be an unsigned 16-bit integer. – Colonel Thirty Two Aug 13 '14 at 19:39
  • 3
    Also, not only is the `memcpy` cast unnecessary, but incorrect. `memcpy` takes a `void*` pointer. – Colonel Thirty Two Aug 13 '14 at 19:40
  • I hate that this question actually asks 2 questions, but I think the one about when to use uint*_t is good. Nothing immediately comes up when googling it or in related SO questions, which surprised me. Somewhat recently I actually had an intern who decided it was best to declare everything as `[u]int32_t`, so it's apparently a problem that exists and should have an authoritative answer... – indiv Aug 13 '14 at 19:41
  • 2
    The first question is a duplicate of [this one](http://stackoverflow.com/q/6144682/166749) (except that it asks about C++, but the answer is identical). – Fred Foo Aug 13 '14 at 19:43
  • @Colonel Thirty Two Agree cast not needed in `memcpy()`. But it is not incorrect. `memcpy((char *)dst, (char *)src, len)` creates valid code. Sure, code could be `memcpy((void *)dst, (void *)src, len)`, but there is nothing more correct about casting to `void *` vs. `char *`. IAC, best to do `memcpy(dst, src, len)`. – chux - Reinstate Monica Aug 13 '14 at 19:59
  • 1
    @larsmans This post should _not_ be considered a duplicate for it is tagged `C` and not `C++`. Hence a user may not find your proposed duplicate looking for a `C` problem. Now if that answer was also tagged `C`, it would be a different story. But as many say: `C` and `C++` are different languages. – chux - Reinstate Monica Aug 13 '14 at 20:36
  • @chux I didn't vote to close. But questions can be edited, and C and C++ are compatible in many aspects, and this is one of the aspects. – Fred Foo Aug 14 '14 at 08:18
  • @larsmans I agree `uint16_t` vs. `int` concerns are both valid in C and C++, but the languages offer different solutions. In C++, code can readily make `foo(unitn16_t)` and `foo(int)` to deal with issues. In C, that is not available and code is left with `foo_unitn16_t(unitn16_t)` and `foo_int(int)`, not nearly so elegant. Summary: problem applies to both, but solutions _may_ differ. – chux - Reinstate Monica Aug 14 '14 at 14:41

4 Answers4

3
  1. Use the plain types (int etc) except when you need a precisely-sized type. You might need the precisely sized type if you are working with a wire protocol which defines that the size field shall be a 2-byte unsigned integer (hence uint16_t), but for most work, most of the time, use the plain types. (There are some caveats to this, but most of the time, most people can work with the plain types for simple numeric work. If you are working to a set of interfaces, use the types dictated by the interfaces. If you're using multiple interfaces and the types clash, you'll have to consider using casting some of the time — or change one or both interfaces. Etc.)

  2. The casts added by your friend are pointless. The actual prototype of memcpy() is:

    void *memcpy(void * restrict s1, const void * restrict s2, size_t n);
    

    The compiler converts the long * values to void * (nominally via char * because of the cast), all of which is almost always a no-op.

    More generally, you use a cast when you need to change the type of something. One place you might need it is in bitwise operations, where you want a 64-bit result but the operands are 32-bit and leaving the conversion until after the bitwise operations gives a different result from the one you wanted. For example, assuming a system where int is 32 bits and long is 64 bits.

    unsigned int  x = 0x012345678;
    unsigned long y = (~x << 22) | 0x1111;
    

    This would calculate ~x as a 32-bit quantity, and the shift would be performed on a 32-bit quantity, losing a number of bits. By contrast:

    unsigned long z = (~(unsigned long)x << 22) | 0x1111;
    

    ensures that the calculation is done in 64-bit arithmetic and doesn't lose any bits from the original value.

Jonathan Leffler
  • 666,971
  • 126
  • 813
  • 1,185
1

The size of "classical" types like int and long int can vary between systems. This can cause problems when, for example, accessing files with fixed-width data structures. For example, int long is currently a 64-bit integer on new systems, but only 32 bits on older systems.

The intN_t and uintN_t types were introduced with C99 and are defined in <inttypes.h>. Since they explicitly specify the number of bits, they eliminate any ambiguity. As a rule, you should use these types in preference if you are at all concerned about making your code portable.

Wikipedia has more information

r3mainer
  • 21,407
  • 3
  • 39
  • 74
  • 2
    `int` is currently 16-bit on 100s of millions of embedded processors in 2014. – chux - Reinstate Monica Aug 13 '14 at 19:48
  • I've never encountered a system where `int` denotes 64-bit integer. Also, the types are in `` too, which is likely to be a slimmer header because it doesn't need to define all the `printf`/`scanf` macros in ``. – Fred Foo Aug 13 '14 at 19:51
  • oops, I meant `long` :-( – r3mainer Aug 13 '14 at 20:01
  • 1
    @larsmans: I have (Cray vector systems). In any case, the sizes of `int` and `long` aren't really a property of the *age* of a system. – Keith Thompson Aug 13 '14 at 20:07
  • @KeithThompson Interesting. Then how did you get a 32-bit integer on such a system? (And if it was with `short`, how did you get a 16-bit one?) – Fred Foo Aug 14 '14 at 12:21
  • @larsmans: On the Cray T3E (Alpha based, not a vector system), `char` is 8 bits, `short` is 32 bits, `int` and `long` are 64 bits. On vector systems like the T90, `char` is 8 bits and `short` and wider types are 64 bits. To answer your question: you don't. (These systems were heavily optimized for massive floating-point operations.) – Keith Thompson Aug 14 '14 at 14:57
0

If you do not want to rely on your compiler use predefined types provided by standard library headers. Every C library you'd compile with is guaranteed to assign proper types to have at least size to store values of size their types declare.

In your friend specific case one can assume that he made this type cast just because he wanted to point other readers that two pointers actually hold symbol characters. Or maybe he is kind of old-fashion guy who remembers the times when there was no void type and the "lowest common divisor" was pointer to char. In my developer life, if I want to emphasize some of my actions I'll make an explicit type cast even if it is, in fact, redundant.

mesmerizingr
  • 1,377
  • 1
  • 17
  • 25
-3

For you 1st question, look at : https://stackoverflow.com/questions/11786113/difference-between-different-integer-types

Basically, the _t is the real standard type name and without, it's a define of the same type. the u is for unsigned which doesn't allow negative number.

As for your second question, you often need to cast when the function called needs arguments of another type that what you're passing. You can look here for casting tips, or here...

Community
  • 1
  • 1