Keywords: C Programming | Function Parameters | String Literals | Pointer-Array Equivalence | Undefined Behavior
Abstract: This paper thoroughly examines the complete equivalence between char arr[] and char *arr declarations in C function parameters, analyzing the behavior when string literals are passed as arguments through code examples. It explains why modifying string literals leads to undefined behavior, compares stack-allocated arrays with pointers to read-only memory, and details the memory mechanism of parameter passing during function calls. Based on high-scoring Stack Overflow answers, this article systematically organizes core concepts to provide clear technical guidance for C programmers.
Equivalence in Function Parameter Declarations
In C function parameter lists, the declarations char arr[] and char *arr have identical semantics. When processing function prototypes and definitions, compilers automatically convert array-form parameters to pointer form. This equivalence applies not only to function definitions but also to function declarations.
// The following two function declarations are completely equivalent
void process_string(char str[]);
void process_string(char *str);
// The following two function definitions are also completely equivalent
void process_string(char str[]) {
// Function body
}
void process_string(char *str) {
// Function body
}
Memory Characteristics of String Literals
String literals such as "MyString" are typically stored in read-only memory regions during program execution. When a string literal is passed as a function argument, what is actually passed is a pointer to this read-only memory area. Attempting to modify the content of a string literal results in undefined behavior, which in most implementations manifests as a segmentation fault.
// Dangerous example: attempting to modify a string literal
void modify_string(char arr[]) {
arr[0] = 'X'; // If arr points to a string literal, this causes undefined behavior
}
int main() {
modify_string("MyString"); // Passing the address of a string literal
return 0;
}
Memory Mechanism of Parameter Passing
During function calls, parameters are passed by value. For pointer-type parameters, what is passed is a copy of the pointer value (memory address), not the data pointed to by the pointer. This means the function receives a copy of the original pointer, but this copy still points to the same memory location.
// Parameter passing example
void demonstrate_parameter_passing(char *ptr) {
// ptr is a copy of the pointer from the main function
// but ptr and the original pointer point to the same memory address
printf("Address value: %p\n", (void*)ptr);
}
int main() {
char *str = "Literal";
demonstrate_parameter_passing(str);
return 0;
}
Comparison of Stack-Allocated Arrays and Pointers
Understanding the distinction between string literals and stack-allocated arrays is crucial. Stack-allocated character arrays have modifiable memory space within the function scope, while pointers to string literals reference read-only memory.
// Examples of correct and incorrect modifications
int main() {
// Case 1: Pointer to string literal (not modifiable)
char *ptr_to_literal = "Hello";
// ptr_to_literal[0] = 'Z'; // Error: undefined behavior
// Case 2: Stack-allocated character array (modifiable)
char stack_array[] = "Hello";
stack_array[0] = 'Z'; // Correct: modifying memory allocated on stack
return 0;
}
Best Practice Recommendations
To avoid undefined behavior, it is recommended to treat string literals as static const char arrays. When a function needs to modify string content, it should receive stack- or heap-allocated character arrays, not string literals. For read-only string operations, use const char* parameters to clearly express intent.
// Safe function design examples
// Read-only version: using const qualifier
void print_string(const char *str) {
printf("%s\n", str);
}
// Modifiable version: requiring callers to provide writable buffers
void modify_buffer(char buffer[], size_t size) {
if (size > 0) {
buffer[0] = 'X';
}
}
int main() {
// Safe call to read-only function
print_string("Safe literal");
// Safe call to modifiable function
char writable[] = "Modifiable";
modify_buffer(writable, sizeof(writable));
return 0;
}
By understanding function parameter declaration equivalence, the read-only nature of string literals, and parameter passing mechanisms, C programmers can avoid common string handling errors and write safer, more reliable code.