【问题标题】:Why does this code to modify a string not work?为什么这段代码修改字符串不起作用?
【发布时间】:2010-10-30 21:38:02
【问题描述】:

对于 c 风格的字符串,如何将 char 分配给字符指针指向的内存地址?例如,在下面的示例中,我想将 num 更改为“123456”,因此我尝试将 p 设置为 '0' 所在的数字,并尝试用 '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;
}

【问题讨论】:

标签: c pointers char segmentation-fault access-violation


【解决方案1】:

首先,当你这样做时:

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';

其他几点说明:

  • 尽管常见的风格实践,将malloc() 的返回值强制转换为(char *) 是不必要的。 void * 类型的转换和对齐由 C 语言保证。

  • 始终检查malloc() 的返回值。如果堆分配失败(即您的内存不足),它将为 NULL,此时您的程序应该退出。

  • 根据实现,malloc() 分配的内存区域在某些情况下可能包含陈旧垃圾。分配后将其清零总是一个好主意:

    memset(num, 0, 100);
    
  • 永远不要忘记free() 你的堆!在这种情况下,程序会退出,操作系统会清理你的垃圾,但如果你不养成这种习惯,你很快就会出现内存泄漏。

所以,这里是“最佳实践”版本:

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

【讨论】:

  • 感谢您提供出色的内存管理技巧。这些提示对来自自动垃圾收集语言的人很有帮助!
  • sizeof(char) 定义为 1。see 6.5.3.4 The sizeof and _Alignof operators: 4 当 sizeof 应用于具有 char、unsigned char 或 signed char 类型(或其限定版本)的操作数时,结果是 1。
【解决方案2】:

该代码不起作用,仅仅是因为以下行:

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,但考虑到程序会立即退出,所以没有必要。 sn-p 的想法是传达相关信息,而不是所有信息,否则,我们需要检查 malloc() 是否正常工作。
【解决方案3】:

除了其他人指出的 *p 问题外,您还有内存使用问题。您有一个 100 字节的缓冲区,内容未知。您还有另一个 7 字节的缓冲区,其中包含字符串“123056”和一个空终止符。你正在这样做:

  1. num 设置为指向 100 字节的缓冲区
  2. p 设置为指向 num;即,它指向 100 字节的缓冲区
  3. 您将 num 重置为指向 7 字节缓冲区; p 仍然指向 100 字节的缓冲区
  4. 你使用 p 来修改 100 字节的缓冲区
  5. 然后使用 num 打印出 7 字节缓冲区

因此,您打印的缓冲区与正在修改的缓冲区不同。

【讨论】:

  • 谢谢布鲁斯,你的解释很有帮助,但 Pax 的 strcpy 真的很清楚。
【解决方案4】:

嗯,你知道 p 是指针类型。它存储 char '0' 的地址。如果您为 p 分配“4”的值。它将以“4”作为地址。但是,地址“4”是非法的。您可以使用 '*' 运算符来获取存储在 p 中的地址值。

【讨论】:

  • 吹毛求疵,地址“4”并不违法。实现可以很容易地使用 0x00000034 处的内存(它甚至可以正确对齐 32 位字)。
  • 好吧,我说这个应用程序是非法的。这是一个未知的内存地址。
【解决方案5】:

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

【讨论】:

  • 取消引用它不起作用。字符串“num”是不可变的还是什么?我在 linux 上用 cc 编译。
  • 是的,字符串文字很可能分配在标记为只读的内存段中,任何尝试写入它都会导致运行时错误。
  • @Igor,很好的发现,编辑修复,tx -- @sharptooth,这非常依赖于你的编译器和 c,strdup 可能会有所帮助。
猜你喜欢
  • 2010-09-18
相关资源
最近更新 更多