CS50字符串转大小写后打印异常:存储变量与直接输出结果不符
我正在完成CS50的替换密码习题,使用cs50.h库的string类型处理字符串。自己实现了两个字符串大小写转换函数:
string strtolower(string text) { int length = strlen(text); for (int i = 0; i < length; i++) { text[i] = tolower(text[i]); } return text; } string strtoupper(string text) { int length = strlen(text); for (int i = 0; i < length; i++) { text[i] = toupper(text[i]); } return text; }
测试异常情况
当在main函数中按以下方式调用时:
string key_upper = strtoupper(key); string key_lower = strtolower(key); printf("%s\n", key_upper); printf("%s\n", key_lower);
无论输入的key是FFFFFFFFFFFFFFFFFFFFFFFFFF还是ffffffffffffffffffffffffff,输出都是:
ffffffffffffffffffffffffff ffffffffffffffffffffffffff
修改调用方式,增加直接打印函数返回值的语句:
string key_upper = strtoupper(key); string key_lower = strtolower(key); printf("%s\n", key_upper); printf("%s\n", key_lower); printf("%s\n", strtoupper(key));
输出变为:
ffffffffffffffffffffffffff ffffffffffffffffffffffffff FFFFFFFFFFFFFFFFFFFFFFFFFF
进一步测试:
printf("%s\n", key_upper); printf("%s\n", key_lower); printf("%s\n", strtoupper(key)); printf("%s\n", key_lower); printf("%s\n", strtolower(key));
输出为:
ffffffffffffffffffffffffff ffffffffffffffffffffffffff FFFFFFFFFFFFFFFFFFFFFFFFFF FFFFFFFFFFFFFFFFFFFFFFFFFF ffffffffffffffffffffffffff
核心疑问
- 为什么直接打印函数返回值和存储到变量后打印的结果不同?
- 这是否和cs50.h的
string实现有关? - 如何正确存储函数返回值以便后续调用?
问题原因
cs50.h中的string本质是char*的别名,你的转换函数直接修改了传入的text指向的内存区域——也就是原key字符串所在的内存。
当执行string key_upper = strtoupper(key);时,key_upper和key指向同一块内存,此时内容被转为大写;但紧接着调用strtolower(key),又把同一块内存的内容改成小写,所以key_upper和key_lower指向的都是被修改为小写后的内存,打印结果自然一致。
而直接调用printf("%s\n", strtoupper(key));时,函数修改内存后立刻打印,此时还未被后续的strtolower修改,所以能看到大写结果;后续再打印key_lower时,内存已经被strtoupper再次改成大写,输出也随之变化。
解决方案
要解决这个问题,需要在转换函数中创建原字符串的独立副本,修改副本后返回,而非直接修改原字符串的内存。修改后的函数示例:
#include <stdlib.h> #include <string.h> string strtolower(string text) { int length = strlen(text); // 分配内存存储副本,+1用于存放字符串结束符'\0' string lower = malloc(length + 1); if (lower == NULL) // 检查内存分配是否成功 { return NULL; } strcpy(lower, text); // 复制原字符串到副本 for (int i = 0; i < length; i++) { lower[i] = tolower(lower[i]); } return lower; } string strtoupper(string text) { int length = strlen(text); string upper = malloc(length + 1); if (upper == NULL) { return NULL; } strcpy(upper, text); for (int i = 0; i < length; i++) { upper[i] = toupper(upper[i]); } return upper; }
注意:使用完动态分配的字符串后,需要用free()释放内存避免泄漏:
string key_upper = strtoupper(key); string key_lower = strtolower(key); // 使用key_upper和key_lower free(key_upper); free(key_lower);
修改后,key_upper和key_lower会指向各自独立的内存区域,不会互相覆盖,即可得到预期输出。
内容的提问来源于stack exchange,提问作者56492583

