In the C language, the “interchangeability” of arrays and pointers refers to the high consistency in syntax and behavior between the two in specific scenarios. However, this consistency does not stem from their inherent similarity, but is determined by the array name decay rules, syntax design, and compiler processing logic in C language. This article will systematically summarize the scenarios where arrays and pointers are interchangeable, the boundaries where they are not interchangeable, the essential reasons for their interchangeability, and best practices in actual programming, along with code examples to verify these conclusions, helping developers accurately grasp the relationship between the two.
1. Core Scenarios of Array and Pointer Interchangeability
The interchangeability of arrays and pointers is mainly reflected in expression usage and function parameter passing. The underlying logic is that the array name will implicitly decay to a pointer (of type <span>T*</span>) pointing to the first element in most expressions, thus exhibiting behavior similar to that of pointer variables.
1.1 Interchangeability of Array Names and Pointer Variables in Expressions
When the array name decays to a pointer in an expression, it is completely interchangeable with the pointer variable pointing to that array in terms of address value, pointer arithmetic, and dereferencing.
Code Example: Verification of Interchangeability in Expressions
#include <stdio.h>
int main() {
int arr[5] = {10, 20, 30, 40, 50};
int *ptr = arr; // The array name arr decays to a pointer, assigned to ptr (interchangeable starting point)
// Scenario 1: Address value and dereferencing operations are interchangeable
printf("Address value: arr = %p, ptr = %p\n", (void*)arr, (void*)ptr); // Same address
printf("Dereference result: *arr = %d, *ptr = %d\n", *arr, *ptr); // Both are arr[0] (10)
// Scenario 2: Pointer arithmetic (+/- integer) is interchangeable
printf("Pointer +1 address: arr+1 = %p, ptr+1 = %p\n", (void*)(arr+1), (void*)(ptr+1)); // Same step size (+4 bytes)
printf("Pointer +2 dereference: *(arr+2) = %d, *(ptr+2) = %d\n", *(arr+2), *(ptr+2)); // Both are arr[2] (30)
// Scenario 3: Interchangeability in string processing
char str_arr[] = "Author_WeiXin:HGbg666888"; // Character array
char *str_ptr = str_arr; // The array name decays to a pointer, assigned to str_ptr
printf("String access: str_arr[7] = %c, str_ptr[7] = %c\n", str_arr[7], str_ptr[7]); // Both are 'W' (interchangeable)
return 0;
}
Output Result:
Address value: arr = 0x7ffdabcdef10, ptr = 0x7ffdabcdef10
Dereference result: *arr = 10, *ptr = 10
Pointer +1 address: arr+1 = 0x7ffdabcdef14, ptr+1 = 0x7ffdabcdef14
Pointer +2 dereference: *(arr+2) = 30, *(ptr+2) = 30
String access: str_arr[7] = W, str_ptr[7] = W
Explanation:
After the array name <span>arr</span> decays to a pointer of type <span>int*</span>, it is completely consistent with the explicitly defined pointer <span>ptr</span> in terms of address value, pointer arithmetic step size (both are <span>sizeof(int)</span> bytes), and dereference results; similarly, the character array <span>str_arr</span> and the pointer <span>str_ptr</span> are interchangeable in string element access, verifying the behavioral consistency of array names and pointer variables in expressions.
1.2 Interchangeability of Array Declarations and Pointer Declarations in Function Parameters
The C language stipulates that: array declarations in function parameters (such as <span>int arr[]</span> or <span>int arr[5]</span>) will be automatically converted by the compiler to pointer declarations (<span>int *arr</span>). Therefore, arrays and pointers are completely interchangeable in function parameters, and the declaration forms of the two do not affect the compiler’s processing logic.
Code Example: Verification of Interchangeability in Function Parameters
#include <stdio.h>
// Function 1: Parameter declared as array form (actually converted to pointer)
void print_array(int arr[], int len) { // Equivalent to int *arr
for (int i = 0; i < len; i++) {
printf("%d ", arr[i]); // arr[i] is equivalent to *(arr+i), consistent with pointer access logic
}
printf("\n");
}
// Function 2: Parameter declared as pointer form
void print_pointer(int *ptr, int len) {
for (int i = 0; i < len; i++) {
printf("%d ", ptr[i]); // ptr[i] is equivalent to *(ptr+i), consistent with array access logic
}
printf("\n");
}
int main() {
int data[4] = {1, 3, 5, 7};
int *data_ptr = data; // The array name decays to a pointer
// Array passed as parameter (decays to pointer)
printf("Array parameter call:");
print_array(data, 4); // Output: 1 3 5 7
// Pointer passed as parameter (consistent with array parameter logic)
printf("Pointer parameter call:");
print_pointer(data_ptr, 4); // Output: 1 3 5 7
// Array name directly passed as pointer parameter (interchangeability)
printf("Array name as pointer parameter:");
print_pointer(data, 4); // Output: 1 3 5 7 (same as array parameter call)
return 0;
}
Output Result:
Array parameter call: 1 3 5 7
Pointer parameter call: 1 3 5 7
Array name as pointer parameter: 1 3 5 7
Explanation:
The parameter of the function <span>print_array</span> declared as <span>int arr[]</span> is converted by the compiler to <span>int *arr</span>, which is completely equivalent to the parameter of <span>print_pointer</span> declared as <span>int *ptr</span>. Whether passing the array name (after decay) or the pointer variable, the access logic inside the function (<span>arr[i]</span> or <span>ptr[i]</span>) is completely the same, verifying the interchangeability of arrays and pointers in function parameters.
1.3 Syntax Interchangeability of Subscript Access and Pointer Arithmetic
The C language standard defines: array subscript access<span>arr[i]</span> is equivalent to pointer arithmetic<span>*(arr + i)</span>. Since the array name decays to a pointer and behaves consistently with pointer variables in arithmetic, the syntax for subscript access of arrays and pointers is completely interchangeable.
Code Example: Verification of Interchangeability in Subscript Access and Pointer Arithmetic
#include <stdio.h>
int main() {
int arr[5] = {2, 4, 6, 8, 10};
int *ptr = arr; // The array name decays to a pointer
// Interchangeability of array subscript and pointer subscript
printf("arr[3] = %d, ptr[3] = %d\n", arr[3], ptr[3]); // Both are 8 (equivalent to *(arr+3) and *(ptr+3))
// Reverse interchangeability of pointer arithmetic and array subscript (counterintuitive but legal)
printf("3[arr] = %d, 3[ptr] = %d\n", 3[arr], 3[ptr]); // Both are 8 (equivalent to *(3+arr) and *(3+ptr))
// Interchangeability in string subscript access
char wechat[] = "Author_WeiXin:HGbg666888";
char *wechat_ptr = wechat;
printf("WeChat string 15th character: wechat[14] = %c, wechat_ptr[14] = %c\n", wechat[14], wechat_ptr[14]); // Both are 'H'
return 0;
}
Output Result:
arr[3] = 8, ptr[3] = 8
3[arr] = 8, 3[ptr] = 8
WeChat string 15th character: wechat[14] = H, wechat_ptr[14] = H
Explanation:
<span>arr[i]</span> and <span>ptr[i]</span> have the same underlying logic of pointer arithmetic, thus the syntax is completely interchangeable; even the counterintuitive syntax <span>i[arr]</span> is legal (due to the commutative property of addition), further verifying the consistency of array and pointer access syntax.
2. Boundaries Where Arrays and Pointers Are Not Interchangeable
Although arrays and pointers are interchangeable in the above scenarios, their essential differences determine that they are completely non-interchangeable in the following scenarios, which is also the core point to avoid confusion.
2.1 Non-Interchangeability of Results from the sizeof Operator
<span>sizeof(array name)</span><code> returns the <strong><span>size of the entire array in bytes</span></strong>, while sizeof(pointer) returns the <strong><span>size of the pointer variable itself</span></strong> (related to system bitness, 8 bytes for 64-bit systems), resulting in completely different outcomes.
Code Example: Non-Interchangeability of sizeof Results
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr; // The array name decays to a pointer
printf("sizeof(arr) = %zu\n", sizeof(arr)); // Result: 20 (5*4 bytes, size of the entire array)
printf("sizeof(ptr) = %zu\n", sizeof(ptr)); // Result: 8 (size of pointer on 64-bit system)
printf("sizeof(*arr) = %zu\n", sizeof(*arr)); // Result: 4 (size of first element int, *arr is equivalent to arr[0])
printf("sizeof(*ptr) = %zu\n", sizeof(*ptr)); // Result: 4 (size of pointed element int, consistent with *arr)
// Comparison of sizeof string array and pointer
char str_arr[] = "Author_WeiXin:HGbg666888"; // Array size: string length + 1 (including '\0')
char *str_ptr = str_arr;
printf("sizeof(str_arr) = %zu\n", sizeof(str_arr)); // Result: 22 (21 characters + 1)
printf("sizeof(str_ptr) = %zu\n", sizeof(str_ptr)); // Result: 8 (size of pointer, unrelated to string length)
return 0;
}
Output Result:
sizeof(arr) = 20
sizeof(ptr) = 8
sizeof(*arr) = 4
sizeof(*ptr) = 4
sizeof(str_arr) = 22
sizeof(str_ptr) = 8
Explanation:
<span>sizeof(arr)</span><code> calculates the size of the entire array (20 bytes for 5 ints), while sizeof(ptr) only calculates the size of the pointer variable (8 bytes); even for strings, sizeof(str_arr) returns the total size of the array (including ‘\0’), while sizeof(str_ptr) remains the size of the pointer, making the two completely non-interchangeable.
2.2 Non-Interchangeability of Mutability
The array name is a constant (representing the starting address, unmodifiable), while the pointer is a variable (the stored address can be modified), which leads to complete non-interchangeability in assignment operations.
Code Example: Non-Interchangeability of Mutability
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr; // The array name decays to a pointer, assigned to ptr
// Pointer can be modified (legal)
ptr++; // Pointer points to arr[1]
printf("ptr after modification points to: %d\n", *ptr); // Output: 2
// Array name cannot be modified (compilation error)
// arr++; // Error: array name is a constant, cannot be modified as left value
// Pointer can point to other memory (legal)
int x = 100;
ptr = &x;
printf("ptr points to new variable: %d\n", *ptr); // Output: 100
// Array name cannot be reassigned (compilation error)
// arr = &x; // Error: array name is a constant, cannot be assigned
return 0;
}
Output Result (after commenting out erroneous code):
ptr after modification points to: 2
ptr points to new variable: 100
Explanation:
The pointer <span>ptr</span> can be modified by <span>++</span> or reassigned, while the array name <span>arr</span> is a constant, and any modification operation (such as <span>arr++</span> or <span>arr = &x</span>) will trigger a compilation error, making the two completely non-interchangeable in terms of mutability.
2.3 Non-Interchangeability of Address Types
The type of the decayed array name is “pointer to element” (<span>T*</span>), while the address of the array (<span>&array name</span>) is of type “pointer to array” (<span>T (*)[n]</span>); the address of the pointer variable (<span>&pointer</span>) is of type “pointer to pointer” (<span>T**</span>). The differences in types lead to non-interchangeability in pointer arithmetic step sizes.
Code Example: Non-Interchangeability of Address Types
#include <stdio.h>
int main() {
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr; // The array name decays (int* type)
// Address type comparison
printf("arr type: %s\n", __typeof__(arr)); // Output: int[5] (array type, before decay)
printf("ptr type: %s\n", __typeof__(ptr)); // Output: int* (pointer type)
printf("&arr type: %s\n", __typeof__(&arr)); // Output: int (*)[5] (array pointer type)
printf("&ptr type: %s\n", __typeof__(&ptr)); // Output: int** (pointer to pointer type)
// Pointer arithmetic step size comparison (non-interchangeable)
int (*arr_ptr)[5] = &arr; // Array pointer (points to the entire array)
int **ptr_ptr = &ptr; // Pointer to pointer (points to pointer variable)
printf("arr+1 address: %p\n", (void*)(arr + 1)); // Step size: +4 bytes (size of int)
printf("arr_ptr+1 address: %p\n", (void*)(arr_ptr + 1)); // Step size: +20 bytes (total size of array 5*4)
printf("ptr+1 address: %p\n", (void*)(ptr + 1)); // Step size: +4 bytes (size of int)
printf("ptr_ptr+1 address: %p\n", (void*)(ptr_ptr + 1)); // Step size: +8 bytes (size of pointer)
return 0;
}
Output Result (GCC Compiler):
arr type: int[5]
ptr type: int*
&arr type: int (*)[5]
&ptr type: int**
arr+1 address: 0x7ffdabcdef14
arr_ptr+1 address: 0x7ffdabcdef24
ptr+1 address: 0x7ffdabcdef14
ptr_ptr+1 address: 0x7ffdabcdef38
Explanation:
<span>arr</span> (after decay <span>int*</span>) and <span>ptr</span> (<span>int*</span>) have the same arithmetic step size (+4 bytes), but <span>&arr</span> (<span>int (*)[5]</span>) and <span>&ptr</span> (<span>int**</span>) have completely different types and step sizes (+20 bytes vs +8 bytes), verifying the non-interchangeability of arrays and pointers in address types.
3. Essential Reasons for Array and Pointer Interchangeability
The interchangeability of arrays and pointers does not stem from their inherent similarity, but is determined by the following core mechanisms:
3.1 Implicit Decay Rule of Array Names
The C language standard stipulates that: array names will automatically convert to pointers to the first element (<span>T*</span>) in most expressions. This rule is the fundamental premise of interchangeability, allowing array names to exhibit behavior similar to pointer variables in expressions (such as address values, pointer arithmetic, and dereferencing).
3.2 Array Decay Optimization in Function Parameters
To avoid the performance overhead of passing arrays by value (copying the entire array memory), the C language forces the conversion of array declarations in function parameters to pointers, achieving “pass-by-address” calls. This optimization makes the declaration forms of arrays and pointers interchangeable in function parameters, but essentially it is the compiler’s “dimensional reduction” of array parameters.
3.3 Pointer Arithmetic Nature of Subscript Access
The C language defines <span>arr[i]</span> as syntactic sugar for <span>*(arr + i)</span>, and since the decayed array name behaves consistently with pointer variables in arithmetic logic, the subscript access syntax is completely applicable to both arrays and pointers, further reinforcing the appearance of interchangeability.
4. Summary of Interchangeability and Best Practices
4.1 Comparison Table of Interchangeable and Non-Interchangeable Scenarios
| Scenario | Interchangeability | Core Reason | Example |
| Address value and dereferencing in expressions | Interchangeable | Array name decays to the same address as pointer variable | <span>*arr == *ptr</span>, <span>arr + i == ptr + i</span> |
| Function parameter declarations | Interchangeable | Array parameters are converted to pointers by the compiler | <span>void func(int arr[])</span> is equivalent to <span>void func(int *arr)</span> |
Subscript access (<span>arr[i]</span>) |
Interchangeable | Subscript access is essentially pointer arithmetic<span>*(arr + i)</span> |
<span>arr[3] == ptr[3]</span>, <span>i[arr] == i[ptr]</span> |
<span>sizeof</span> operator results |
Non-interchangeable | Array name represents the entire array, pointer represents the address variable | <span>sizeof(arr) = 20</span> (array) vs <span>sizeof(ptr) = 8</span> (pointer) |
| Mutability (assignment/increment) | Non-interchangeable | Array name is a constant, pointer is a variable | <span>ptr++</span> legal, <span>arr++</span> illegal |
Address types (<span>&array name</span> vs <span>&pointer</span>) |
Non-interchangeable | Array address is an array pointer, pointer address is a pointer to pointer | <span>&arr</span> type is <span>int (*)[5]</span>, <span>&ptr</span> type is <span>int**</span> |
4.2 Best Practices in Actual Programming
- Clearly define the boundaries of interchangeability: Only utilize interchangeability in scenarios such as expression usage, function parameter passing, and subscript access, avoiding confusion between arrays and pointers in
<span>sizeof</span>, address types, and modification operations. - Explicitly pass length when passing arrays as function parameters: Since array parameters decay to pointers, the original size cannot be obtained through
<span>sizeof</span>, and the length parameter must be explicitly passed (e.g.,<span>void func(int arr[], int len)</span>). - Distinguish between string arrays and string pointers:
<span>char str_arr[] = "Author_WeiXin:HGbg666888"</span>is a modifiable array, while<span>char *str_ptr = "Author_WeiXin:HGbg666888"</span>points to read-only memory, modifying<span>str_ptr[i]</span>will lead to undefined behavior. - Avoid using
<span>i[arr]</span>and other counterintuitive syntax: Although<span>i[arr]</span>is equivalent to<span>arr[i]</span>, the latter is more in line with reading habits and can improve code readability.
5. Conclusion
The interchangeability of arrays and pointers is a product of C language syntax design, with the core being the implicit decay of array names in specific scenarios, rather than their inherent similarity. Mastering the interchangeable scenarios (expression usage, function parameters, subscript access) can simplify code writing, while clearly defining the non-interchangeable boundaries (<span>sizeof</span>, mutability, address types) can avoid memory errors.
Through this summary, developers should establish a clear understanding: an array is a collection of contiguous elements, while a pointer is a variable that stores an address, and their interchangeability is a limited equivalence at the syntax level, not an essential unity. Combined with the articles “Detailed Explanation of the Differences Between C Language Arrays and Pointers”, “Detailed Explanation of Similar Scenarios”, and “Reasons for Confusion”, a complete knowledge system of C language arrays and pointers can be formed to write more efficient and safe code.