为何C语言encrypt函数返回值用%s无输出,用%c却能显示字符?
问题描述
我编写了一个encrypt函数,用于返回string类型的密文。函数内部创建char数组cptxt_arr,通过循环将用户输入的明文转换后的密文字符存入该数组,再将数组赋值给string变量返回。
遇到的异常:
- 在
main函数中用%s格式打印encrypt的返回值时,命令行无任何输出 - 但用
%c格式打印返回值的首个字符时,能正常显示对应字符
补充说明:
- 清楚C语言无法直接返回数组,因此采用将char数组赋值给string再返回的方式
- 在
encrypt函数内部打印ciphertext是正常的,说明密文生成逻辑无问题 - 尝试过在main中用变量接收返回值,同样无法正常输出
附上代码:
#include <cs50.h> #include <stdio.h> #include <string.h> string encrypt(string plaintext, int key); int main(void) { int key; string plaintext; // 获取密钥 key = get_int("Key: "); // 获取用户输入的明文 plaintext = get_string("plaintext: "); // 打印密文 printf("ciphertext: %s\n", encrypt(plaintext, key)); // 用%c格式打印的注释代码 // printf("ciphertext: %c\n", encrypt(plaintext, key)[0]); } // 加密函数 string encrypt(string plaintext, int key) { // 获取明文长度 int length = strlen(plaintext); char cptxt_arr[length]; string ciphertext; for (int i = 0; i < length; i++) { if (plaintext[i] >= 'A' && plaintext[i] <= 'Z') { cptxt_arr[i] = ((plaintext[i]) + key); } else if (plaintext[i] >= 'a' && plaintext[i] <= 'z') { cptxt_arr[i] = ((plaintext[i]) + key); } } // 将char数组赋值给string并返回 ciphertext = cptxt_arr; return ciphertext; }
问题原因
- 局部数组生命周期失效:
cptxt_arr是encrypt函数内的局部栈数组,函数执行完毕后,栈上的局部变量会被销毁,内存被释放。此时返回的string(本质是char*)指向的是已失效的内存,属于野指针。- 用
%c能打印首个字符只是巧合——内存刚被释放还未被覆盖,侥幸能读到数据,但这是未定义行为,完全不可靠。 - 用
%s打印时,printf会从指针位置一直读取到\0终止符,但此时内存已无效,要么读不到终止符,要么读到乱码,最终导致无输出或程序异常。
- 用
- 缺少字符串终止符:即便数组内存未失效,
cptxt_arr也没有添加字符串必需的\0(空字符),printf用%s打印时找不到终止符,会持续读取内存直到遇到\0,行为同样未定义。
解决方案
提供两种可靠的修复方式:
方式一:动态分配堆内存
用malloc在堆上分配内存,堆内存不会随函数结束而释放,需后续手动释放(CS50的string本质是char*,可直接用free):
string encrypt(string plaintext, int key) { int length = strlen(plaintext); // 分配length+1的空间,预留位置存\0 char *cptxt_arr = malloc(length + 1); if (cptxt_arr == NULL) { // 检查内存分配是否成功 return NULL; } for (int i = 0; i < length; i++) { if (plaintext[i] >= 'A' && plaintext[i] <= 'Z') { // 新增模运算处理字母循环,避免超出Z后变成非字母 cptxt_arr[i] = 'A' + (plaintext[i] - 'A' + key) % 26; } else if (plaintext[i] >= 'a' && plaintext[i] <= 'z') { cptxt_arr[i] = 'a' + (plaintext[i] - 'a' + key) % 26; } else { // 非字母字符直接保留 cptxt_arr[i] = plaintext[i]; } } cptxt_arr[length] = '\0'; // 添加字符串终止符 return cptxt_arr; }
在main中使用后记得释放内存,避免泄漏:
string ciphertext = encrypt(plaintext, key); printf("ciphertext: %s\n", ciphertext); free(ciphertext);
方式二:由调用者提供目标数组
把密文数组作为参数传入encrypt函数,内存管理由调用者负责:
void encrypt(string plaintext, int key, char *ciphertext) { int length = strlen(plaintext); for (int i = 0; i < length; i++) { if (plaintext[i] >= 'A' && plaintext[i] <= 'Z') { ciphertext[i] = 'A' + (plaintext[i] - 'A' + key) % 26; } else if (plaintext[i] >= 'a' && plaintext[i] <= 'z') { ciphertext[i] = 'a' + (plaintext[i] - 'a' + key) % 26; } else { ciphertext[i] = plaintext[i]; } } ciphertext[length] = '\0'; }
在main中的调用方式:
int main(void) { int key; string plaintext; key = get_int("Key: "); plaintext = get_string("plaintext: "); int len = strlen(plaintext); char ciphertext[len + 1]; // 分配足够空间 encrypt(plaintext, key, ciphertext); printf("ciphertext: %s\n", ciphertext); }
注:原代码的加密逻辑存在小缺陷——直接plaintext[i] + key会导致字母超出Z/z后变成非字母字符,上述修复中已添加模运算实现循环移位,符合凯撒密码的预期逻辑。
内容的提问来源于stack exchange,提问作者Fito
相关产品推荐
相关产品推荐

