在C ++中加入字符串时出错

时间:2015-10-01 14:16:10

标签: c++ string

我正在尝试将一个数组中包含的所有字符串连接到一个字符串中,所以我想出了这段代码:

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

char *join_strings(const char *array[])
{
    int n_array (sizeof (array) / sizeof (const char *));

    int i;
    int total_length;
    char *joined;
    char *end;
    int *lengths;

    lengths = (int *)malloc (n_array * sizeof (int));
    if (!lengths) {
        fprintf (stderr, "Malloc failed.\n");
        exit (EXIT_FAILURE);
    }

    total_length = 0;

    for (i = 0; i < n_array; i++) {
        lengths[i] = strlen (array[i]);
        total_length += lengths[i];
    }

    joined = (char *)malloc(total_length + 1);

    if (!joined) {
        fprintf (stderr, "Malloc failed.\n");
        exit (EXIT_FAILURE);
    }

    end = joined;

    for (i = 0; i < n_array; i++) {
        int j;
        for (j = 0; j < lengths[i]; j++) {
            end[j] = array[i][j];
        }
        end += lengths[i];
    }
    end[total_length] = '\0';

    char *result = joined;

    free (lengths);
    free (joined);

    return result;
}

int main()
{
    const char *lol0 = "First";
    const char *lol1 = "Second";
    const char *fullString[] = { lol0, lol1 };
    const char *joined = join_strings(fullString);

    puts(joined);

    return 0;
}

但由于某种原因它只输出“First”,不知道问题可能是什么。 :/ 提前谢谢!

4 个答案:

答案 0 :(得分:0)

sizeof(array)等于sizeof(const char **)n_array未准确计算。
您必须将数组的长度作为参数传递。

还有另一个错误。由于缓冲区仍在使用,因此您不能在函数free (joined);中执行join_strings

固定代码:

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

char *join_strings(const char *array[], int n_array)
{

    int i;
    int total_length;
    char *joined;
    char *end;
    int *lengths;

    lengths = malloc (n_array * sizeof (int));
    if (!lengths) {
        fprintf (stderr, "Malloc failed.\n");
        exit (EXIT_FAILURE);
    }

    total_length = 0;

    for (i = 0; i < n_array; i++) {
        lengths[i] = strlen (array[i]);
        total_length += lengths[i];
    }

    joined = malloc(total_length + 1);

    if (!joined) {
        fprintf (stderr, "Malloc failed.\n");
        exit (EXIT_FAILURE);
    }

    end = joined;

    for (i = 0; i < n_array; i++) {
        int j;
        for (j = 0; j < lengths[i]; j++) {
            end[j] = array[i][j];
        }
        end += lengths[i];
    }
    end[total_length] = '\0';

    char *result = joined;

    free (lengths);

    return result;
}

int main()
{
    const char *lol0 = "First";
    const char *lol1 = "Second";
    const char *fullString[] = { lol0, lol1 };
    char *joined = join_strings(fullString, 2);

    puts(joined);
    free(joined);

    return 0;
}

注意:

  • 此代码是C,而不是C ++。我删除了malloc的返回值的强制转换,这似乎在这个网站上很讨厌。这将导致C ++中的编译错误。
  • free缓冲区,joined函数中main的类型不应该是const char*。 gcc警告它。

答案 1 :(得分:0)

很难绝对肯定,但是从性能方面来说,知道长度的胜利可能小于分配堆内存只是为了保持字符串长度的成本。

所以,为了好玩,让我们在没有额外长度缓冲的情况下做到这一点:

char * join_strings(const char *array, size_t array_len)
{
  size_t out_len;

  for(size_t i = out_len = 0; i < array_len; ++i)
    out_len += strlen(array[i]);

  char *out = malloc(out_len + 1);
  if(out != NULL)
  {
    char *put = out;
    for(size_t i = 0; i < array_len; ++i)
    {
      for(const char *src = array[i]; *src;)
        *put++ = *src++;
    }
    *put = '\0';
  }
  return out;
}

顺便说一句,上面是C99。我删除了错误消息,返回NULL可能就足够了。它没有经过测试,但不应该太远。 :)

我的观点基本上是有时更短的代码更容易使用。

答案 2 :(得分:0)

如MikeCAT所示,以下内容并不符合您的想法......但为什么?

