C语言:指针上的字符串连接

4

我编写了这个add_str函数:

void    add_str(char *str1, char *str2, char **res)
{
  int   i;
  int   j = 0;

  res[0] = malloc((strlen(str1) + strlen(str2) + 1) * sizeof(char));
  if (res[0] == NULL)
    return;
  for (i = 0; str1[i]; i++)
    res[0][j++] = str1[i];
  for (i = 0; str2[i]; i++)
    res[0][j++] = str2[i];
  res[0][j] = '\0';
}

它接收两个字符串,str1str2以及一个指向未分配内存的字符串**res的指针。我的函数将str1str2添加到**res中。
我的问题是:是否有一种方法可以不每次都写res[0]来执行与它相关的操作?

1
char *temp = res[0]; ? - user1781290
3
好的,我了解strcpystrcat,它们是C语言中用于操作字符串的函数。 - Some programmer dude
使用strcpy和strcat - Santhosh Pai
5个回答

6

res 是一个指向指针的指针,因此在使用之前需要对其进行解引用。您说得没错,但在这种情况下,res[0] 不是正确的方式。请改用 (*res)


1
你可以直接传递'char* res'而不是'char** res'... 另外,你可以使用strcpy和strcat代替两个'for'循环。 - barak manos
如果OP想继续使用当前的代码,那么括号是必需的,因为*res[j](*res)[j]是不同的东西。 - Some programmer dude
1
@barakmanos 那么原始操作者无法在函数中分配内存。 - Some programmer dude
@JoachimPileborg:你对括号的使用是正确的。我会修正我的回答。谢谢! - Peter Bloomfield
@JoachimPileborg:我改正了。所以要么是'res[0]',要么就是'*res',但我没有看到任何明显的理由可以优选其中一个。 - barak manos
@barakmanos:我认为这主要是可读性的问题。res[0]可以工作,但它错误地暗示了res是一个数组,这可能会导致未来程序员的错误。 - Peter Bloomfield

3

您真正想要的是取消引用:

*res

2
为什么您需要传递一个指向指针的指针呢?您可以将其更改为以下内容:
char *add_str(char *str1, char *str2)
{
    char *res = malloc((strlen(str1) + strlen(str2) + 1));
    if (res == NULL)
        return NULL;

    char *ret = res;

    while(*str1 != 0)
        *res++ = *str1++;

    while(*str2 != 0)
        *res++ = *str2++;

    *res = '\0';
    return ret;
}

它具有相同的效果,而且您不必处理那个丑陋的结构。

这个原型是强制性的,这是一项我想要优化的学校练习。 - Jérémy Pouyet
啊,我想这可能是情况。 - Devolus

2

这并不是直接回答你的问题,而更像是一些通用的编程建议:

为了正确管理程序中所有动态内存操作,你应该尽可能地让x=malloc(...)free(x)两个操作在同一个函数中执行。

如果你正确设计了代码,大多数情况下都可以做到这一点。

在某些情况下,当流程是异步的时,这是不可能做到的。例如,一个函数分配缓冲区,在系统中发生某些事件后,另一个函数(也称为回调)释放缓冲区。

在这种情况下,你仍然应该尝试将这两个操作放在同一个“作用域”(类、文件或模块)中。

在你的编码示例中,函数add_str分配了缓冲区但没有释放它,这意味着其他函数最终将不得不释放它(否则会有内存泄漏)。因此,如果可能的话,你应该尝试在这个函数之外执行malloc


这是无稽之谈。add_str函数的整个目的是分配一个足够大以容纳两个其他字符串的新字符串。所需内存的计算被整洁地包装在需要它的函数中,并且对客户端代码隐藏。您的建议会导致客户端代码混乱不堪,因为需要进行此类计算。当然,必须向用户明确指出她需要稍后释放内存。此外,可以认为如果函数返回指针而不是通过指向指针的指针进行更改,则该函数将更易于使用。 - M Oehm
@M Oehm:首先,它不需要“更改指向指针”,因为它将从用户那里接收一个“普通”指针,并简单地用数据填充指向的缓冲区。其次,这正是像strcpymemcpy等函数的工作方式——它们不会在内部分配新的缓冲区,而是从用户那里接收一个缓冲区,假定它足够大,可以安全地执行“复制”操作。 - barak manos
是的,好(?)旧的字符串操作就是这样工作的。是的,不必要地分配临时内存不是好的风格,即使你释放它们。是的,在C语言中管理内存很难。是的,OP的函数设计有些古怪。尽管如此,这里涉及到的标准函数应该是strcat,可能会发生溢出,或者是strncat,但是在溢出时会写入n + 1个字符,其行为与snprintf不一致。对我来说,看起来OP看到他需要经常计算长度和分配内存,于是写了一个函数。我认为这没有什么问题。 - M Oehm
@M Ohem:这就是为什么我在我的回答开头说“这不是对你的问题的直接回答,而更像是一般性的编码建议”。如果你同意我关于“好用的字符串操作”(如strcpy)的看法,那么我想你也同意这个编码建议。OP可能有特定的原因来实现他所做的事情……或者他可能没有……我提供这个建议只是为了以防万一后者是真的。 - barak manos
这就是为什么我强调,如果不可能的话,那么人们仍然应该努力将malloc/free(或new/delete)放在同一个类、文件或模块中。我举了一个“不可能”的例子,其中流程是异步的。当然,还有其他的例子,比如初始化/终止、构造函数/析构函数等等。从我的经验来看,你应该尽力使它“可见对称”,这样对于每个malloc,你可以轻松地找到相应的free。 - barak manos
显示剩余2条评论

0

我会使用另一个指针变量来保存结果字符串,这样可以避免一直对结果变量进行解引用操作。除此之外,当你只需要简单的指针解引用时,应该避免使用数组下标运算符。我还对你的示例代码进行了一些其他更改,使其更加简洁,如果你愿意可以忽略它们。

void add_str(char *str1, char *str2, char **res) {
    char* result = *res = malloc((strlen(str1) + strlen(str2) + 1) * sizeof(char));
    //Dereferencing a NULL pointer will safely crash your program on any sane system.
    //if (!result) return;
    int   j = 0;    //Since C99 you are allowed to mix code and variable declarations.
    for(int i = 0; str1[i]; i++) result[j++] = str1[i];
    for(int i = 0; str2[i]; i++) result[j++] = str2[i];
    result[j] = '\0';
}

如果充分利用指针,你的代码可以看起来像这样:

void add_str(char *str1, char *str2, char **res) {
    char* result = *res = malloc((strlen(str1) + strlen(str2) + 1) * sizeof(char));
    while(*str1) *result++ = *str1++;
    while(*str2) *result++ = *str2++;
    *result = '\0';
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接