【问题标题】:Concatenation in C as pyramidC中的连接作为金字塔
【发布时间】:2012-11-12 18:02:46
【问题描述】:

我在 Python 上工作过,我知道要将字符串连接为 --- 你可以简单地用字符“-”“乘”一个整数,所以在这种情况下,我们可以像 result=3* 一样简单地做“-”。我一直在尝试用 C 语言来做这件事。

我如何在 C 中做到这一点,例如:

#include <stdio.h>
int main (void)
{

    int height=0;
    int n=0;
    char symbol='#';
    printf("Height: ");
    scanf("%d",&height);
    n=height+1;
    while (n>=2)
        {
        printf("symbol*n");
        n=n-1;
        }
    return 0;
}

所以它打印一个高度为 5 的倒金字塔:

#####
####
###
##
#

提前谢谢你!!

【问题讨论】:

    标签: c string concatenation


    【解决方案1】:

    没有内置的方法可以重复输出。您必须自己编写代码。

    void multiputchar(char c, size_t count)
    {
        for (int i = 0; i < count; i++)
            putchar(c);
    }
    

    对于一个库函数,你可能关心putchar() 是否失败,所以你最好写:

    int multiputchar(char c, size_t count)
    {
        for (int i = 0; i < count; i++)
        {
            if (putchar(c) == EOF)
                return(EOF);
        }
        return (unsigned char)c;
    }
    

    但如果返回值总是被忽略,第一个更简单。强制转换是必要的,以确保如果您的 char 类型已签名,您可以区分 ÿ 的失败和成功输出(y-变音符号,U+00FF,带分音符号的拉丁小写字母 Y,8859-1 中的 0xFF和相关的代码集)。

    【讨论】:

    • 哇,答案很快,谢谢!
    • return EOF; 的风格比 return(EOF); 更好,这会诱使新手开发人员认为 return 是某种内置函数。
    • @djechlin 我想极端新手可能会犯错,但现在是 2012 年,每个人都应该有语法高亮。此外,虽然这里可能是多余的,但我个人喜欢将复杂的回报包裹在括号中,也许他只是陷入了这种习惯
    【解决方案2】:

    在 C 中,您还需要处理您使用的内存。因此,如果您想要一个“---”字符串,您还需要为该字符串分配 空间。分配空间后,您将使用给定的字符填充它。

    然后,您必须释放该区域。

    所以:

    char *charmul(char c, int n)
    {
        int i;
        char *buffer; // Buffer to allocate
        buffer = malloc(n+1); // To store N characters we need N bytes plus a zero
        for (i = 0; i < n; i++)
            buffer[i] = c;
        buffer[n] = 0;
        return buffer;
    }
    

    然后我们需要添加错误检查:

    char *charmul(char c, int n)
    {
        int i;
        char *buffer; // Buffer to allocate
        buffer = malloc(n+1); // To store N characters we need N bytes plus a zero
        if (NULL == buffer)
             return NULL;
        for (i = 0; i < n; i++)
            buffer[i] = c;
        buffer[n] = 0;
        return buffer;
    }
    

    您的来源将变为:

    #include <stdio.h>
    
    // charmul here
    
    int main (void)
    {
    
        int height=0;
        int n=0;
        char symbol='#';
        printf("Height: ");
        scanf("%d",&height);
        n=height+1;
        while (n>=2)
            {
                char *s;
                s = charmul(symbol, n);
                printf("%s\n", s);
                free(s); s = NULL;
                n=n-1;
            }
        return 0;
    }
    

    另一种实现方法是减少malloc 的数量,以提高性能。为此,您还需要将指向前一个缓冲区的指针传递给函数,如果更短,可以回收而无需进一步的malloc,如果更长,将是free'd 和重新分配(或者可以使用realloc)。然后,您将只对最后一个未回收的值执行free()

    char *charmul_recycle(char c, int n, char *prevbuf)
    {
        int i;
        if (prevbuf && (n > strlen(*prevbuf)))
        {
            free(prevbuf); prevbuf = NULL;
        }
        if ((NULL == prevbuf)
        { 
            prevbuf = malloc(n+1); // To store N characters we need N bytes plus a zero
            if (NULL == prevbuf)
                return NULL;
        }
        for (i = 0; i < n; i++)
            prevbuf[i] = c;
        prevbuf[n] = 0;
        return prevbuf;
    }
    
    
    char *buffer = NULL;
    
    while(n > 2)
    {
         buffer = charmul_recycle(symbol, n, buffer);
         if (NULL == buffer)
         {
             fprintf(stderr, "out of memory\n");
             abort();
         }
         printf("%s\n", buffer);
         n--;
    }
    

    当然,整个事情可以通过一次直接分配和逐步缩短字符串来完成(通过将s[n] 设置为零),但是我们不会使用“生成多个字符”功能:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    
    int main (void)
    {
        char *string;
        int height=0;
        int n=0;
        char symbol='#';
        printf("Height: ");
        scanf("%d",&height);
        n=height+1;
        string = malloc(n);        // allocate just enough memory (n is height+1)
        memset(string, symbol, n); // fill string with symbols
    
        while (--n) // with n ever decreasing...
        {
            string[n] = 0;            // Truncate string after n characters
            printf("%s\n", string);   // output string
        }
        free(string); // string = NULL; // finally free the string
        return 0;
    }
    

    更新(感谢 Jonathan Leffler):上面有一个潜在的危险“过度优化”。在每次使用 string 之前,string 正确地以零结尾(“string[n] = 0;”)。 但是 确实,我已经分配了一个字符串变量并用东西填充它,并且没有立即零终止它。在上面的代码中,一切都很完美。这仍然是不好的编码习惯,因为如果代码被重用并删除了循环,并且字符串用于其他目的(在 this 情况下不太可能,但仍然......),未终止的 @ 987654335@ 可能会成为一个微妙的错误。

    最快的解决方法是在分配后终止:

        string = malloc(n);        // allocate just enough memory (n is height+1)
        memset(string, symbol, n-1); // fill string with symbols
        string[n-1] = 0;           // zero-terminate string
    

    我现在偏离了原始主题,但这意味着在这种情况下,字符串正确地以零结尾两次。为避免这种情况,可以将代码重写为“剪切和粘贴安全”版本,还可以更清楚地显示额外的零作为 n 的添加:

        n=height;                   // Number of characters
        string = malloc(n+1);       // Allocate memory for characters plus zero
        memset(string, symbol, n);  // Store the characters
        string[n] = 0;              // Store the zero
        while (n)                   // While there are characters
        {
            printf("%s\n", string); // Print the string
            string[--n] = 0;        // Reduce it to one character less than before
        }
    

    该循环现在接受任何具有有意义n 的有效字符串,如果将其删除,则该字符串将保持可用状态。

    【讨论】:

    • 感谢您的快速回答! :)
    • 由于您没有使用calloc(),因此您需要确保在最后一个版本的代码中的memset() 之后(或之前)字符串为空终止。
    • 它是 - string[n] = 0; 看到了这一点(现在我仔细观察,我不必要地分配了一个额外的字节)。
    • @JonathanLeffler:即便如此,代码还是很糟糕。感谢您的提醒。我添加了一个题外话,也许有人会感兴趣。
    • 我同意您的代码在给定的上下文中在技术上是安全的,但也同意它不是复制粘贴安全的。您很好地解释了我忽略的内容。
    猜你喜欢
    • 2014-09-01
    • 2014-05-11
    • 1970-01-01
    • 1970-01-01
    • 2020-10-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多