Reputation: 860
See the following code snippet:
int len = -2;
char* buff = (char*) malloc(len+4);
if (len > sizeof(buff))
puts("ERROR!");
else
puts("OK!");
Compiling and running this code on Ubuntu-14.04 (64-bit) using GCC 4.8.2 prints ERROR
.
I used the following code to print the values of len
and sizeof(buf)
:
printf("len = %d, size = %lu", len, sizeof(buff));
and it prints:
len = -2, size = 8
Changing the value of len
has no effect on the value of sizeof(buff)
, not even for a positive len
.
If I'm not mistaken, the value 8
is the length of a pointer address on my 64-bit machine, which is constant no matter what I give to malloc
. If this is it, I have two questions:
1) Why is the above if
statement printing ERROR
? (Since -2 is not greater than 8 !!)
2) Why doesn't the following code print 8
?
char array[10];
printf("%lu", sizeof(array));
This code prints the length of the array. What's the difference between a char[]
and char*
from malloc
? I know that the first is allocated on the stack, and the latter is dynamically allocated on heap, but in any case they are both pointer addresses of the system memory. I don't understand the different behavior of sizeof
relative to a char[]
and char*
from malloc
! It seems inconsistent!
Upvotes: 1
Views: 118
Reputation: 310990
1) Why is the above if statement printing ERROR? (Since -2 is not greater than 8 !!)
In the condition expression of the if statement
if (len > sizeof(buff))
puts("ERROR!");
type size_t
(usually defined as unsigned long
) that corresponds to the type of the returned value of operator sizeof
has higher rank than the type of variable len that has type int
. So to get the common type len is converted to type size_t
according to the rules of the usual arithmetic conversions and considered as unsigned integral value that is greater than the value returned by sizeof( buff ).
[Note: internal representation of -2
can look (I will use only a byte for simplicity) like
11111110
while of 8
it looks like
00001000
Thus if to consider the internal representation of -2
as some unsigned value then it is obvious that -2
is greater than 8
. - end Note]
From the C Standard (6.3.1.8 Usual arithmetic conversions)
1 Many operators that expect operands of arithmetic type cause conversions and yield result types in a similar way. The purpose is to determine a common real type for the operands and result. For the specified operands, each operand is converted, without change of type domain, to a type whose corresponding real type is the common real type. Unless explicitly stated otherwise, the common real type is also the corresponding real type of the result, whose type domain is the type domain of the operands if they are the same, and complex otherwise. This pattern is called the usual arithmetic conversions:
And
Otherwise, if both operands have signed integer types or both have unsigned integer types, the operand with the type of lesser integer conversion rank is converted to the type of the operand with greater rank.
Otherwise, if the operand that has unsigned integer type has rank greater or equal to the rank of the type of the other operand, then the operand with signed integer type is converted to the type of the operand with unsigned integer type.
If you would for example define len
as having type long long
then it is posiible that the condition would be equal to false because it could occur that the rank of long long is greater than rank of size_t
that usually is defined as a typedef for type unsigned long
Try the following code snippet
long long int len = -2;
char* buff = (char*) malloc(len+4);
if (len > sizeof(buff))
puts("ERROR!");
else
puts("OK!");
2) Why doesn't the following code print 8?
char array[10];
printf("%lu", sizeof(array));
Operator sizeof
returns in bytes the size of the object used as operand of the operator. Variable array is defined as an array of 10 elements of type char. In any implementation sizeof( char ) is equal to 1. Thus 10 * sizeof( char ) will result in 10.
Take into account that arrays are not pointers.
Upvotes: 2
Reputation: 137810
if (len > sizeof(buff))
sizeof
produces a value of type size_t
, which is unsigned. When you compare it to a negative int
, the negative value is promoted to a very large unsigned value. (Unsigned types always use modular arithmetic, and they trump signed types in binary operations.) So, -2
is "greater than" anything you could get from sizeof
.
Changing the value of len has no effect on the value of sizeof(buff), not even for a positive len.
sizeof(buff)
is the size of the pointer, not the size of the allocation block. You need to save that in your own variable because C doesn't keep track of allocation sizes.
What's the difference between a
char[]
andchar*
frommalloc
?
The char[]
is an array and its size depends on the number of elements in it. The char*
is a pointer. An array may be used in contexts that take a pointer, but that doesn't make it a pointer per se.
sizeof( &* array )
or sizeof( & array[0] )
will both have the same value as sizeof( ptr )
. The size is a property of the variable, not of the memory block.
Upvotes: 4
Reputation: 53006
What is wrong with your code?
int len = -2;
char* buff = (char*) malloc(len+4); /* don't cast malloc not wrong, but might hide bugs. */
if (len > sizeof(buff)) /* sizeof() is unsigned and len is signed */
puts("ERROR!");
else
puts("OK!");
you compare a signed
value with an unsigned
value, and because of unsigned
wrap around
unsigned int x = -1;
then x > 0
is always true, and almost surely x > len + 4
which I assume is what you wanted to compare, but surely x > sizeof(char *)
which is what x > sizeof(buff)
means in your case.
Also, sizeof()
gives the size of the type, in your case since buff
is a pointer, then it's the size of a pointer, make the code work do this
Use gcc
warnings, it will tell you about the signed
unsigned
comparison.
gcc -Wall -Wextra -Werror
notice, -Werror
will treat warnings as errors, aborting compilation when a warning is issued.
If you want to test this, just try it like this
int len = -2;
char* buff = (char*) malloc(len+4); /* don't cast malloc not wrong, but might hide bugs. */
if (len > (int)sizeof(buff)) /* sizeof() is unsigned and len is signed */
puts("ERROR!");
else
puts("OK!");
but remember that sizieof(buff)
in your machine will be 8
regardless of the value of len
, you can't compute the length of a malloc
ed block, you need to store it's length for subsequent use.
If you want, you can create a struct to hold both, the length and the data.
Upvotes: 4