【问题标题】:Reversing a string without using any library function在不使用任何库函数的情况下反转字符串
【发布时间】:2020-04-19 11:17:31
【问题描述】:

所以我想编写一个程序来反转从用户那里获取的字符串。

这是我的源代码:

#include <stdio.h>
#include <stdlib.h>

int main(int argv, char *argc[]) {
    if (argv != 2) {
        printf("Please enter the number of elements in your string!\n");
        return 1;
    }
    int n = atoi(argc[1]);
    char *c = malloc((sizeof(char) * n) + 1);
    char *o = malloc((sizeof(char) * n) + 1);
    printf("Enter your string - ");
    fgets(c, n, stdin);
    for (int i = 0; i < n + 1; i++) {
        *(o + i) = *(c + (n - 1) - i);
    }
    printf("%s\n", o);
    free(c);
    free(o);
}

但是打印出来的输出什么都不是!

谁能指出我的代码有什么问题?

【问题讨论】:

  • 可能(未初始化的)c[n - 1] 为零,使o 成为空字符串"\0......"?
  • fgets(c, n, stdin); -> fgets(c, n+1, stdin);
  • @kaylum ...或者不要将1添加到malloc
  • @pmg 可能。虽然我认为 OP 打算将 n 用作字符串中的字符数。
  • 为什么要交换argvargc??? 强烈建议 OP...继续使用int main(int argc, char **argv)...不要交换主要参数标准标识符。

标签: c string pointers


【解决方案1】:

阻止代码工作的问题是oc 容器的大小不匹配,以及fgets 中的读取大小,因为fgets 以空值终止从输入读取的字符串。

假设n = 6 在您读取字符串时,fgets 将第 6 个字符替换为空终止符,当您反转它时,空终止符现在将成为 o 中的第一个字符,基本上,它将是一个空字符串,因为字符串是以 null 结尾的字符数组或字节数组。

要解决这个问题,请给 fgets 分配空间的大小。

fgets(c, n + 1, stdin);

当你完成反转时,null-terminate o

*(o + n) = '\0';

或者

o[n] = '\0'; //you can use this notation which is more readable than dereferencing

小问题:

  • 您切换了主要参数的名称这一事实。通常是int main(int argc, char * argv[])。这可能会让阅读您的代码的人感到困惑。
  • char *c = malloc((sizeof(char) * n) + 1); 有不必要的逻辑,可以是char *c = malloc(n + 1);char 是一个字节。
  • 程序的逻辑存在潜在问题,当输入的字符串比您要求用户的字符串短时,输出将不是您想要的,您可以付出额外的努力来防止您的代码出现错误输入.

考虑所有因素,以您的代码为基础,它可能是这样的:

