2

I know about this: Do I cast the result of malloc?

I read it and other questions, and I still haven't satisfied my concerns. I understand that the type cast will occur implicitly, but the error is something I do not understand no matter how much I read these arguments.

The main argument is that int and pointer may have different sizes. For the sake of an example, let size of int* be 8 and size of int be 4.

int *x = (int*) malloc(sizeof(int));

First off, the number of bytes allocated by malloc() is 4, although the variable x will store a pointer of size 8 bytes.

I'm explicitly casting the return of malloc to (int *), which — by the way — has size 8. How come there's any loss of bits here?

Community
  • 1
  • 1
  • 1
    What error? I see nothing in the above code that would cause a warning or error in C. Note the lack of errors or warnings here: https://ideone.com/jMccon – Paul Roub Feb 11 '16 at 17:00
  • 1
    @Judismar Junior I have not uderstood what you are asking. – Vlad from Moscow Feb 11 '16 at 17:03
  • The first answer of the question you mention is pretty explicit. – Jabberwocky Feb 11 '16 at 17:03
  • What loss of bits do you mean? In your example, `x` has size 8 since it is an `int*`, but `*x` (or `x[0]`) has size 4, since it is an `int`. In other words, you have a 64 bit pointer to a 32 bit memory region. I think you may be mixing up the pointer, and what it points to? – Kenney Feb 11 '16 at 17:05
  • 2
    The linked answer says that **if** you forget to include stdlib.h, which is a fatal error, **then** the cast masks this error. What happens next is classified as undefined behaviour, which **may** manifest itself as bits being truncated off the returned address (or anything else). OTOH if you don't have a habit of casting the return value of malloc, then failure to include stdlib.h results in a mere compilation error. – n. m. could be an AI Feb 11 '16 at 17:13
  • @n.m. you mean, in other words, that casting explicitly is a way to "correct" the error (originally generated by the fatal error of not including stdlib), which would mask it? So it would be only "necessary" if the programmer doesn't include the library? If yes and yes, I think I got it this time... – Judismar Arpini Junior Feb 11 '16 at 17:39
  • @n.m., ignore this question. I read the linked question again, now I got it. Thank you very much. – Judismar Arpini Junior Feb 11 '16 at 17:50
  • 2
    @JudismarJunior If the linked question has answered your question, you might want to accept this question as duplicate by clicking the button at the top of your question. – Tunaki Feb 11 '16 at 18:38

4 Answers4

3

If you haven't #included 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;
R Sahu
  • 204,454
  • 14
  • 159
  • 270
  • 4
    "the return value of malloc is truncated". The result is simply undefined, which may involve truncating the return value and/or blowing up a nuclear power plant. – n. m. could be an AI Feb 11 '16 at 17:07
  • 1
    @JudismarJunior, type-casting won't solve the problem. In fact it hides the problem until run time instead of warning you at compile time. That's why it is recommended that you don't type-cast the return value of `malloc`. – R Sahu Feb 11 '16 at 17:53
2

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.

Jay Kumar R
  • 537
  • 2
  • 7
  • *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.* No. Read [**6.2.5 Types**, paragraph 28](http://www.open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf) of the C Standard: A pointer to void shall have the same representation and alignment requirements as a pointer to a character type. Similarly, pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements. – Andrew Henle Feb 11 '16 at 17:20
  • 2
    (cont) All pointers to structure types shall have the same representation and alignment requirements as each other. All pointers to union types shall have the same representation and alignment requirements as each other. **Pointers to other types need not have the same representation or alignment requirements.** See http://stackoverflow.com/questions/1473935/can-the-size-of-pointers-vary-between-data-and-function-pointers for examples. – Andrew Henle Feb 11 '16 at 17:20
  • 2
    Your story is misleaing. Different pointer types **may** have different sizes and/or alignment requirements. The standard does not preclude that. It also fails to answer the question. – n. m. could be an AI Feb 11 '16 at 17:21
  • @n.m. You are unable to comprehend. My story is not misleading. If you worry about rent/city tax paid by the house address that are not applicable for C address/pointers, yes my story is misleading. – Jay Kumar R Feb 11 '16 at 17:43
  • @AndrewHenle, prove that sizeof ( a pointer) (not array) is different in case of different types. – Jay Kumar R Feb 11 '16 at 17:47
  • @JayKumarR *prove that sizeof ( a pointer) (not array) is different in case of different types.* The proof is in the links I've already provided. Read the standard - I linked to it, provided the location, and actually quoted the relevant portions. The fact that you may only know of implementations where all pointers are the same size does not make your claim that all pointers are the same size true in general. I provided a link to a question that provides numerous examples - and that page provides numerous links to lots of other examples. Read them. – Andrew Henle Feb 11 '16 at 20:00
1

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

#include <stdio.h>
int main(int argc, char ** argv) {
  printf("sizeof(int) = %zu\nsizeof(int *) = %zu\n",
    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) = %zu\nsizeof(int *) = %zu\n",
    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) = %zu\nsizeof(int *) = %zu\n",
    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 :)

Community
  • 1
  • 1
Daniel Jour
  • 15,896
  • 2
  • 36
  • 63
0

note that in "int *x" the x variable has been allocated as soon as its definition. It is a pointer, i.e. it contains an address inside the heap. However its value is meaningless before an assignment; just like any other variable. malloc(n) ensures that n bytes at the address whose value is returned are allocated in the memory. In other words, x has size 8, but x[0] has size 4 and the malloc(4) allocates enough memory for storing x[0]. A good practice for seeing their difference would be to compare &x and &x[0]. The latter will be equal to the x.

AliVar
  • 159
  • 5