I know sometimes compilers remove unused arrays.
But my question is do the affect on dynamic variables which are allocated using malloc or just the stack variables ?
Is malloc a compile time operation or runtime?
If it is runtime can compiler remove an array which is allocated using malloc or it can only remove the arrays which are fixed size ?
The compiler is allowed to remove malloc and its family because memory allocation is not an observable behavior.
For example, both gcc and clang optimize these functions to just return 42 with -O2:
int foo(){
malloc(10);
return 42;
}
int bar(){
int* p = (int*)malloc(10);
*p = 17;
return 42;
}
int qax(){
int* p = (int*)malloc(10);
*p = 17;
int a = *p + 25;
free(p);
return a;
}
Even a more complex one is successfully optimized to return 42 by clang:
void bar(int* xs){
for (int i = 0; i < 10; i++){
xs[i] = i + 35;
}
}
int foo(){
int* xs = (int*)malloc(40);
bar(xs);
return xs[7];
}
But you should not expect much: such optimizations are unusual and, in general, unreliable.
As you have changed the question:
variables with the external linkage will not be optimized out. Other potentially yes. They are not "removed" - they are optimised out - ie they do not exist in the compiled code
I think it is self explanatory.
Related
let's imagine I have this dynamically allocated 2D array:
//Example of a 3 row * 2 columns int array
int (*arr)[2] = malloc(sizeof(int[3][2]));
However, then I found that if I do:
arr[0][5] = 1;
The compiler does not complain, and at least testing with valgrind, it neither complains. It doesn't unless I try to access to a space which exceeds the size of the allocated space.
I found that the same happens for automatic arrays:
int arr[3][2];
arr[0][5] = 1; //Code works without errors
My question now is: what's the point of having for example declared: int arr[3][2]; if the compiler will accept arr[0][5] = 1; anyway?
I'm using GCC compiler
In general, don't write past the bounds of memory that you've allocated.
Clang will warn about both examples by default, while GCC will warn about neither without the variables actually being used (that's the fault of the dead code eliminator). You can enable the warning with -O2 -Wall -Wextra if the variable is used or is declared volatile.
With GCC and Clang it's sort of "safe" to do this; the same thing will happen each time.
However, this is undefined behavior, so it's a bad idea. It's entirely valid for a program that does this to make your computer grow legs and walk away.
An equivalent way of doing the assignment would be:
arr[2][1] = 1;
This goes based on the assumption that the array elements are stored sequentially in memory.
So, &arr[5][0] is technically the same as &arr[2][1], but it shouldn't be used.
My advice:
int arr[3][2];
int x, y;
for( x = 0; x < 3; x++ )
for( y = 0; y < 2; y++ )
arr[x][y] = x * y;
This is guaranteed to be safe.
In my pc Gcc 8.1.0
#include <stdio.h>
#include <stdlib.h>
int main(){
int i,j;
int (*arr)[2] = malloc(sizeof(int[3][2]));
printf("%p %d %d\n",arr,sizeof(int),sizeof(int[3][2]));
//in my computer print
//00C63E38 4 24
//legal memory from 00C63E38~00C63E4C
for(i=0;i<3;i++){
for(j=0;j<2;j++){
printf("%p ",&arr[i][j]);
}
printf("\n");
}
//00C63E38 00C63E3C
//00C63E40 00C63E44
//00C63E48 00C63E4C
printf("------------------\n");
for(i=0;i<3;i++){
for(j=0;j<2;j++){
printf("%p ",*(arr+i)+j);
}
printf("\n");
}
//00C63E38 00C63E3C
//00C63E40 00C63E44
//00C63E48 00C63E4C
//So arr[i][j] is equel *(arr+i)+j
printf("-------------\n");
for(i=0;i<6;i++){
printf("%p ",arr+i);
printf("\n");
}
printf("-------------\n");
//jump 4*2 pointer address per loop from 00C63E38
//00C63E38
//00C63E40
//00C63E48
//00C63E50
//00C63E58
//00C63E60
for(i=0;i<6;i++){
printf("%p ",arr[0]+i);
printf("\n");
}
//jump 4 pointer address per loop from 00C63E38
//00C63E38
//00C63E3C
//00C63E40
//00C63E44
//00C63E48
//00C63E4C
free(arr);
return 0;
}
The example is taken from Wikipedia:
void updatePtrs(size_t *restrict ptrA, size_t *restrict ptrB, size_t *restrict val)
{
*ptrA += *val;
*ptrB += *val;
}
I call this function in the main():
int main(void)
{
size_t i = 10;
size_t j = 0;
updatePtrs(&i, &j, &i);
printf("i = %lu\n", i);
printf("j = %lu\n", j);
return 0;
}
The val pointer is not be loaded twice according to the Wikipedia's description, so the value of j should be 10, but it's 20 in fact.
Is my comprehension about this keyword not correct? Should I utilize some specific options of gcc?
Thanks in advance.
Your code causes undefined behaviour. restrict is a promise from you to the compiler that all of the pointer parameters point to different memory areas.
You break this promise by putting &i for two of the arguments.
(In fact, with restrict it is allowed to pass overlapping pointers, but only if no writes are done through any of the overlapping pointers within the function. But typically you would not bother with restrict if there is no writing happening).
FWIW, on my system with gcc 4.9.2, output is j = 20 at -O0 and j = 10 at -O1 or higher, which suggests that the compiler is indeed taking note of the restrict. Of course, since it is undefined behaviour, your results may vary.
Ok, I understand that my title might be a bit confusing, but I'll explain. I'm working on a homework assignment in C. I'm given a .c file and need to come up with implementations for some functions.
In short, I have this as a .c file
typedef int set_t;
...
void init(set_t *a, int N); // Initialized an array to a of size N
...
int main() {
set_t a;
init(&a, 10);
}
In a couple of implementations I've come up with, I was able to create an array using a, but I keep getting segmentation faults when the program runs :-/. Is there away to initialize a as an array without changing anything in the original .c file except for the implementation of init(set_t *a, int N)?
EDIT
Here's my current implementation of init --> it leads to a segmentation fault
void init(set_t *a, int N) {
//set_t thing[10];
*a = malloc(sizeof(set_t)*N);
for (int i = 0; i < N; i++) {
*(a + i) = i;
}
printf("value of a[2] = %d\n", a[2]);
}
As things currently stand, the requirements imposed on you are wholly unreasonable. If you are building for 32-bit only, so sizeof(int) == sizeof(int *), then you can use brutal casting to get around the constraints. The code will not work on a 64-bit machine, though (unless sizeof(int) == sizeof(int *), which isn't the case on any machine I can immediately think of.
So, the brute force and casting technique is:
void init(set_t *a, int N)
{
assert(sizeof(set_t) == sizeof(set_t *)); // Ick, but necessary!
set_t *base = malloc(sizeof(set_t)*N);
if (base == 0)
*a = 0;
else
{
*a = (int)base; // Brutal; non-portable; stupid; necessary by the rules given!
for (int i = 0; i < N; i++) {
base[i] = i;
printf("value of a[2] = %d\n", base[2]);
printf("value of a[2] = %d\n", ((int *)*a)[2]); // Brutal and stupid too
}
}
Further, in the code in main(), you'll have to use ((int *)a) to make the type usable for dereferencing, etc. Without knowing about what is actually in that other code, it is impossible to be confident that anything will work. It might, but it probably won't.
At this stage, this looks like someone criminally misleading innocent novice programmers. This is not the way it should be coded at all. However, if that's what the doctor (professor) orders, then that's what you've got to do. But it is a mockery of good coding practices AFAICS and AFAIAC.
Professor realized that he had made an error in the assignment and fixed it. Changed set_t a to set_a *a.
Thanks for all your help (hope I didn't cause too many headaches!
Platform: Linux 3.2.0 x86 (Debian Wheezy)
Compiler: GCC 4.7.2 (Debian 4.7.2-5)
I am writing a function that copies the contents of a buffer to another buffer. I use a void pointer so that the function is not type specific. I have a testable version and it appears that the function is working properly. But I do not know if what I am doing is legal so my question is what are the pitfalls of what I have done if there are any.
#include <stdio.h>
#include <stdlib.h>
void* voidcpy(void *void_ptr, size_t nbytes)
{
char *char_ptr = void_ptr;
char *cpy_char_ptr = NULL;
size_t i = 0;
if((cpy_char_ptr = malloc(nbytes)) == NULL) return NULL;
for(; i < nbytes; i++) cpy_char_ptr[i] = char_ptr[i];
return cpy_char_ptr;
}
int main()
{
short int *intp = NULL;
short int *cpy_intp = NULL;
size_t siz = 5;
int i = 0;
if((intp = malloc(siz * sizeof(short int))) == NULL)
{
perror("(malloc)");
return -1;
}
intp[0] = 0;
intp[1] = 14;
intp[2] = 187;
intp[3] = 12678;
intp[4] = -234;
if((cpy_intp = voidcpy(intp, siz * sizeof(short int))) == NULL)
return -2;
printf("intp = %p\ncpy_intp = %p\n\n", (void*)intp, (void*)cpy_intp);
for(; i < siz; i++) printf("cpy_intp = %i\n", cpy_intp[i]);
free(intp);
free(cpy_intp);
return 0;
}
Yes, this is perfectly legal, in C you can legally assign a void pointer to any other pointer type and you can assign any pointer type to a void pointer.
In C++ this is not allowed. In C++ you would have to use a reinterpret_cast to cast to a different pointer type because the free void pointer casting that is allowed in C is considered a "loop hole" that is easy to make mistakes with.
Of course there is a truth to that idea, if you're not careful you could be doing the wrong thing, e.g. you could easily pass a pointer to a pointer to this function by mistake and then your function will happily overwrite whatever is on the stack beyond that pointer. This is no fault of your implementation however, it's just the way the function is to be used and memcpy behaves no differently.
Nevertheless you would be better off using memcpy instead as this will very likely be much better optimized, though these days the compiler will probably make a pretty decent version out of your code as well.
A few more pointers;
1) you do not need to malloc the original array, you can initialize it statically like so
short int int_arr[] = {
0,
14,
187,
12678,
-234,
};
2) you can then call your function in the following way:
cpy_int_arr = voidcpy(int_arr, sizeof(int_arr));
3) if you don't want to define the array statically, then use the pointer to get the element size, that way you can change the array type without needing to change it somewhere else in the code, which reduces the potential dangers of the "loop hole" void casting:
cpy_intp = voidcpy(intp, siz * sizeof(*intp));
4) you don't need to cast to void* in the printf call
5) try to assign variables immediately and do not put assignments inside of if statements:
char *cpy_char_ptr = malloc(nbytes)
if (cpy_char_ptr == NULL)
return NULL;
6) similarly you can define a iteration variable inside the loop clause:
for(size_t i = 0; i < nbytes; i++) cpy_char_ptr[i] = char_ptr[i];
The reason to define variables as late as possible and initialize them immediately is that you keep the scope of the variables as small as possible and you can not mistakenly use a variable before it is initialized.
7) (personal preference) don't use type names in your identifiers (intp, voidcpy) your code will either become difficult to read/understand if your identifier states a type different from what the variable actually is, (e.g. your type is actually a short int and not an int as the variable name would suggest,) or you will need to change the identifier throughout the entire code whenever you change the type with the possibility of making mistakes.
I wrote a little little program in C, and I expected an error with vectors.
#include <stdlib.h>
#include <stdio.h>
main(){
int vet[0];
vet[0] = 1;
vet[1] = 2;
vet[2] = 2;
vet[3] = 2;
vet[4] = 2;
vet[5] = 2;
vet[6] = 2;
vet[7] = 2;
int x;
for(x=0;x<19;x++) printf("%d\n", vet[x]);
system("pause");
}
Before writing this code I thought that to add an element to a vector, moreover his size, I should call the malloc function, but then I discovered that also without the malloc function the code works fine. So my question is, why should I use the malloc function if in this case it's useless? And why should I write the dimension of a vector when I initialize it ?
Because, by writing to memory you never allocated, you are causing undefined behaviour. Sometimes it works, sometimes it doesn't, sometimes it makes your dog explode.
That's cute, but let's change the program a little by adding another such "vector":
#include <stdio.h>
int main(void){
int vet[0];
int vex[0];
vet[0] = 1;
vet[1] = 2;
vet[2] = 2;
vet[3] = 2;
vex[0] = 3;
vex[1] = 3;
vex[2] = 3;
vex[3] = 3;
int x;
for(x=0;x<4;x++) printf("%d\n", vet[x]);
}
I guess you could expect the program to print 1 2 2 2. I actually get 1 3 2 3. What do you get?
Unexpected things like this happen when you use something that causes "undefined behavior."
You should define array like;
int vet [8];
Otherwise it will invoke undefined behavior (which is the case in your program) as memory is not allocated for vet.
And also the loop
for(x=0;x<19;x++)
is going out of bounds which also invokes undefined behavior. Therefore it should be
for(x=0;x<8;x++)
Also change main definition to int main() or int main(void).
Standard C doesn't even permit zero sized arrays, not even for variable length arrays. It's undefined behavior. And you try to access out of array boundary, another undefined behavior.
In another word, it may happen to work here and it may not be working in another machine.