在C语言中将字符追加到字符指针中

10

我想将一个 char 添加到一个 char* 中。

例如,我有 char *word = " ";char ch = 'x';

我使用以下方法进行添加:append(word, ch); ...

void append(char* s, char c)
{

    int len = strlen(s);
    s[len] = c;
    s[len+1] = '\0';
}

这段代码会导致段错误,我理解原因是s[len]越界了。那么如何才能让它正常工作呢?如果我使用char word[500];,我也需要经常清空它,那么当其中添加了一些字符后,如何清空呢?strlen的结果始终是500吗?谢谢。


2
"因为 s[len] 超出了边界。" <- 对于初学者来说,你发现得非常好!恭喜。然而,你还有另一个错误——字符串字面值是只读的,你不能修改它的内容。" - user529758
2
@snowyowl s[len + 1] 超出了范围。 s[len] 是空字节,不会被 strlen 计算在内。 - pmr
@pmr 是的,从技术上讲是对的。我应该写成 sizeof(s) - user529758
当将字符串字面值的const属性传递给函数时,难道不应该触发编译器警告吗? - Offirmo
@H2CO3 绝对不是,那可能是4或8,并反映了指针的大小。 - djechlin
6个回答

11

典型的 C 编程做法通常是这样的:

//returns 1 if failed, 0 if succeeded 
int  append(char*s, size_t size, char c) {
     if(strlen(s) + 1 >= size) {
          return 1;
     }
     int len = strlen(s);
     s[len] = c;
     s[len+1] = '\0';
     return 0;
}

当将一个数组传递给函数以修改时,函数在编译时无法知道它有多少空间。在C语言中通常的做法是同时传递数组的长度,函数会信任这个边界并在不能完成工作的空间内失败。另一个选项是重新分配并返回新的数组,你需要返回char*或以char**作为输入,但在这种情况下必须仔细考虑如何管理堆内存。但如果不重新分配,则你的函数必须某种方式在没有剩余空间可供添加时失败,具体如何失败取决于你。


这是一个糟糕的答案,因为你不知道 *s 是否包含字符串字面量。它们通常存储在只读内存中,如果你尝试修改它们可能会导致访问冲突。你的代码可能能够工作,但被认为是未定义行为。 - user1888162
@user1888162 如果你传入一个很大的数字作为大小,它也是未定义行为。 - djechlin
我收到的错误信息是:'append' 在没有强制转换的情况下从指针中获取整数 [-Werror=int-conversion],名字,strlen(nickname)," "); - bomben

5

在C语言中,直接原地追加字符串是比较困难的。可以尝试类似以下的方法:

char *append(const char *s, char c) {
    int len = strlen(s);
    char buf[len+2];
    strcpy(buf, s);
    buf[len] = c;
    buf[len + 1] = 0;
    return strdup(buf);
}

注意:完成操作后,请释放返回的字符串。

顺便说一句:它可能会导致段错误,因为你传递的字符串存储在只读内存中。但你是对的,你也写到了末尾([len+1] 写入,而不是 [len])。


根据编译器版本的不同,char buf[len+2] 可能无法编译。 - Tom Tanner

3
如果您正在传递
append("foo", 'X');

它会崩溃,因为foo通常被放在只读存储中。即使不是这样,它也很可能会覆盖某些坏东西!在这种情况下,如果编译器正常,它应该会警告您从const char *转换为char *,这应该是个线索。


0

使用snprintf进行附加是最好的选择

包含stdio.h头文件

#include <stdio.h>

那么

void append(char* s, char c)
{

    int len1 = strlen(s);
    int len2 = strlen(c);
    int len = len1 + len2;

    snprintf(s, len, "%s%s", s, c);
}

使用这个函数append(" ", "x")将会得到" x",而append("", "x")将会得到"x"

如果你想在单词之间加上分隔符,可以这样做

void append(char* s, char c)
{
     int len1 = strlen(s);
     int len2 = strlen(c);
     int len = len1 + len2;

     snprint(s, len, "%s_%s", s, c);
} 

使用这个函数append("ab", "x")将会得到"ab_x",而append("", "x")将会得到"_x"


0

是的,你所做的假设 - 几乎 - 是正确的 - 崩溃可能是因为你试图写入字符串边界之外(实际上只有s[strlen(s) + 1]超出了边界,因为s[strlen(s)]仍然是一个有效的位置 - 终止NUL字节存储在那里)。但你也不能修改字符串常量,因为它通常位于进程内存的某个只读部分。这两种操作都会导致未定义行为的调用,可能会导致崩溃。你可以通过将字符串复制到动态分配的存储器中,然后修改副本来解决这个问题。此外,你应该在函数参数中使用const char *,因为char *表明只读字符串无法传递。

char *append(const char *orig, char c)
{
    size_t sz = strlen(orig);
    char *str = malloc(sz + 2);
    strcpy(str, orig);
    str[sz] = c;
    str[sz + 1] = '\0';
    return str;
}

此外,当不再需要返回的字符串时,请不要忘记使用free()释放它。

0

你不能安全地向任意字符串追加内容,因为首先,字符串常量往往存储在只读内存中,因此尝试写入它们可能会导致分段错误;其次,你无法保证如果他们传递给你一个缓冲区,你没有超出它的末尾。

特别是,如果你这样做 char x[500];

没有保证strlen(x)将返回500。它将返回从x的开头开始计数到达null之前必须向前计数多少个字符。它可以返回0、1...500、501...等,具体取决于x中的内容。

实际上,你唯一的选择是调用带有要追加的缓冲区大小的append(这样你就可以在缓冲区已满时执行适当的操作),或者使append每次被调用时分配一个新的缓冲区,在这种情况下,你当然需要释放缓冲区。


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