C : If as I understand 0 and '\0' are the same, how does the compiler knows the size of an array when I write int my_array = {0};?

Viewed 150

I am trying to create a function to copy an array into another using pointers. I'd like to add the following condition : if the array of destination is smaller, the loop must break.

So basically it's working, but it is not working if I intilize the the destination array as follows :

int dest_array[10] = {0};

From what I understand it fills the array with int 0's which are equivalent to '\0' (null characters). So here is my question :

In this case how can the computer know the array size or when it ends ?

(And how do I compare arrays passed as parameters ?)

void copy(int *src_arr, int *dest_arr)
{
    // The advantage of using pointers is that you don't need to provide the source array's size

        // I can't use sizeof to compare the sizes of the arrays because it does not work on parameters.
        // It returns the size of the pointer to the array and not of of the whole array

    int* ptr1;
    int* ptr2;

    for(     ptr1 = source, ptr2 = dest_arr ;
        *ptr1 != '\0'              ;        
             ptr1++, ptr2++            )
    {   
        if(!*ptr2) // Problem here if dest_arr full of 0's
                { 
                     printf("Copy interrupted :\n" +
                            "Destination array is too small"); 
                     break; 
                }

        *ptr2 = *ptr1;
    }
3 Answers

In C, it is impossible to know the length of an array inherently. This is due to the fact that an array is really just a contiguous chunk of memory, and the value passed to functions is really just a pointer to the first element in the array. As a result of this, to actually know the length of an array within a function other than the function where that array was declared, you have to somehow provide that value to the function. Two common approaches are the use of sentinel values which indicate the last element (similar to the way '\0', the null character, is per convention interpreted as the first character not part of a string in C), or providing another parameter which contains the array length.

As a very common example of this: if you have written any programs which use command-line parameters, then surely you are familiar with the common definition of int main(int argc, char *argv[]), which uses the second of the aforementioned approaches by providing the length of the argv array via the argc parameter.

The compiler has some ways to work around this for local variables. E.g., the following would work:

#include <stdio.h>

int main(){
    int nums[10] = {0};
    printf("%zu\n", sizeof(nums)/sizeof(nums[0]));
    
    return 0;
}

Which prints 10 to STDOUT; however, this only works because the sizeof operation is done locally, and the compiler knows the length of the array at that point.

On the other hand, we can consider the situation of passing the array to another function:

#include <stdio.h>

int tryToGetSizeOf(int arr[]){
    printf("%zu", sizeof(arr)/sizeof(arr[0]));
}

int main(){
    int nums[10] = {0};
    printf("%zu\n", sizeof(nums)/sizeof(nums[0]));
    
    puts("Calling other function...");
    tryToGetSizeOf(nums);
    
    return 0;
}

This will end up printing the following to STDOUT:

10
Calling other function...
2

This may not be the value you're expecting, but this occurs due to the fact that the method signature int tryToGetSizeOf(int arr[]) is functionally equivalent to int tryToGetSizeOf(int *arr). Therefore, you are dividing the size of an integer pointer (int *) by the size of a single int; whereas while you're still in the local context of main() (i.e., where the array was defined originally), you are dividing the size of the allocated memory region by the size of the datatype that memory region is partitioned as (int).

An example of this available on Ideone.

int* ptr1;
int* ptr2;

You lose size information when you refer to arrays as pointers. There is no way you can identify the size of the array i.e. the number of elements using ptr1. You have to take help of another variable which will denote the size of the array referred by ptr1 (or ptr2).

Same holds for character arrays as well. Consider the below:

char some_string[100];
strcpy(some_string, "hello");

The approach you mentioned of checking for \0 (or 0) gives you the number of elements which are part of the string residing in some_string. In no way does it refer to the number of elements in some_string which is 100.

To identify the size of destination, you have to pass another argument depicting its size.

There are other ways to identify the end of the array but t is cleaner to pass the size explicitly rather than using some pointer hack like passing a pointer to end of the array or using some invalid value as the last element in array.

TL/DR - You will need to pass the array size as a separate parameter to your function. Sentinel values like 0 only mark the logical end of a sequence, not the end of the array itself.

Unless it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array. So when you pass your source and destination arrays as arguments to copy, what the function actually receives is just two pointers.

There's no metadata associated with a pointer that tells it whether it's pointing to the first object in a sequence, or how long that sequence is1. A sentinel value like the 0 terminator in strings only tells you how long a logical sequence of values is, not the size of the array in which they are stored2.

You will need to supply at least one more parameter to copy to tell it how large the target buffer is, so you stop copying when you've reached the end of the target buffer or you see a 0 in the source buffer, whichever comes first.


  1. The same is true for array objects - there's no runtime metadata in the array object to store the size or anything else. The only reason the sizeof trick works is that the array's declaration is in scope. The array object itself doesn't know how big it is.
  2. This is a problem for library functions like strcpy, which only receives the starting address for each buffer - if there are more characters in the source buffer than the target is sized to hold, strcpy will blast right past the end of the target buffer and overwrite whatever follows.

Related