3

if we assign a pointer to another pointer, it's called "swap the pointers". For example,

float *temp, *ptr1, *ptr2;
temp = ptr1;
ptr1 = ptr2;
ptr2 = temp;

However, if we assign an array to a pointer, it's illegal because array is not really the pointer type. But look at the next 3 examples:

float arr[]={1.2, 1.9, 3.1};
float *ptr;
int i;
ptr = (float *)calloc(3,sizeof(float));
ptr = arr;
for (i=0;i<3;i++){
    printf("ptr[%d]=%f\n",i,ptr[i]);
}

This code snippet passed the compilation and ran correctly (illegal code gets the correct answer?):

ptr[0]=1.200000
ptr[1]=1.900000
ptr[2]=3.100000

If I add free(ptr) following the last line, i.e.

float arr[]={1.2, 1.9, 3.1};
float *ptr;
int i;
ptr = (float *)calloc(3,sizeof(float));
ptr = arr;
for (i=0;i<3;i++){
    printf("ptr[%d]=%f\n",i,ptr[i]);
}
free(ptr);  /*add free here*/

This time a warning came up:

warning: attempt to free a non-heap object ‘arr’ [-Wfree-nonheap-object]

int i;
int flag=0;
float arr1[3]={1.07,3.01,5.02};
float arr2[3]={2.07,6.01,9.02};
float arr3[3]={3.07,8.01,0.02};
float *ptr;
ptr = (float *)calloc(3,sizeof(float));
if(flag==0){
    ptr = arr1;
}
else if(flag==1){ 
    ptr = arr2;
}
else{ 
    ptr = arr3;
}
for (i=0;i<3;i++){
    printf("ptr[%d]=%f\n",i,ptr[i]);
}

It can run correctly with different flag, but if I add free(ptr) at last line, it still has warning like the previous.

Does anybody help analyze why?