char *join_strings(const char *array[])
{
    int n_array (sizeof (array) / sizeof (const char *));

您正在将*fullString[]传递给join_strings函数,但由于指针衰减,join_strings得到的有效**fulString。每次将数组传递给函数(例如a[])时,它都会衰减为指针(例如*a)。对于一组指针也是如此。

这正是您将main函数语法视为:

的原因
int main (int argc, char *argv[])

int main (int argc, char **argv)

实际上,主要接收的是第二种情况,但允许的语法是。因此,当您将*fullString[]传递到join_strings函数时,指针衰减会确保join_strings收到**fullString。这表明如下:

    int n_array (sizeof (array) / sizeof (const char *));

有效int n_array 8/81sizeof array / sizeof *array仅适用于声明和定义array的范围。一旦将其传递给另一个函数,指针衰减就会使其不正确。

然而,你的代码并不遥远。有一些变化要做,但除了需要的这一点学习之外,你接近一个有效的字符串连接:

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

char *join_strings (const char **array, size_t n_array)
{
    // int n_array (sizeof (array) / sizeof (const char *));

    size_t i;
    size_t total_length;
    char *joined;
    char *end;
    size_t *lengths;

    // lengths = (int *) malloc (n_array * sizeof (int));
    lengths = malloc (n_array * sizeof *lengths);
    if (!lengths) {
        fprintf (stderr, "Malloc failed.\n");
        exit (EXIT_FAILURE);
    }

    total_length = 0;

    for (i = 0; i < n_array; i++) {
        lengths[i] = strlen (array[i]);
        total_length += lengths[i];
    }

    joined = malloc (total_length + 1);

    if (!joined) {
        fprintf (stderr, "Malloc failed.\n");
        exit (EXIT_FAILURE);
    }

    end = joined;

    for (i = 0; i < n_array; i++) {
        size_t j;
        for (j = 0; j < lengths[i]; j++) {
            end[j] = array[i][j];
        }
        end += lengths[i];
    }
    // end[total_length] = '\0';   /* ALWAYS run valgrind (Note3) */
    joined[total_length] = '\0';

    // char *result = joined;

    free (lengths);
    // free (joined);

    return joined;
}

int main ()
{
    const char *lol0 = "First";
    const char *lol1 = "Second";
    const char *fullString[] = { lol0, lol1 };
/*const */char *joined = join_strings (fullString, sizeof fullString/sizeof *fullString);

    puts (joined);
    free (joined);

    return 0;
}

注意您函数中intsize_t的所有长度和计数器变量的变化。您需要尝试将您的类型与他们将持有的实际数字范围相匹配。由于没有负长度并且您只使用正计数器来计数,size_t将帮助编译器在您尝试错误地使用变量时发出警告。

注意2:不要在[{1}}上投放回报。它只不过是内存块开头的地址。它没有类型。转换malloc提供的好处,但可以屏蔽编译器可能会生成的错误或警告,以便在您要做一些愚蠢的事情时警告您。

注3:如果要动态分配内存始终通过valgrind运行代码以确保正确使用内存。没有理由不这样做,这很简单:

malloc

它不仅会检查您的分配和释放,还会检查您是否尝试访问已分配块之外的内存。 (如果使用符号启用编译valgrind ./myprogname ,valgrind将显示与错误关联的行号。这里valgrind显示:

-g

咦?仔细查看您是如何尝试空终止==27700== Invalid write of size 1 ==27700== at 0x40090E: join_strings (ptrdecay.c:45) ==27700== by 0x400960: main (ptrdecay.c:60) ==27700== Address 0x51e00a6 is 10 bytes after a block of size 12 alloc'd ==27700== at 0x4C29964: calloc (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so) ==27700== by 0x400838: join_strings (ptrdecay.c:29) ==27700== by 0x400960: main (ptrdecay.c:60) 的。那时end的价值是多少? end(或等效编写的end[total_length])将null终结符放在哪里?

*(end + total_length)

您应该修复另一个警告,可以使用==27700== Address 0x51e00a6 is 10 bytes after a block of size 12 alloc'd calloc或将其值初始化为joined,然后再填充它们。这是所有valgrind在抱怨:

0

消除此消息的最简单方法是:

==27647== Conditional jump or move depends on uninitialised value(s)
==27647==    at 0x4C2ACF8: strlen (in /usr/lib64/valgrind/vgpreload_memcheck-amd64-linux.so)
==27647==    by 0x4E9DE2B: puts (in /lib64/libc-2.18.so)
==27647==    by 0x40092B: main (ptrdecay.c:62)

尝试并看看......当您解决所有问题并再次运行valgrind时,您将看到每次都想看到的内容:

    joined = calloc (1, total_length + 1);

答案 3 :(得分:0)

首先,这段代码非常糟糕 - 所有这些手册malloc和裸const char *阵列都是非常糟糕的风格。

其次,您只是传递第一个数组元素的地址。尽管有误导性[],但在这种情况下,C和C ++都会将参数衰减为指针,并且数组大小信息会丢失。

但是,如果您的数组大小确实是编译时常量而不是动态分配,那么 可以在不改变您的有效接口的情况下使其工作:

template <size_t n_array>
char *join_strings(const char *(&array)[n_array])
{
    // other ghastly code omitted for aesthetic reasons
}

将完全按照所示的方式调用,并且(一旦修复双重免费错误)实际上可以正常工作。

实际C ++中的最小解决方案是:

#include <algorithm>
#include <iostream>
#include <iterator>
#include <string>
#include <sstream>
#include <vector>

template <typename InputIterator>
std::string join_strings(InputIterator begin, InputIterator end) {
    std::ostringstream os;
    std::copy(begin, end, std::ostream_iterator<std::string>(os));
    return os.str();
}

int main() {
    std::vector<std::string> arr = {"First","Second"};
    std::string joined = join_strings(begin(arr), end(arr));
    std::cout << joined << '\n';
}

这个有一些冗余分配,但实际上是正确的,同样重要的是非常简单。如果您对自己进行了分析并且确实需要更快的速度,请将ostringstream替换为预先调整大小的std::stringappend