What's the difference among array, &array and &array[0] in C language?

Learn what's the difference among array, &array and &array[0] in c language? with practical examples, diagrams, and best practices. Covers c, arrays, pointers development techniques with visual exp...

Understanding C Arrays: array, &array, and &array[0]

Hero image for What's the difference among array, &array and &array[0] in C language?

Demystify the subtle yet crucial differences between an array name, its address, and the address of its first element in C programming.

In C programming, arrays are fundamental data structures. However, their interaction with pointers, especially when it comes to their addresses, can be a source of confusion for many developers. This article aims to clarify the distinctions between array, &array, and &array[0], explaining what each expression evaluates to and how they behave in different contexts.

The Array Name: array

When you declare an array in C, the array's name itself acts as a constant pointer to its first element. This means that in most expressions, array decays into a pointer to its first element. Its type is 'pointer to element type'. For an array int arr[5];, arr evaluates to the address of arr[0], and its type is int *.

int arr[5] = {10, 20, 30, 40, 50};

printf("Value of arr: %p\n", (void*)arr);          // Address of the first element
printf("Type of arr: %s\n", "int *");             // Conceptual type
printf("Value of &arr[0]: %p\n", (void*)&arr[0]); // Same address as arr
printf("Type of &arr[0]: %s\n", "int *");         // Conceptual type

Demonstrating the value and conceptual type of arr and &arr[0].

Address of the Array: &array

While array points to the first element, &array refers to the address of the entire array object. The key difference here is the type. For int arr[5];, &arr has the type int (*)[5], which means 'pointer to an array of 5 integers'. Although its value (the memory address) is the same as arr and &arr[0], its type carries information about the size of the entire array. This type difference becomes significant in pointer arithmetic.

int arr[5] = {10, 20, 30, 40, 50};

printf("Value of &arr: %p\n", (void*)&arr);        // Address of the entire array
printf("Type of &arr: %s\n", "int (*)[5]");       // Conceptual type

// Pointer arithmetic difference:
printf("arr + 1: %p\n", (void*)(arr + 1));        // Moves by sizeof(int) (4 bytes)
printf("&arr + 1: %p\n", (void*)(&arr + 1));      // Moves by sizeof(arr) (5 * 4 = 20 bytes)

Illustrating the type and pointer arithmetic behavior of &arr.

flowchart TD
    subgraph Memory Layout
        A[arr[0]]
        B[arr[1]]
        C[arr[2]]
        D[arr[3]]
        E[arr[4]]
    end

    arr -- points to --> A
    "&arr[0]" -- points to --> A
    "&arr" -- points to --> A

    style A fill:#f9f,stroke:#333,stroke-width:2px
    style B fill:#fff,stroke:#333,stroke-width:1px
    style C fill:#fff,stroke:#333,stroke-width:1px
    style D fill:#fff,stroke:#333,stroke-width:1px
    style E fill:#fff,stroke:#333,stroke-width:1px

    subgraph Pointer Types
        P1["arr (int *)"]
        P2["&arr[0] (int *)"]
        P3["&arr (int (*)[5])"]
    end

    P1 --> arr
    P2 --> "&arr[0]"
    P3 --> "&arr"

Visual representation of arr, &arr[0], and &arr pointing to the same starting memory address but having different types.

Address of the First Element: &array[0]

This expression explicitly takes the address of the first element of the array. It is the most straightforward way to get a pointer to the first element. Its type is 'pointer to element type', which for int arr[5]; is int *. As seen, its value is identical to arr.

Summary of Differences

The core difference lies in their types and how those types influence pointer arithmetic and type checking. All three expressions evaluate to the same memory address, which is the starting address of the array. However, their types dictate how the compiler interprets operations like + 1.

flowchart LR
    A["int arr[5]"]
    B["arr"]
    C["&arr[0]"]
    D["&arr"]

    B -- "Decays to" --> E["Pointer to first element (int *)"]
    C -- "Explicitly is" --> E
    D -- "Is" --> F["Pointer to entire array (int (*)[5])"]

    E -- "Value: Start Address" --> G[Memory Address]
    F -- "Value: Start Address" --> G

    E -- "Pointer Arithmetic: Moves by sizeof(int)" --> H[Next int address]
    F -- "Pointer Arithmetic: Moves by sizeof(arr)" --> I[Next array address]

    style G fill:#f9f,stroke:#333,stroke-width:2px
    style E fill:#ccf,stroke:#333,stroke-width:1px
    style F fill:#ccf,stroke:#333,stroke-width:1px

Conceptual differences between arr, &arr[0], and &arr.

Understanding these distinctions is crucial for writing correct and efficient C code, especially when dealing with functions that accept array arguments or when performing complex pointer manipulations.