自定义 STRCAT 被太多的参数淹没

Custom STRCAT is overwhelmed by too many arguments

我正在尝试编写一个自定义 strcat 代码,它用 \n 分隔参数,最后一个参数除外,并用 [=15=].

终止字符串

最多 5 个参数工作正常,但如果我尝试传递第六个参数,我会收到一条奇怪的响应:

MacBook-Pro-de-Domingo% ./test ok ok ok ok ok
ok
ok
ok
ok
ok
MacBook-Pro-de-Domingo% ./test ok ok ok ok ok ok
ok
ok
ok
ok
ok
P/Users/domingodelmasok

这是我的自定义 strcat 代码:

char    cat(char *dest, char *src, int current, int argc_nb)
{
    int i = 0;
    int j = 0;

    while(dest[i])
        i++;

    while(src[j])
    {
        dest[i + j] = src[j];
        j++;
    }

    if(current < argc_nb - 1)
        dest[i + j] = '\n';
    else
        dest[i + j] = '[=11=]';

    return(*dest);
}

UPDATE 完成调用功能:

char    *concator(int argc, char **argv)
{
    int i;
    int j;
    int size = 0;
    char *str;

    i = 1;

    while(i < argc)
    {
        j = 0;
        while(argv[i][j])
        {
            size++;
            j++;
        }
        i++;
    }

    str = (char*)malloc(sizeof(*str) * (size + 1));

    i = 1;

   while(i < argc)
   {
        cat(str, argv[i], i, argc);
        i++;
    }

    free(str);
    return(str);
}

这是怎么回事?

谢谢!

编辑:修复错误。

代码有不少问题:

  • sizeof (char) == 1 根据 C 标准。

  • cat() 要求目标是一个字符串(以 [=18=] 结尾),但不附加它本身(current >= argc_nb - 1 除外)。这是一个错误。

  • free(str); return str; 是一个释放后使用错误。如果调用 free(str)str 中的内容将不可挽回地丢失,无法访问。 free(str) 应该简单地删除;这里不合适。

  • C 中的数组索引为 0。但是,concator() 函数会跳过第一个字符串指针(因为 argv[0] 包含用于执行程序的名称)。这是错误的,最终会绊倒某人。相反,让 concator() 添加数组中的所有字符串,但使用 concator(argc - 1, argv + 1);.

  • 调用它

可能还有更多,但在这一点上,我认为使用更合适的方法从头开始重写是合适的。

考虑以下 join() 函数:

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

char *join(const size_t parts, const char *part[],
           const char *separator, const char *suffix)
{
    const size_t separator_len = (separator) ? strlen(separator) : 0;
    const size_t suffix_len = (suffix) ? strlen(suffix) : 0;
    size_t       total_len = 0;
    size_t       p;
    char        *dst, *end;

    /* Calculate sum of part lengths */
    for (p = 0; p < parts; p++)
        if (part[p])
            total_len += strlen(part[p]);

    /* Add separator lengths */
    if (parts > 1)
        total_len += (parts - 1) * separator_len;

    /* Add suffix length */
    total_len += suffix_len;

    /* Allocate enough memory, plus end-of-string '[=10=]' */
    dst = malloc(total_len + 1);
    if (!dst)
        return NULL;

    /* Keep a pointer to the current end of the result string */
    end = dst;

    /* Append each part */
    for (p = 0; p < parts; p++) {

        /* Insert separator */
        if (p > 0 && separator_len > 0) {
            memcpy(end, separator, separator_len);
            end += separator_len;
        }

        /* Insert part */
        if (part[p]) {
            const size_t  len = strlen(part[p]);
            if (len > 0) {
                memcpy(end, part[p], len);
                end += len;
            }
        }
    }

    /* Append suffix */
    if (suffix_len > 0) {
        memcpy(end, suffix, suffix_len);
        end += suffix_len;
    }

    /* Terminate string. */
    *end = '[=10=]';

    /* All done. */
    return dst;
}

逻辑很简单。首先,我们找出每个组件的长度。请注意,separator 仅在部分之间添加(因此出现 parts-1 次),并且 suffix 在最后。

((string) ? strlen(string) : 0 习语只是表示 "if string is non-NULL, strlen(0), otherwise 0"。我们这样做是因为我们允许 NULL 分隔符和后缀,但是 strlen(NULL) 是未定义的行为。)

接下来,我们为结果分配足够的内存,包括未包​​含在长度中的字符串结尾 NUL 字符 [=18=]

为了附加每个部分,我们保持结果指针不变,而是使用一个临时的 end 指针。 (到目前为止,它是字符串的结尾。)我们使用一个循环,将下一部分复制到 end。在第二部分及后续部分之前,我们复制该部分之前的分隔符。

接下来,我们复制后缀,最后是字符串结尾 '[=40=]'。 (重要的是 return 指向字符串开头的指针,而不是结尾,当然;这就是为什么我们保持 dst 指向新的结果字符串,并且 end 在我们附加每个子字符串的位置。)

您可以从命令行使用它,例如使用以下 main():

int main(int argc, char *argv[])
{
    char *result;

    if (argc < 4) {
        fprintf(stderr, "\n");
        fprintf(stderr, "Usage: %s SEPARATOR SUFFIX PART [ PART ... ]\n", argv[0]);
        fprintf(stderr, "\n");
        return EXIT_FAILURE;
    }

    result = join(argc - 3, (const char **)(argv + 3), argv[1], argv[2]);
    if (!result) {
        fprintf(stderr, "Failed.\n");
        return EXIT_FAILURE;
    }

    fputs(result, stdout);
    return EXIT_SUCCESS;
}

如果你将上面的编译为例如example(我用的是gcc -Wall -O2 example.c -o example),然后是运行

./example ', ' $'!\n' Hello world

在 Bash shell 输出中

Hello, world!

(最后换行)。 运行

./example ' and ' $'.\n' a b c d e f g

产出

a and b and c and d and e and f and g

(同样在末尾换行)。 $'...' 只是一个 Bash 习惯用法,用于指定字符串中的特殊字符; $'!\n' 在 Bash 中与 "!\n" 在 C 中相同,$'.\n' 等同于 Bash 在 C 中 ".\n"

(删除部分之间的自动换行符,并允许使用一个字符串而不是一个字符作为分隔符和后缀,是出于两个原因的深思熟虑的选择。主要的一个是阻止任何人只是复制-粘贴这个作为一些练习的答案。第二个是为了表明虽然它可能 听起来 比只使用单个字符更复杂,但实际上它只是很少的额外代码;如果您考虑实际用例,允许将字符串用作分隔符会打开很多选项。)

上面的示例代码只经过了非常轻微的测试,可能包含错误。如果您发现或不同意我上面写的任何内容,请在评论中告诉我,以便我进行审查并根据需要进行修复。