为什么这段修改字符串的代码没有起作用?

5
使用C风格字符串,如何将字符分配给字符指针所指的内存地址?例如,在下面的示例中,我想将num更改为"123456",因此我尝试将p设置为'0'所在的数字并尝试用'4'覆盖它。谢谢。
使用指针和数组的方式可以完成这个操作。您可以将指针p指向要更改的字符的位置,并使用赋值运算符=将新值赋给该位置。例如,在本例中,您可以使用以下代码将'4'赋给'0'的位置:
*p = '4';
#include <stdio.h>
#include <stdlib.h>

int main()
{
    char* num = (char*)malloc(100);
    char* p = num;

    num = "123056";

    p = p+3;    //set pointer to where '4' should be
    p = '4';

    printf("%s\n", num );

    return 0;
}

5个回答

13
首先,当您执行以下操作时:
num = "123056";

您没有将字符串“123056”复制到由malloc()分配的堆区域。在C语言中,将一个char *指针赋值为字符串字面值等同于将其设置为常量 - 即与以下代码相同:

char str[] = "123056";

因此,您刚刚放弃了对malloc()分配的100字节堆区域的唯一引用,这就是为什么您的后续代码没有打印正确值的原因;'p'仍然指向由malloc()分配的堆区域(因为在赋值时num指向它),但num不再指向它。
我假设您实际上想要将字符串“123056”复制到该堆区域中。以下是如何执行此操作:
strcpy(num, "123056");

虽然如此,出于多种原因,这是更好的做法:

strncpy(num, "123056", 100 - 1);  /* leave room for \0 (null) terminator */

如果你刚刚做了以下操作:

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

int     main(void) {
        char    *num = malloc(100);
        char    *p = num;

        strncpy(num, "123056", 100 - 1);

        p = p + 3;
        *p = '4';

        printf("%s\n", num);

       return 0;
} 

您应该会得到正确的结果:
123456

您可以收缩此操作:
p = p + 3;
*p = '4';

...并避免迭代指针,通过以下方式解除引用:

*(p + 3) = '4';

一些其他的注意事项:
  • Although common stylistic practice, casting the return value of malloc() to (char *) is unnecessary. Conversion and alignment of the void * type is guaranteed by the C language.

  • ALWAYS check the return value of malloc(). It will be NULL if the heap allocation failed (i.e. you're out of memory), and at that point your program should exit.

  • Depending on the implementation, the area of memory allocated by malloc() may contain stale garbage in certain situations. It is always a good idea to zero it out after allocation:

    memset(num, 0, 100);
    
  • Never forget to free() your heap! In this case, the program will exit and the OS will clean up your garbage, but if you don't get into the habit, you will have memory leaks in no time.

所以,以下是“最佳实践”版本:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int     main(void) {
        char    *num, *p;

        /*
         * Don't take 1-byte chars for granted - good habit to get into.
         */

        num = malloc(sizeof(char) * 100);

        if(num == NULL)
                exit(1);

        memset(num, 0, sizeof(char) * 100);

        p = num;

        strncpy(num, "123056", 100 - 1);

        *(p + 3) = '4';

        printf("%s\n", num);

        free(num);

        return 0;
}

1
谢谢你提供了很好的内存管理技巧。这些技巧对于那些来自自动垃圾收集语言的人非常有用! - Mike Anderson
sizeof(char)被定义为1。参见6.5.3.4 sizeof和_Alignof运算符: 4 当sizeof应用于具有char、unsigned char或signed char类型(或其限定版本)的操作数时,结果为1。 - Olaf Seibert

12

那段代码行不通,因为这一行:

num = "123056";

num 指针指向已分配内存的外部(p 仍指向该内存,因此它们不再指向同一位置),指向的是最可能的只读内存。您不允许更改字符串文本的内存,这是未定义的行为。

您需要以下内容:

#include <stdio.h>
#include <stdlib.h>
int main (void) {
    char *num = malloc (100); // do not cast malloc return value.
    char *p = num;

    strcpy (num, "123056");   // populate existing block with string.

    p = p + 3;                // set pointer to where '0' is.
    *p = '4';                 // and change it to '4'.

    printf ("%s\n", num );    // output it.

    return 0;
}

还需要释放内存。 - Igor Krivokon
2
这是一个很好的形式,@Igor,但是考虑到程序立即退出,这并不是必要的。而代码片段的目的是传达相关信息,而不是所有信息,否则我们还需要检查malloc()是否成功。 - paxdiablo

1
除了其他人指出的*p问题之外,您还有内存使用问题。您有一个100字节的缓冲区,其内容未知。您还有另一个包含字符串“123056”和空终止符的7字节缓冲区。您正在执行以下操作:
  1. 将num设置为指向100字节缓冲区
  2. 将p设置为指向num;即,它指向100字节缓冲区
  3. 您将num重置为指向7字节缓冲区;p仍然指向100字节缓冲区
  4. 您使用p修改100字节缓冲区
  5. 然后您使用num打印出7字节缓冲区
所以您没有打印出您正在修改的相同缓冲区。

谢谢Bruce,你的解释很有帮助,但是Pax的strcpy真的让我明白了。 - Mike Anderson

0

只需使用*p = '4';...!


解除引用它没有起作用。字符串“num”是不可变的吗?我在Linux上使用cc编译。 - Mike Anderson
是的,字符串字面量很可能分配在一个标记为只读的内存段中,任何尝试写入其中的操作都会导致运行时错误。 - sharptooth
@Igor,发现得好,正在编辑修复,谢谢 -- @sharptooth,这取决于你的编译器等因素,使用strdup可能会有帮助。 - Alex Martelli

0

嗯,你知道p是指针类型。它存储了字符'0'的地址。如果你把p赋值为'4',它将把'4'作为地址。然而,地址'4'是非法的。你可以使用'*'运算符来获取存储在p中的地址的值。


一个小问题,地址“4”并不是非法的。实现可以很容易地使用0x00000034处的内存(它甚至对于32位字对齐正确)。 - paxdiablo
好的,我说这个应用程序是非法的。它是一个未知的内存地址。 - Sefler

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