字符串操作导致段错误 C

String operations cause segfault C

我正在尝试 'deep copy' 一个字符串,以便我可以对一个副本执行操作,同时保留原始副本。这是我得到的基本示例,由于某种原因,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);
}

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

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

这一行:

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)

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

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

#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] = '[=10=]';
    
    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] == '[=10=]', 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 '[=10=]' 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 '[=10=]', 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 '[=10=]' 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 '[=10=]' 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 标准修订版中,因此如果您这样做,您可能会遇到麻烦 把你的程序移到一个没有它的地方(无论如何,这应该很奇怪)