Type casting in malloc

This question already has an answer here:

  • Do I cast the result of malloc? 27 answers

  • If you haven't #include d stdlib.h , the return value of malloc is most likely truncated before it gets returned to the calling code. In theory, it is undefined behavior. If the return value of malloc is truncated, it's analogous to using:

    int a = 10;
    int* ap = &a;
    int temp = (int)ap;    // This is where you lose the pointer value due to truncation.
    int* bp = (int*)temp;
    

    Your house is say 50 ft wide. Your house address is '40, Church st, Newtown'. Say there is a huge store like walmart or a school next to your house. That is 450 ft wide. But the address is '50, Church St, Newtown' .

    Do you require different sized paper to write these addresses because one is bigger than other? of course not. A pointer is synonymous to address. it is location. What stored there (in your case int of 4 bytes) does not change the address length.

    so at the end, x will have size of 8 and will be pointing to a 4 byte size.


    Let's see what could happen. Assume this code:

    #include <stdio.h>
    int main(int argc, char ** argv) {
      printf("sizeof(int) = %zunsizeof(int *) = %zun",
        sizeof(int), sizeof(int *));
      // output:
      // sizeof(int) = 4
      // sizeof(int *) = 8
      int * foo = (int *) malloc(sizeof(int));
      return 0;
    }
    

    Compiling this with clang ouch.c on my system gives:

    ouch.c:5:23: warning: implicitly declaring library function 'malloc' with type 'void *(unsigned long)'
      int * foo = (int *) malloc(sizeof(int));
                          ^
    ouch.c:5:23: note: include the header <stdlib.h> or explicitly provide a declaration for 'malloc'
    

    Here, clang is smart enough to notice that I'm calling malloc , which is a known library function, and assumes the (correct) function signature void *(unsigned long) . So everything's fine. But not every compiler is that smart, and I can also trick clang:

    #include <stdio.h>
    int main(int argc, char ** argv) {
      printf("sizeof(int) = %zunsizeof(int *) = %zun",
        sizeof(int), sizeof(int *));
      int * foo = (int *) wrapper();
      return 0;
    }
    

    And in a separate file, which I'll link to the main file above:

    #include <stdlib.h>
    void * wrapper(void) {
      return malloc(sizeof(int));
    }
    

    Running clang wrapper.c ouch2.c gives me:

    ouch2.c:5:23: warning: implicit declaration of function 'wrapper' is invalid in C99 [-Wimplicit-function-declaration]
      int * foo = (int *) wrapper();
                          ^
    ouch2.c:5:15: warning: cast to 'int *' from smaller integer type 'int' [-Wint-to-pointer-cast]
      int * foo = (int *) wrapper();
                  ^
    2 warnings generated.
    

    Which is pretty good, since if these warnings are read, then it's pretty easy to understand the source of the issue and fix them. But if I ignore them and keep the code like it is, the following happens:

    When compiling ouch2.c , clang does not see any declaration for wrapper . Since I removed it's smart library function detection from the loop it has no choice but to assume that somewhere this is declared as

    int wrapper();
    

    That's a function returning an int and taking any number of arguments. We see proof of that because clang (being an intelligent compiler) warns me about the cast from the (returned) int to int * with its second warning.

    Casting that int to an int * is not what's the bad thing here. What is bad is assuming that we get an int in the first place. Assume the call to malloc in the wrapper function returned this value:

    0xAABBCCDD11223344
    

    What happens then depends on the calling convention. Let's just assume that it puts this value as return value into some 64 bit register.

    The calling code in main expects an int , so it reads only 32 bits from the register (probably the lower half) and uses that. So in main , I get from wrapper this:

    0x11223344
    

    This is then cast to an (64 bit) int * , possibly resulting in:

    0x0000000011223344
    

    which is then used as the memory address. Accessing this address will probably (if you're lucky) result in a segmentation fault or (if you're not so lucky) change some random data (this is especially fun if it happens on the stack, changing for example a return address).

    So, last but not least, if I leave the cast out:

    #include <stdio.h>
    int main(int argc, char ** argv) {
      printf("sizeof(int) = %zunsizeof(int *) = %zun",
        sizeof(int), sizeof(int *));
      int * foo = wrapper();
      return 0;
    }
    

    And compile that with clang wrapper.c ouch3.c I get:

    ouch3.c:5:15: warning: implicit declaration of function 'wrapper' is invalid in C99 [-Wimplicit-function-declaration]
      int * foo = wrapper();
                  ^
    ouch3.c:5:9: warning: incompatible integer to pointer conversion initializing 'int *' with an expression of type 'int' [-Wint-conversion]
      int * foo = wrapper();
            ^     ~~~~~~~~~
    2 warnings generated.
    

    Also a warning, but a different warning. This (kind of) warning is a lot more likely to be produced by your compiler.

    Long story short: clang does a great job warning about potential errors and don't cast the return value, because then you can be certain to get a warning if you forget to include stdlib.h, even when not compiling with clang :)

    链接地址: http://www.djcxy.com/p/28368.html

    上一篇: malloc()函数的返回

    下一篇: 在malloc中输入类型