0

All

I am writing a very simple C code of dynamic 2D array declaration and initializing it with memset and then printing out the value. My code is something as follows:

float **env;
int i,j,num;

printf("Enter a number : \n");
scanf("%d",&num);

env = (float **)malloc(num*sizeof(float *));

for(i=0;i<num;i++)
{env[i] = (float *)malloc(num*sizeof(float));}

memset(env, 0, sizeof(float)*num*num);

for(i=0;i<num;i++)
{  for (j=0;j<num;j++)
   {
      printf("%f\t",env[i][j]);
      if (j == num -1)
       { printf("\n\n");}
    }
}

for(i=0;i<num;i++)
{free(env[i]);
}

free(env);

When I compile the program, there is no compile error or warning, but when I try to print out the values I am not able to print them. Then I debugged the program, and after the memset statement the env 2D variable is showing something like CXX0030: Error: expression cannot be evaluated, and when I print the values a window appears showing

Unhandled exception at 0x008b1e27 in ***.exe: 0xC0000005: Access violation reading location 0x00000000.

I have tried explicitly initializing the 2D array env to 0 using 2 for loops and it works perfectly and I am also able to print the values, but it doesn't work when I use memset. It would be very helpful if somebody could help me out. Thank you.

Dabbler
  • 9,733
  • 5
  • 41
  • 64
duttasankha
  • 717
  • 2
  • 10
  • 32

3 Answers3

3

Firstly, the usual advice: stop using type names under sizeof and stop casting the result of malloc. This is what it should've looked like

env = malloc(num * sizeof *env);

for (i = 0; i < num; i++)
  env[i] = malloc(num * sizeof *env[i]);

In general, prefer to write you memory allocations by following this pattern

some_ptr = malloc(N * sizeof *some_ptr);

Secondly, the "array" you create by using this technique is not a classic C-style contiguous 2D array. Instead, you get a "simulated" 2D array assembled from a bunch of completely unrelated 1D arrays. The latter can potentially be scattered randomly in memory. This immediately means that you won't be able to process your array as a continuous object. Meanwhile your memset call attempts to do just that. You can't do it this way. In your case you have to zero each 1D sub-array independently, using a cycle.

However, instead of allocating your 1D sub-arrays independently, you can allocate them all as a single memory block

assert(num > 0);
env = malloc(num * sizeof *env);
env[0] = malloc(num * num * sizeof *env[0]);

for (i = 1; i < num; ++i)
  env[i] = env[i - 1] + num;

If allocated as shown above, you array's data will be stored in a single continuous memory block, although you still have to understand what you are doing when you are working with that array. For example, memseting the array data would look as follows

memset(*env, 0, num * num * sizeof **env);

or

memset(&env[0][0], 0, num * num * sizeof env[0][0]);

(which is the same thing).

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • Thanks for the advice and I included what you have mentioned in my code. So considering what you have mentioned, is there any way of initializing the 2D array to 0 without explicitly using 2 for loops? – duttasankha Nov 03 '12 at 19:13
  • 1
    @duttasankha: See the second part of my answer. It is not possible for your code. But if you change the allocation scheme as shown in my answer, it will become possible. – AnT stands with Russia Nov 03 '12 at 19:15
  • Thanks for your help. But when I have stopped the casting the malloc then I compiler is throwing me error like error : a value of type "void *" cannot be assigned to an entity of type "float **" error : a value of type "void *" cannot be assigned to an entity of type "float *" What can I do about this. I am using microsoft visual studio 2010 – duttasankha Nov 03 '12 at 19:20
  • Actually the program works perfectly well when I did the type casting. What are the disadvantages of doing the typecasting explicitly? – duttasankha Nov 03 '12 at 19:27
  • @duttasankha The answer to this question, for C and for C++, has been discussed to death on stackoverflow. See for instance http://stackoverflow.com/questions/1565496/specifically-whats-dangerous-about-casting-the-result-of-malloc – Pascal Cuoq Nov 03 '12 at 19:32
  • @PascalCuoq : Thanks for the post and help. – duttasankha Nov 03 '12 at 19:33
  • @duttasankha: You tagged your question as [C], while you are compiling your code as C++. If you really have to do it as C++, then you will have to cast the result of `malloc`. – AnT stands with Russia Nov 03 '12 at 19:35
2

Remove the memset and use calloc instead of malloc, it zero-initializes allocated memory. What you're doing now is zeroing pointers you have just allocated.

Michael Krelin - hacker
  • 138,757
  • 24
  • 193
  • 173
2

In addition to Michael's answer, it is important to realize that float **env will not give you a continuous chunk of memory. It will give you a pointer to a continuous chunk of memory, and in each of those memory locations is a pointer to a continuous chunk of memory, but there is no guarantee that those chunks are aligned.

For example doing:

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

x and y are not guaranteed to be continuous in memory, yet this is what your code is assuming.

Austin
  • 1,122
  • 2
  • 10
  • 27