Reputation: 155
Let's say we have the following two pieces of code:
int *a = (int *)malloc(sizeof(*a));
int *b = (int *)malloc(sizeof(*b));
And
int *a = (int *)malloc(2 * sizeof(*a));
int *b = a + 1;
Both of them allocate two integers on the heap and (assuming the normal usage) they should be equivalent. The first seems to be slower as it calls malloc twice and can result in a more cache-friendly code. The second however is possibly insecure as we can accidentally override the value of what b points to just by incrementing a and writing to the resulting pointer (or someone malicious can instantly change the value of b just by knowing where a is).
It's possible that the above claims are not true (for example the speed is questioned here: Minimizing the amount of malloc() calls improves performance?) but my question is just: Can the compiler do this type of transformation or is there something fundamentally different between the two according to the standard? If it is possible, what compiler flags (let's say gcc) can allow it?
Upvotes: 0
Views: 761
Reputation: 597530
In reality, no, the compiler will never combine the 2 malloc()
calls into a single malloc()
call automatically. Each call to malloc()
returns the address of a new memory block, there is no guarantee that the allocated blocks will be located anywhere close to each other, and each allocated block must be free()'d
individually. So no compiler will ever assume anything about the relationship between multiple allocated blocks and try to optimize their allocations for you.
Now, it is possible that in a very simplified use-case, where the allocation and deallocation were in the same scope, and if it can be proven to be safe to do so, then the compiler vendor might decide to try to optimize, ie:
void doIt()
{
int *a = (int *)malloc(sizeof(*a));
int *b = (int *)malloc(sizeof(*b));
...
free(a);
free(b);
}
Could become:
void doIt()
{
void *ptr = malloc(sizeof(int) * 2);
int *a = (int *)ptr;
int *b = a + 1;
...
free(ptr);
}
But in reality, no compiler vendor will actually attempt to do this. It is not worth the effort, or the risk, for such little gain. And it would not work in more complex scenarios anyway, eg:
void doIt()
{
int *a = (int *)malloc(sizeof(*a));
int *b = (int *)malloc(sizeof(*b));
...
UseAndFree(a, b);
}
void UseAndFree(int *a, int *b)
{
...
free(a);
free(b);
}
Upvotes: 3
Reputation: 1647
I've done exactly that with a bignum library, but you only free the one pointer.
//initialization every time program runs
extern bignum_t *scratch00; //these are useful for taylor series, etc.
extern bignum_t *scratch01;
extern bignum_t *scratch02;
.
.
.
bignum_t *bn_malloc(int bignums)
{
return(malloc(bignums * bn_numbytes));
}
.
.
.
//bignums specific to the program being written at the moment
bignum_t *numerator;
bignum_t *denom;
bignum_t *denom_add;
bignum_t *accum;
bignum_t *term;
.
.
.
numerator = bn_malloc(1);
denom = bn_malloc(1);
denom_add = bn_malloc(1);
accum = bn_malloc(1);
term = bn_malloc(1);
Upvotes: 0
Reputation: 211690
There's a number of reasons why this will likely never happen, but the most important is lifetimes where these allocations, if made independently, can be freed independently. If made together they're locked to the same lifetime.
This sort of nuance is best expressed by the developer rather than determined by the compiler.
Is the second "insecure" in that you can overwrite values? In C, and by extension C++, the language does not protect you from bad programming. You are free to shoot yourself in the foot at any time, using any means necessary:
int a;
int b;
int* p = &a;
p[1] = 9; // Bullet, meet foot
(&b)[-1] = 9; // Why not?
If you want to allocate N of something by all means use calloc()
to express it, or an appropriately sized malloc()
. Doing individual allocations is pointless unless there's a good reason.
Normally you wouldn't allocate a single int
, that's kind of useless, but there are cases where that might be the only reasonable option. Typically it's larger blocks of things, like a full struct
or a character buffer.
Upvotes: 2
Reputation: 67835
First of all:
int *a = (int *)malloc(8);
int *b = a + 4;
Is not what you think. You want:
int *a = malloc(sizeof(*a) * 2);
int *b = a + 1;
It shows that pointer arithmetic is something you need to learn.
Secondly: the compiler does not change anything in your code, and it will not combine any function calls in one. What you try to achieve is a micro-optimization. If you want to use a larger chunk of memory simply use arrays.
int *a = malloc(sizeof(*a) * 2);
a[0] = 5;
a[1] = 6;
/* some other code */
free(a);
Do not use "magic" number is malloc only sizeof
of the objects. Do not cast the result of malloc
Upvotes: 1
Reputation: 73219
No, it can't, because the compiler (in general) doesn't know when a
and b
might get free()
'd, and if it allocates them both as part of a single allocation, then it would need to free()
them both at the same time also.
Upvotes: 3