下面我将详细解释这类错误的各种情况、原因以及如何解决。

(图片来源网络,侵删)
错误的核心原因
编译器告诉你:"嘿,你正在试图给一个只读的内存区域赋值,这是不允许的!"
这通常发生在以下几种情况:
- 使用了
const关键字修饰的变量。 - 直接使用了字面量(如数字、字符串)。
- 在函数参数中错误地使用了指针。
最常见的错误 - 修改 const 变量
这是最直接、最常见的 "constant expression" 错误。
错误示例
#include <stdio.h>
int main() {
const int MAX_AGE = 100; // MAX_AGE 被声明为一个常量
printf("MAX_AGE is: %d\n", MAX_AGE);
// 尝试修改一个 const 变量
MAX_AGE = 101; // 这里会出错!
printf("Now MAX_AGE is: %d\n", MAX_AGE);
return 0;
}
编译器错误信息 (以GCC为例)
test.c: In function 'main':
test.c:8:9: error: assignment of read-only variable 'MAX_AGE'
8 | MAX_AGE = 101;
| ^~~~~~
错误分析:

(图片来源网络,侵删)
const int MAX_AGE = 100;告诉编译器,MAX_AGE是一个常量,它的值在初始化后不能被改变。MAX_AGE = 101;这行代码试图改变MAX_AGE的值,编译器在编译时就能发现这个违规操作,并报错。
如何解决
-
移除修改操作:如果你确实需要一个在程序运行过程中不会改变的值,那就不要去修改它。
const int MAX_AGE = 100; // ... 程序中使用 MAX_AGE,但不要修改它
-
使用普通变量:如果你这个值确实需要被修改,那就不要使用
const关键字。int max_age = 100; max_age = 101; // 这样就没问题了
在函数参数中错误地修改了 const 指针指向的内容
这种情况稍微复杂一些,是初学者容易混淆的地方。
错误示例
#include <stdio.h>
// 这个函数的意图是打印一个数组的元素
void print_array(const int *arr, int size) {
// arr 是一个指向 const int 的指针
// 这意味着我们不能通过 arr 来修改它所指向的整数值
for (int i = 0; i < size; i++) {
// 尝试修改指针指向的值
arr[i] = 0; // 这里会出错!
printf("%d ", arr[i]);
}
printf("\n");
}
int main() {
int my_array[] = {1, 2, 3, 4, 5};
print_array(my_array, 5);
return 0;
}
编译器错误信息
test.c: In function 'print_array':
test.c:10:13: error: assignment of read-only location '*arr'
10 | arr[i] = 0;
| ^~~~
错误分析:
const int *arr的含义是:arr是一个指针,它指向一个const int(一个整型常量)。- 这保证了指针所指向的数据是只读的,目的是为了安全。
print_array函数只需要读取数组内容,不需要修改它,所以使用const是一个好习惯。 arr[i]实际上是*(arr + i)的语法糖,意思是“获取arr指向的值”,试图给这个值赋0,就等于修改了一个常量,所以编译器报错。
如何解决
-
移除修改操作:如果函数确实不需要修改数据,那就不要修改,这是最安全的做法。
void print_array(const int *arr, int size) { for (int i = 0; i < size; i++) { // 只读,不修改 printf("%d ", arr[i]); } } -
修改函数签名:如果函数确实需要修改传入的数组,那么应该在声明指针时去掉
const。// 函数签名改为 void modify_array(int *arr, int size) { for (int i = 0; i < size; i++) { arr[i] = 0; // 现在就没问题了 } }
试图修改一个字面量
这种情况比较少见,但也是可能的,字面量是直接写在代码中的值,如 10、'a'、"hello"。
错误示例
int main() {
// 尝试修改一个整型字面量
10 = 20; // 绝对会出错!
// 尝试修改一个字符串字面量(在C语言中,字符串字面量通常存储在只读内存区)
char *str = "hello";
str[0] = 'H'; // 在很多编译器和系统上,这会导致段错误或运行时崩溃
// 一些编译器可能会在编译时给出警告或错误
return 0;
}
编译器错误信息
test.c: In function 'main':
test.c:4:5: error: expression is not assignable
4 | 10 = 20;
| ^~
错误分析:
10是一个字面量,它没有内存地址,只是一个值,你不能给一个值“赋值”,这是没有意义的。- 字符串字面量
"hello"通常被放在程序的只读数据段,试图修改它会导致未定义行为,通常是程序崩溃。
如何解决
- 不要这样做! 字面量就是用来表示固定值的,不能修改,如果需要一个可变的字符串,请使用字符数组。
// 正确做法 char str[] = "hello"; // str 是一个字符数组,存储在栈上,是可修改的 str[0] = 'H'; // 现在可以安全地修改 printf("%s\n", str); // 输出 "Hello"
在 switch 语句中使用非常量表达式
switch 语句的 case 标签必须是整型常量表达式,这意味着它必须在编译时就能计算出值,并且不能是变量。
错误示例
#include <stdio.h>
int main() {
int choice = 2;
const int C = 5;
switch (choice) {
case 1:
printf("One\n");
break;
case choice: // 错误!choice 是一个变量
printf("Two\n");
break;
case C: // 这是允许的!C 是一个 const 变量,它的值在编译时是已知的
printf("Five\n");
break;
default:
printf("Other\n");
}
return 0;
}
编译器错误信息
test.c: In function 'main':
test.c:11:10: error: case label does not reduce to an integer constant
11 | case choice:
| ^~~~~~
错误分析:
case标签需要编译器在编译代码时就确定好跳转的地址,而choice是一个运行时才能确定值的变量,编译器无法处理。const int C = 5;之所以可以,是因为C的值在编译时是固定的(即使你不能通过代码修改它),编译器可以用5来代替它。
如何解决
- 使用
if-else if-else结构:当条件不是常量时,这是唯一的选择。if (choice == 1) { printf("One\n"); } else if (choice == 2) { printf("Two\n"); } else if (choice == C) { // C 仍然可以使用 printf("Five\n"); } else { printf("Other\n"); }
| 错误场景 | 错误原因 | 解决方案 |
|---|---|---|
修改 const 变量 |
变量被声明为只读,但尝试赋新值。 | 不要修改它。 如果需要修改,去掉 const 关键字。 |
修改 const 指针指向的内容 |
指针指向的数据是只读的,但尝试通过指针修改它。 | 不要修改它。 如果需要修改,去掉指针声明中的 const。 |
| 修改字面量 | 字面量(如 10, "hello")是代码中的固定值,没有内存地址或位于只读区。 |
不要修改! 使用变量(如 int a = 10; 或 char str[] = "hello";)来存储需要变化的值。 |
switch 中使用变量作为 case |
case 标签必须是编译时就能确定的整型常量表达式。 |
使用 if-else if-else 结构。确保使用的是真正的常量(如 #define 或 const 变量)。 |
当你遇到 "constant" 相关的错误时,首先找到错误行号,然后检查该行是否属于以上四种情况之一,问题都能迎刃而解。
