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

C语言凯撒密码程序输出"?"符号问题排查

CS50x P2凯撒密码程序问题分析与修复

我正在学习CS50x的Problem Set 2,尝试实现凯撒密码程序,但还未完成核心加密逻辑就遇到无法正常输出明文的问题,程序运行时输出"?"符号。我是数组新手,已查阅部分资料仍不清楚原因,虽知道可能把代码复杂化了,或许可直接使用用户输入的string,但仍想了解当前代码失效的原因及修复方法。

程序需求:仅接受单个正整数命令行参数,无参数、参数为负数、参数非数字、参数数量多于1个时,均需提示"Usage: ./caesar key"。

原代码

#include <cs50.h>
#include <stdio.h>
#include <ctype.h>
#include <string.h>
#include <stdlib.h>

string cipher(string word, int key);

int main(int argc, string argv[])
{
    // Checks whether the user inputted only 1 argument
    if (argc == 2)
    {
        // Convert argv to an int
        int key = atoi(argv[1]);
        string plainText = get_string("plaintext:  ");

        // Use function to return the (soon to be encrypted) string
        string cipherText = cipher(plainText, key);

        // Print for how long the word is
        int n = strlen(plainText);
        for (int i = 0; i < n; i++)
        {
            // Print the char of the array based upon the iteration of the loop which runs for however long the word is
            printf("%c", cipherText[i]);
        }
        printf("\n");
        // If input is not a positive integer then it will fail
        if (key < 1)
        {
            printf("Usage: ./caesar key\n");
        }
    }
    else
    {
        // If user inputted too many or no inputs then it will fail
        printf("Usage: ./caesar key\n");
    }
    return 0;
}

string cipher(string word, int key)
{
    // Find the length of the word in order to set the size of the array
    // This is so that both strings, the word inputted and the word to return are the same to add to
    int n = strlen(word);

    string cipherText[n];

    // Loop through the input word
    for (int i = 0; i < n; i++)
    {
        // If char[i] is a letter then copy that letter into ciphertext
        if (isalpha(word[i]))
        {
            cipherText[i] = &word[i];
        }
        else
        {
            cipherText[i] = &word[i];
        }
    }
    // Return the array which, for example the input word is nobody
    // Return array[n, o, b, o, d, y]
    return cipherText[0-n];
}

代码失效的核心原因

  1. 数组类型完全错误:CS50的string本质是char*,所以string cipherText[n]定义的是一个指针数组,但你需要的是存储字符的数组来构建新字符串,类型完全不匹配。
  2. 字符赋值逻辑错误:cipherText[i] = &word[i]是把原字符串中每个字符的地址赋值给指针数组元素,根本没有复制字符本身,完全达不到构建新字符串的目的。
  3. 野指针与越界访问:
    • 函数内的局部数组cipherText存在栈上,函数执行结束后会被系统回收,返回它的指针属于返回野指针,访问这种指针会触发未定义行为(输出?就是典型表现)。
    • return cipherText[0-n]中的0-n是负数,属于数组越界访问,进一步加剧了未定义行为。
  4. 参数验证不完整:仅用atoi转换后检查key < 1有漏洞——如果输入12a这种混合字符,atoi会返回12,程序会错误认为参数合法;只有纯数字的正整数才符合要求。

修复方法

1. 修正cipher函数的字符串构建逻辑

把指针数组改成动态分配的字符数组(避免局部数组销毁的问题),直接复制字符并添加字符串结束符:

string cipher(string word, int key)
{
    int n = strlen(word);
    // 动态分配内存,+1是为了存储字符串结束符'\0'
    char *cipherText = malloc(n + 1);
    if (cipherText == NULL) { // 检查内存分配是否成功
        return NULL;
    }

    for (int i = 0; i < n; i++)
    {
        // 先直接复制原字符,后续再添加加密逻辑即可
        cipherText[i] = word[i];
    }
    cipherText[n] = '\0'; // 必须添加结束符,否则无法被识别为合法字符串
    return cipherText;
}

2. 完善命令行参数验证

新增函数检查参数是否全为数字,调整验证顺序(先确保参数合法,再处理输入):

// 新增函数:检查字符串是否全为数字
bool is_all_digits(string s)
{
    for (int i = 0; s[i] != '\0'; i++)
    {
        if (!isdigit(s[i]))
        {
            return false;
        }
    }
    return true;
}

修改main函数的参数验证逻辑:

int main(int argc, string argv[])
{
    // 先检查参数数量和是否全为数字
    if (argc != 2 || !is_all_digits(argv[1]))
    {
        printf("Usage: ./caesar key\n");
        return 1;
    }

    int key = atoi(argv[1]);
    // 再检查是否为正整数
    if (key < 1)
    {
        printf("Usage: ./caesar key\n");
        return 1;
    }

    string plainText = get_string("plaintext:  ");
    string cipherText = cipher(plainText, key);

    if (cipherText != NULL)
    {
        printf("ciphertext: %s\n", cipherText);
        free(cipherText); // 释放动态分配的内存,避免内存泄漏
    }
    return 0;
}

3. 简化输出逻辑

修复后的cipherText是合法字符串,直接用printf("%s", cipherText)打印即可,不需要循环逐个字符输出。

内容的提问来源于stack exchange,提问作者Angus Hay

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 03:35:22