【问题标题】:String and pointers in CC中的字符串和指针
【发布时间】:2014-09-17 10:30:24
【问题描述】:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main(void)
{
        char* a = malloc(5 * sizeof(char));
        a = "1";
        free(a);
}

我对上述代码有两个问题:

  1. 当我像这样初始化 a 时,为什么代码会在 free(a) 中出现错误:

a = "1"

当我像这样初始化 a 时,free(a) 没有任何错误:

a[0] = '1'。

  1. 当我用 a[0] = '1' 并使用打印字符串 a printf("%s", a) 为什么我得到结果

    '1'

没有用 '\0' 声明第二个索引?

【问题讨论】:

  • a = "1";不会用 a 填充 a,它会将指针重新分配给内存中的某个字符串“a”。该字符串已编译和修复,不能/不应该被释放。
  • 从字面上看,只要 C 程序执行此操作:Type* p = malloc(...); p = ... 就错了。您的程序在前两行中泄漏了内存,然后释放了只读字符串文字的地址..
  • 关于 '\0' 的第二个索引。这纯粹是运气,因为内存中包含“\0”。

标签: c string pointers memory malloc


【解决方案1】:

mallocfree 的规则是你必须只向 free 传递一个通过调用 分配的指针malloc(或其等效项之一,如 realloccalloc)。你不这样做:

char* a = malloc(5 * sizeof(char));
a = "1";
free(a);

当您调用 free 时,您传递的 a 不是由调用 malloc 分配的。 这会导致您的程序具有未定义的行为.


你理解的问题是我怀疑你不明白 a = "1" 做了什么。

我怀疑您认为这是为了将字符串复制到您分配的内存中。它将指针 a 更改为指向不同的地址。

确实,通过分配给 a,而不是它指向的内存,您正在泄漏分配的内存。您的代码与此完全相同:

int i = 1;
i = 2;

我相信您可以看到 i 的初始化是没有意义的,因为您立即用新值覆盖了 i 的值。这正是您使用指针所做的。


您可以使用strcpy 复制字符串。像这样:

char* a = malloc(5 * sizeof(char));
strcpy(a, "1");
free(a);

同样,你写的时候

char* a = malloc(5 * sizeof(char));
a[0] = '1';
printf("%s", a);
free(a);

您将调用分配的缓冲区的内容分配给 malloc。因此对free 的调用是正确的。

但是,当您这样做时,字符串不一定以 null 结尾。您观察到您的 printf 似乎可以工作,但这只是偶然。

a[1] 的内容定义不明确,似乎在您运行程序时,a[1] 确实包含 null。

这就是为什么您通常使用像 strcpy 这样的字符串函数来执行复制并确保空终止。您当然需要注意不要超出缓冲区。


最后,根据定义,sizeof(char) == 1 所以你可以这样写 malloc

char* a = malloc(5);

【讨论】:

  • 在处理字符串时,最好始终使用string.h 中的函数,例如此答案中的strcpy。除非您真的确实知道自己在做什么,否则它不太可能破坏或给您带来不想要的结果,而且它也可能比自创功能更有效。
【解决方案2】:

a = "1"; 正在将只读内存分配给aa 现在指向一个完全不同的内存位置。 malloc 之前的返回值现在丢失了,因此您将无法释放内存。

这是一场灾难,因为不包括内存泄漏,您在尝试 free 的新值 a 时会有未定义的行为轰隆隆。此外,如果您尝试修改a 的内容,那么这也是未定义的行为轰隆隆

没有错误的重新分配,a[0] = '1' 非常好:您推迟分配的字符缓冲区的第零个元素并将其值设置为文字 1

但是,如果您尝试printf("%s", a),那么,繁荣!更多未定义的行为,因为您没有对字符缓冲区进行空终止,printf 要求您这样做以防止它访问您不拥有的内存。为此,请使用a[1] = 0

【讨论】:

    【解决方案3】:

    当你这样做时

    a = "1";
    

    a 不再指向您使用malloc 分配的内存。相反,它指向一个字符串常量,存储在程序的文本段中。自然,这样的指针不能被释放。原来的a 指针丢失了。

    至于您的第二个问题,这是未定义的行为。 a[1] 碰巧是一个空字符,但你当然不应该依赖这种情况。

    【讨论】:

      【解决方案4】:
      a = "1";
      

      使a 指向一个字符串文字,它存储在只读内存中。你不能free()那部分内存。

      a[0] = '1'; 使它在a 指向的第一个字节中存储31 的值(因为这是十六进制ascii 值1)。 a 仍然指向 malloc 已返回的地址,因此释放它是合法操作。 a[0]*(a + 0) 相同,所以你看到a 的地址没有改变,a 指向的内容发生了变化。

      其他答案已经有关于 null 终止您的 char 数组的信息。您应该知道printf() 在遇到空终止符时会停止将值写入标准输出。如果数组的第 0 个位置有 '1' 而第 1 个位置没有 '\0',您可能会看到一些垃圾值。


      你应该知道"1"'1' 是两个不同的东西。

      【讨论】:

        【解决方案5】:

        使用内联 cmets 更正程序以供您理解:

        int main(void)
        {
          char* a = (char *) malloc(5 * sizeof(char)); // prefer casting from void * to char *
          // a = "1"; -- wrong conversion from string constant to char *
          strcpy(a, "1"); // this would work
          free(a);
        }
        

        【讨论】:

        • 是否允许删除 malloc 演员表?
        • 不行,那你应该使用strdup,而且在strdup之前free。
        • 这是一道 C 题。您不应该在 C 中转换 malloc() 的结果。转换不是必需的,并且可能会隐藏有用的编译器警告。
        • LHS 是 char *,rhs 是 void *,铸造有什么害处?这是一种很好的编程习惯
        • 简单,lhs就是char *,这就是为什么?你能投到别的东西吗?加油!
        猜你喜欢
        • 2012-03-22
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-06-28
        • 2021-08-12
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多