【问题标题】:String operations cause segfault C [duplicate]字符串操作导致段错误C [重复]
【发布时间】:2021-03-16 10:01:14
【问题描述】:

我正在尝试“深拷贝”一个字符串,以便我可以对一个副本执行操作,同时保留原始副本。这是我得到的基本示例,由于某种原因,strncpy 调用会导致段错误。请帮忙

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

int main() {
    char* stringA = "someVeryinTeresTingString";
    char* stringB = malloc(sizeof(char) * strlen(stringA));
    
    printf("A: %s, B: %s\n", stringA, stringB);
    
    for (int i = 0; i < strlen(stringA); i++) {
        stringB[i] = tolower(stringA[i]);
    }
    
    printf("A: %s, B: %s\n", stringA, stringB);
    
    strncpy(stringA, stringB, strlen(stringA) - 1);
    
    printf("A: %s, B: %s\n", stringA, stringB);
}

【问题讨论】:

  • 提示:不要忘记 NUL 终止符。有的话别忘了strdup()
  • malloc() 可能会返回垃圾数据。 stringB 不是有效的 C 字符串,除非您将其清除或放入其中。
  • stringA 也不是可变的,它是一个静态字符串,应该被认为是const char*。您需要一个可以写入的缓冲区,因此分配一个新的缓冲区,或者如前所述,只需使用 strdup()
  • 是的。内联字符串文字的特殊之处在于它们不存储在可变内存中,它们实际上是不可变的。您需要复制它们,因此您可以使用char stringA[] = "..." 创建一个本地数组,而不是指针,或者您可以通过strdup() 创建一个副本。
  • 谢谢!没有在下面看到你的帖子:P

标签: arrays c char


【解决方案1】:

最简单的解决方法是制作该字符串文字的本地副本:

char stringA[] = "someVeryinTeresTingString";

其他一切都一样。

请注意,在原始代码中,您有一个指向不可变内存的指针,而在此版本中,您有一个本地(堆栈)数组,该数组使用该字符串的副本进行初始化。

另外需要注意的是,如果您要复制和操作 C 字符串,请执行以下操作:

char* stringB = strdup(stringA);
  
for (int i = 0; i < strlen(stringB); ++i) {
    stringB[i] = tolower(stringB[i]);
}

或者通过避免所有这些昂贵的strlen() 调用来提高效率:

char* stringB = strdup(stringA);
  
for (char* p = stringB; *p; ++p) {
    *p = tolower(*p);
}

【讨论】:

  • printf("B: %s\n", stringB); 仍然是 UB,因为 stringB 不是 字符串
  • @chux-ReinstateMonica 我在评论中提到了这一点。这里没有涉及。
  • 所有 3 个 printf("B: %s\n", stringB); 都是 UB,因为 stringB 缺少空字符或空格。
  • @Lundin POSIX 中的某些内容是公认的错误,例如 gets(),但我不确定 stdrup() 是其中之一。
  • 无论如何,如果您的设置中缺少strdup,则对其进行编码非常简单。它存在于其他任何地方(99.9% 的时间)。
【解决方案2】:

这一行:

char* stringB = malloc(sizeof(char) * strlen(stringA));

应该是这样的:

char* stringB = malloc(sizeof(char) * (strlen(stringA) + 1));

那么你就可以复制stringA末尾的\0了

另外,您想复制到文字字符串 - 这是分段错误

char *strncpy(char *dest, const char *src, size_t n)

【讨论】:

  • 为什么我不能复制字符串文字?
  • 您可以复制字符串文字,但不能复制 t-o 字符串文字。您可以在 strncpy 函数中切换 stringA 和 stringB,它将运行
  • 啊,是的,我忘了 strlen(stringA) 是 stringA 的长度,没有 \0 字符
【解决方案3】:

我会尝试在您自己的代码中评论并纠正我看到的错误:

(我不会更正可以消除或以其他方式更好地完成但正确或无害的事情,因此您只会看到由于编程错误而必须更正的内容,以及不是关于风格或编程用途的问题)

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

int main() {
    char* stringA = "someVeryinTeresTingString";

    /* you need to consider the space for the final null character in the malloc() call */
    char* stringB = malloc(sizeof(char) * (strlen(stringA) + 1));
    /* you don't need to use sizeof(char) as it is always equal to one.
     * Multiplying by one is not necessary, but you'll probably know.
     * char is warranteed by C standard that its sizeof is one. */

    /* you need to copy the string *before* printing, or you will print an 
     * uninitialized string.  Or at least initialize stringB to zeros, so you can
     * use it with printf like functions  (I do initialize the first char position to
     * zero to make it appear as a length zero "" string)
     * You will incurr in undefined behaviour if you don't do this. */
    stringB[0] = '\0';
    
    printf("A: %s, B: %s\n", stringA, stringB);
    
    /* you need to copy the strings, so you can do it better if you test when
     * stringA[i] == '\0', so you don't calculate the length of a string that is
     * not going to change at every loop iteration.  I will not change your
     * code, because this is not an error.  But strlen() searches from the
     * beginning of the string for the '\0' char, character by character,
     * and this test is done at every loop iteration.  With the expression
     * stringA[i] == 0 you do only a test per loop iteration to see if
     * the char at position i in stringA is the null character. */
    int i;
    for (i = 0; i < strlen(stringA); i++) {
        stringB[i] = tolower(stringA[i]);
    }
    /* you have not copied the final '\0', so I do it now.  I need to move the
     * declaration of i outside of the loop to be able to use it's value. */
    stringB[i] = 0;  /* you can use 0 or '\0' interchangeably */
    
    printf("A: %s, B: %s\n", stringA, stringB);
    
    /* nope.  you need to copy the strings with a normal strcpy() as you know that
     * both are the same length  (better, you know that the space in stringB
     * is the same as the length of stringA plus one).  If you do this, you will not copy the last '\0' char, so wee need to append it.
     * well, I don't know if that is what you want, so I don't actually touch anything here. */
    strncpy(stringA, stringB, strlen(stringA) - 1);
    
    /* stringB should be one char shorter than stringA */
    printf("A: %s, B: %s\n", stringA, stringB);
}

顺便说一句,建议您使用strdup(3)。这是个好主意,在这种情况下您不需要考虑最终的空值,因为strdup() 会处理它。请记住 strdup(3) 不包含在许多 C 标准修订版中,因此如果您 将您的程序移到缺少它的地方(无论如何,这应该很奇怪)

【讨论】:

    猜你喜欢
    • 2020-02-05
    • 1970-01-01
    • 1970-01-01
    • 2020-09-13
    • 2015-01-25
    • 1970-01-01
    • 2019-09-25
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多