coco
  • 71
  • 6
  • 1
    On one line you allocate enough bytes to hold three floats, and set the value of `ptr` to hold that memory location. On the next line change the value of `ptr` to hold the address of the `arr` array, orphaning the memory you previously allocated. Since the memory `ptr` points to is actually a literal array of three floats you cannot free it. The memory you *should* be freeing has been leaked. – Tibrogargan Jul 12 '18 at 01:38
  • 1
    OT: when calling any of the heap allocation functions: `malloc` `calloc` `realloc` 1) the returned type is `void*` which can be assigned to any pointer. Casting just clutters the code, making it more difficult to understand, debug, etc. 2) always check (!=NULL) the returned value to assure the operation was successful – user3629249 Jul 12 '18 at 01:56
  • 2
    regarding: `ptr = arr1;` and similar statements. This overlays the value set in the pointer via the call to `calloc()`, resulting in a memory leak. Perhaps you meant: `memcpy( ptr, arr1, sizeof( arr1) );` Note: in C, referencing the name of an array degrades to the address of the first byte of the array. – user3629249 Jul 12 '18 at 02:00
  • @user3629249 - I know what you mean, but technically an array is *"converted to an expression with type ''pointer to type'' that points to the initial element of the array object"* (*degrades* is a bit odd, but you will see *decays* -- which itself isn't in the standard, but it is commonly understood) See: [C11 Standard - 6.3.2.1 Lvalues, arrays, and function designators (p3)](http://port70.net/~nsz/c/c11/n1570.html#6.3.2.1p3). – David C. Rankin Jul 12 '18 at 06:41

4 Answers4

3
ptr = (float *)calloc(3,sizeof(float));

Here, you set ptr to a block of malloc'ed memory big enough for 3 floats. Then on the next line:

ptr = arr;

You overwrite the value of ptr, causing a memory leak, with arr. This is legal because in an assignment like this an array decays into a pointer to its first element.

When you then print the values that ptr point to, you get the values in arr because that's where ptr points.

This is also why calling free on ptr is invalid since it no longer points to allocated memory.

As for the last bit of code, this is an invalid initializer:

float arr1[3]={{1.07,3.01,5.02}};

When I ran this code, flag=0 showed 0 for the second and third values, while flag=1 and flag=2 showed all the expected values.

You only need one set of braces:

float arr1[3]={1.07,3.01,5.02};
dbush
  • 205,898
  • 23
  • 218
  • 273
  • 1
    For the last code, if I add `free(ptr);` at last line, it still has warning like the previous. – coco Jul 12 '18 at 02:00
  • 1
    @coco Yes, for the same reason: you overwrote the malloc'ed memory with the address of a local array. – dbush Jul 12 '18 at 02:00
  • 1
    So this code snippet is still useless because we have to de-allocate the memory after using them. Does that mean I have to find another way to assign the array to the pointer, like "for" loop? – coco Jul 12 '18 at 02:04
  • 2
    @coco You either have to copy the local array to the allocated memory (either with `memcpy` or a loop), or get rid of the allocation completely. – dbush Jul 12 '18 at 02:06
2

Solution without error

#include<stdio.h>  
#include<stdlib.h>

int main()
{
    float arr[]={1.2, 1.9, 3.1};
    float *ptr;
    int i;
    ptr = (float *)calloc(3,sizeof(float*));

    if(ptr == NULL){
     perror("calloc");
     return -ENOMEM;
    }
    //ptr = arr;

    // here you need to do something like this
    ptr[0] = arr[0];
    ptr[1] = arr[1];
    ptr[2] = arr[2];

    for (i=0;i<3;i++){
       printf("ptr[%d]=%f\n",i,ptr[i]);

           }
        free(ptr);
}
Jeet Parikh
  • 305
  • 2
  • 8
  • 2
    Not an error, but there is no need to cast the return of `malloc` (or `calloc` or `realloc`), it is unnecessary. See: [Do I cast the result of malloc?](http://stackoverflow.com/q/605845/995714) -- and you sure have a funny way of formatting braces... – David C. Rankin Jul 12 '18 at 06:24
  • yes, you're right but for **portability between other languages** like c++, I like to do so. – Jeet Parikh Jul 12 '18 at 10:25
  • Well, that's why there is something called the C-Standard and a separate document called the C++-Standard. Also, you should validate the return of `calloc`, e.g. (`if (ptr != NULL)`) before making use of the memory. The allocation functions return `NULL` on failure (and they do fail). Recall, when answering questions, you step into the roll of teacher. You are helping a new student learn to program. Make sure he remembers you as one of the good teachers `:)` – David C. Rankin Jul 12 '18 at 14:46
  • done!! thanks, from the next time i make sure everything should be clear .. :) – Jeet Parikh Jul 13 '18 at 07:16
1

There's absolutely nothing illegal at all in your code when you point a pointer to an array. It will not even give you a warning.

A pointer is a pointer and it can point at anything. Sometimes you may need to cast it, but pointing at an array of the same base type is not only completely valid, it's even common.

However, you can only use free on memory that's allocated on the heap (malloc, calloc, etc). An array is allocated on the stack and does not need to be freed, and as you have already seen, you will get a runtime error if you do.

klutt
  • 30,332
  • 17
  • 55
  • 95
  • 1
    But for "swap the pointer" (like the very first example), we can "free" the pointers. Is it because the dynamically allocated memory still exits, while an array can overwrite it with constant pointer making the pointer no longer dynamically allocated? – coco Jul 12 '18 at 03:50
  • @coco In the very first example you have not allocated any memory at all, so no, you cannot free them. – klutt Jul 12 '18 at 10:01
1

Assign an array

In C arrays may not be assigned.

You need to assign element by element.

What happens here

float arr[]={1.2, 1.9, 3.1};
float *ptr;

ptr = arr;

is that you assign to ptr the address of arr[0]. This is called "The array is decayed to a pointer (the address of) it's first element"

No array elements get copied!

To prove this just do

arr[0] = 42.;

next and then run the print-loop

for (int i = 0; i < 3; ++i) 
{
  printf("ptr[%d] = %f\n", i, ptr[i]);
}

you will get (something like):

ptr[0] = 42.000
ptr[1] = 1.9000
...
alk
  • 69,737
  • 10
  • 105
  • 255
  • Actually it can get copied. See the second code snippet, it can return the correct values. – coco Jul 12 '18 at 13:40
  • @coco: Did you read and try my proposal to prove that no elements are "copied", in none of the three cases you show? – alk Jul 13 '18 at 04:43