Needless pointer-casts in C

前端 未结 12 662
刺人心
刺人心 2020-12-09 10:05

I got a comment to my answer on this thread:

Malloc inside a function call appears to be getting freed on return?

In short I had code like this:



        
相关标签:
12条回答
  • 2020-12-09 10:51
    #if CPLUSPLUS
    #define MALLOC_CAST(T) (T)
    #else
    #define MALLOC_CAST(T)
    #endif
    ...
    int * p;
    p = MALLOC_CAST(int *) malloc(sizeof(int) * n);
    

    or, alternately

    #if CPLUSPLUS
    #define MYMALLOC(T, N) static_cast<T*>(malloc(sizeof(T) * N))
    #else
    #define MYMALLOC(T, N) malloc(sizeof(T) * N)
    #endif
    ...
    int * p;
    p = MYMALLOC(int, n);
    
    0 讨论(0)
  • 2020-12-09 10:54

    Casting a function which returns (void *) to instead be an (int *) is harmless: you're casting one type of pointer to another.

    Casting a function which returns an integer to instead be a pointer is most likely incorrect. The compiler would have flagged it had you not explicitly cast it.

    0 讨论(0)
  • 2020-12-09 10:54

    On the other hand, if you ever need to port the code to C++, it is much better to use the 'new' operator.

    0 讨论(0)
  • 2020-12-09 10:58

    Well, I think it's the exact opposite - always directly cast it to the needed type. Read on here!

    0 讨论(0)
  • 2020-12-09 10:59

    People have already cited the reasons I usually trot out: the old (no longer applicable to most compilers) argument about not including stdlib.h and using sizeof *p to make sure the types and sizes always match regardless of later updating. I do want to point out one other argument against casting. It's a small one, but I think it applies.

    C is fairly weakly typed. Most safe type conversions happen automatically, and most unsafe ones require a cast. Consider:

    int from_f(float f)
    {
        return *(int *)&f;
    }
    

    That's dangerous code. It's technically undefined behavior, though in practice it's going to do the same thing on nearly every platform you run it on. And the cast helps tell you "This code is a terrible hack."

    Consider:

    int *p = (int *)malloc(sizeof(int) * 10);
    

    I see a cast, and I wonder, "Why is this necessary? Where is the hack?" It raises hairs on my neck that there's something evil going on, when in fact the code is completely harmless.

    As long as we're using C, casts (especially pointer casts) are a way of saying "There's something evil and easily breakable going on here." They may accomplish what you need accomplished, but they indicate to you and future maintainers that the kids aren't alright.

    Using casts on every malloc diminishes the "hack" indication of pointer casting. It makes it less jarring to see things like *(int *)&f;.

    Note: C and C++ are different languages. C is weakly typed, C++ is more strongly typed. The casts are necessary in C++, even though they don't indicate a hack at all, because of (in my humble opinion) the unnecessarily strong C++ type system. (Really, this particular case is the only place I think the C++ type system is "too strong," but I can't think of any place where it's "too weak," which makes it overall too strong for my tastes.)

    If you're worried about C++ compatibility, don't. If you're writing C, use a C compiler. There are plenty really good ones avaliable for every platform. If, for some inane reason, you have to write C code that compiles cleanly as C++, you're not really writing C. If you need to port C to C++, you should be making lots of changes to make your C code more idiomatic C++.

    If you can't do any of that, your code won't be pretty no matter what you do, so it doesn't really matter how you decide to cast at that point. I do like the idea of using templates to make a new allocator that returns the correct type, although that's basically just reinventing the new keyword.

    0 讨论(0)
  • 2020-12-09 10:59

    I think you should put the cast in. Consider that there are three locations for types:

    T1 *p;
    p = (T2*) malloc(sizeof(T3));
    

    The two lines of code might be widely separated. Therefore it's good that the compiler will enforce that T1 == T2. It is easier to visually verify that T2 == T3.

    If you miss out the T2 cast, then you have to hope that T1 == T3.

    On the other hand you have the missing stdlib.h argument - but I think it's less likely to be a problem.

    0 讨论(0)
提交回复
热议问题