//Only the changed parts are represented, the rest is the same
#include <string.h> //for strlen
    //...
    if (argc != 2 || atoi(argv[1]) < 1) { //n must be positive (I switched argv and argc)
        printf("Please enter the number of elements in your string!\n");
        return 1;
    }

    size_t n = atoi(argv[1]); //size_t type more suited for sizes
    char *c = malloc(n + 1);
    char *o = malloc(n + 1);
    //...
    fgets(c, n + 1, stdin);   //as stated n + 1 size argument 

    if(strlen(c) < n) {    //if the length of inputed string is shorter than intended
        puts("The string size shorter than stated!");
        return 1;
    }
    //...
    for (size_t i = 0; i < n + 1; i++) { //repalced int iterator type whith size_t
    //...
    o[n] = '\0'; //null terminate o
    //...

【讨论】:

    【解决方案2】:

    你的程序有多个问题:

    • 为什么需要字符数的参数?假设最大长度并使用自动存储在main() 中定义char 数组会简单得多。

    • 语句char *c = malloc((sizeof(char) * n) + 1); 计算了正确的分配大小,但这是偶然的,因为sizeof(char) 总是1。你应该写char *c = malloc(n + 1);char *c = malloc(sizeof(*c) * (n + 1));

    • 由于fgets() 将存储换行符,您应该将分配大小增加 1 以避免将换行符留在输入流中,但您需要避免在要反转的字符中包含换行符。在所有情况下,您必须将数组的大小传递给fgets(),而不是n,因为fgets() 只会将n - 1 字节存储到数组中并将c[n - 1] 设置为空终止符,这会导致反转字符串以空终止符开头,使其成为空字符串。

    • 您不测试fgets() 是否成功读取标准输入。

    • 您不计算要反转的字符数。如果用户输入的字符少于n,您将转置超出输入的字节,可能是空字节,这将使反转的字符串为空(这是您观察到的一个很好的解释)。

    • 转置循环应该针对i = 0i &lt; n 进行迭代,而不是n + 1

    • 您没有在目标数组的末尾设置空终止符。该数组分配有malloc(),因此未初始化。

    这是修改后的版本:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main(int argv, char *argc[]) {
        if (argv != 2) {
            printf("Please enter the maximum number of characters in your string!\n");
            return 1;
        }
        int n = atoi(argc[1]);
        if (n < 1) {
            printf("Invalid number of characters: %d\n", n);
            return 1;
        }
        // allocate 1 extra byte for the newline, one more for the null terminator
        char *buf = malloc(n + 2);
        char *out = malloc(n + 2);
        printf("Enter your string: ");
        if (!fgets(buf, n + 2, stdin)) {
            printf("no input\n");
            return 1;
        }
        // get the number of characters in the input before the newline, if any
        int len;
        for (len = 0; buf[len] && buf[len != '\n'; n++)
            continue;
        // if you can use the library function `strcspn()`, replace the for loop with this:
        //len = strcspn(buf, "\n");
    
        // copy the string in reverse order
        for (int i = 0; i < len; i++) {
            out[i] = buf[len - 1 - i];
        }
        // set the null terminator
        out[len] = '\0';
        printf("%s\n", out);
        free(buf);
        free(out);
        return 0;
    }
    

    您也可以从 IDE 中在程序终止后立即关闭终端窗口的系统上运行程序。这将阻止您看到输出。在return 0; 之前添加getchar(); 以解决此问题或从shell 窗口手动运行程序。

    【讨论】:

      【解决方案3】:

      我的代码有什么问题!

      关键功能问题包括:

      fgets()读取"12345\n"至少需要6个字节,7个更好1

      o[] 上缺少空字符

      fgets(c, n, stdin)c[n-1]null 字符 并且带有“反转”,因为代码假定 n 字符,c[n-1] 变为 o[0],因此代码打印空字符串.

        // fgets(c, n, stdin);  // too small
        fgets(c, n + 1, stdin);  
      
        // add before printing.
        o[n] = '\0';
      

      存在其他小问题。


      1 也增加分配,然后从输入中删除 \n。

      【讨论】:

        【解决方案4】:

        如果值 n 与数量不匹配,您的程序将失败 输入字符串中的字符主要是因为你没有初始化 您分配的内存。

        e.g.
        
        n = 10
        c = "hello"  
        

        c 的长度为 5,但您已分配 11 个字节,因此 hello\n\0 之后的字节在 c 中未初始化,因为 fgets 不会为您填写这些字节。

        在内存中看起来像这样

            +---+---+---+---+---+---+---+---+---+---+---+
        c ->| h | e | l | l | o |\n |\0 |   |   |   |   |
            +---+---+---+---+---+---+---+---+---+---+---+
        

        当你把绳子转过来时

        *(o + i) = *(c + n - 1 - i)
        

        由于您使用n 作为开始复制字符的偏移量,因此您开始的不是“hello\n\0”复制 位置 9 (10 - 1 - 0) 并将其作为第一个字符放在 o 中, 但由于所有c 都没有初始化任何东西都可以在那里,甚至\0 这可以解释为什么你 不要打印任何东西。

        最好是在你读取字符串后用一个简单的 for 循环计算字符串的长度

        int len = 0;
        for (len = 0; c[len] && c[len] != '\n'; ++len);
        

        然后使用len作为偏移量而不是n

        *(o + i) = *(c + len - 1 + i)
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2017-09-20
          • 1970-01-01
          • 1970-01-01
          • 2011-02-06
          • 1970-01-01
          • 2021-12-09
          相关资源
          最近更新 更多