过多的参数使自定义STRCAT不堪重负

时间:2018-07-24 10:34:12

标签: c arguments concat strcat

我正在尝试编写一个自定义import VueScript2 from 'vue-script2' ... mounted() { VueScript2.load('/static/js/jquery.js').then(function () { VueScript2.load('/static/js/plugins.js') }) } ,该自定义strcat用参数\n分隔,最后一个除外,并用\0终止字符串。

它最多可以处理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] = '\0';

    return(*dest);
}

更新:完整的调用功能:

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);
}

这是怎么了?

谢谢!

编辑:修复了错误。

1 个答案:

答案 0 :(得分:1)

代码有很多问题:

  • sizeof (char) == 1符合C标准。

  • cat()要求目标是字符串(由\0终止),但自身不附加(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 '\0' */
    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 = '\0';

    /* All done. */
    return dst;
}

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

(string) ? strlen(string) : 0惯用语的意思是“如果string为非NULL,则strlen(0),否则为0。我们这样做,因为我们允许使用NULL分隔符和后缀,但是strlen(NULL)是未定义的行为。)

接下来,我们为结果分配足够的内存,包括长度不包括的字符串结尾NUL字符\0

要附加每个部分,我们保持结果指针完整无缺,而是使用临时end指针。 (到目前为止,这是字符串的结尾。)我们使用循环,将下一部分复制到end中。在第二部分和后续部分之前,我们在该部分之前复制分隔符。

接下来,我们复制后缀,最后是字符串结尾'\0'。 (当然,返回指向字符串开头而不是结尾的指针很重要;这就是为什么我们保持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'是C中".\n"的Bash等效。

(删除部件之间的自动换行符,并允许使用字符串而不是仅使用一个char作为分隔符和后缀,这是有意的选择,其原因有两个。主要的原因是阻止任何人仅复制粘贴此内容作为第二种方法的答案,第二种方法是显示虽然听起来声音可能比仅使用单个字符更为复杂,但实际上它几乎不需要额外的代码;并且如果您考虑实际使用情况下,允许使用字符串作为分隔符会带来很多选择。)

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