You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何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;
}

问题原因
  1. 局部数组生命周期失效:cptxt_arr是encrypt函数内的局部栈数组,函数执行完毕后,栈上的局部变量会被销毁,内存被释放。此时返回的string(本质是char*)指向的是已失效的内存,属于野指针。
    • 用%c能打印首个字符只是巧合——内存刚被释放还未被覆盖,侥幸能读到数据,但这是未定义行为,完全不可靠。
    • 用%s打印时,printf会从指针位置一直读取到\0终止符,但此时内存已无效,要么读不到终止符,要么读到乱码,最终导致无输出或程序异常。
  2. 缺少字符串终止符:即便数组内存未失效,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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.16 19:22:42