【问题标题】:How does size information get passed with a pointer?大小信息如何通过指针传递?
【发布时间】:2013-12-11 01:23:24
【问题描述】:

我正在学习 C 并使用 free() 函数将我引向这个问题。假设我有一个指针:

char *p = "String";

现在已经分配了 7 个字节的内存供 p 指向。然而, sizeof(*p) 返回一个 1 ,这使得指针看起来只“知道”它所保存的地址的 1 个字节。

如果是这种情况,那么 free(p) 是如何释放所有 7 个字节的。所以我唯一能想到的是:

  1. 幕后发生了一些事情

  2. 只有被指向的第一个字节需要被释放

是其中之一吗?如果是 1. 什么是隐藏过程/它是如何工作的?如果是 2. 那么机器如何跟踪不应该分配给其他指针和变量的其他 6 个字节?

感谢您的帮助!

【问题讨论】:

  • 在您的情况下,没有为 p 分配任何内存,它只是字符串文字。
  • p 没有分配给malloc() 或者他的朋友,所以你不能在上面调用free()
  • 话虽如此,如果它被分配了 malloc,free “知道”要释放多少内存,因为在幕后有一些你看不到的簿记。
  • 而sizeof(*p)和sizeof(char)一样,当然是1。
  • 答案似乎有点冲突。

标签: c pointers memory-management


【解决方案1】:

sizeof(*p) 为 1,因为 p 指向一个字符(即一个字节)。

“它怎么知道”分配的内存有多大——它是“平台相关的”。很多时候你会发现关于 malloc 的两件事:

  • 它分配了比您要求的更大的块 - 因为它更多 一次处理 64 个字节的效率(对齐问题等)
  • 通常,在指针“正下方”有一小段代码,其中包含有关块大小的一些信息,以及指向“下一个和前一个块”的指针 - 就像一个链表。这样,如果您清除一个块,内存管理可以跟踪哪些是空闲的,哪些不是。
  • 也有可能单独维护一个分配表,其中描述了哪些块是空闲/使用的,它们有多大等等。

注意-我上面描述的内容与malloc 的内存分配有关-这是C 可以free() 的唯一类型。字符串常量以不同的方式分配,它的大小和位置是固定的——这就是你通常写的原因

const char *p = "hello";

对于这样的字符串,您可以通过查找nul 终止符来了解其大小。但是如果你真的决定用'\0'覆盖字符串中前面的一个字符,它不会改变分配的内存量:

char p[] = "hi there world";
p[2] = '\0';
printf("%s***\n", p);

输出:

hi***

但是内存没有被释放。

【讨论】:

  • 我认为这是解决 OP 帖子(内存分配簿记)意图的唯一答案,即使他在示例中不小心给出了字符串文字
  • 有一个信息丰富的线程here
  • 注意:p[2] = '\0' 不一定会改变任何东西。 printf("%s***\n", p) 仍然可以打印 "hi there world"
  • @rici UB 是 UB。这里一个典型的潜在案例是"hi there world" 被放置在只读内存中并且忽略了更改。
  • @chux 你当然是对的。我将其更改为char p[],所以我的示例确实有效。如果其他人想知道发生了什么,请阅读stackoverflow.com/questions/1704407/…
【解决方案2】:
char *p = "String";

这是合法的,但确实应该是:

const char *p = "String";

这样您就不会不小心尝试修改数组。

你问什么数组?任何字符串文字都对应于大小为LEN+1 的静态分配数组,其中LEN 是文字的长度。所以在这种情况下,有一个char[7] 类型的 anonymous 数组存在于程序的整个执行过程中,其中包含值

`{ 'S', 't', 'r', 'i', 'n', 'g', '\0' }`.

p 的初始化程序使其指向该数组的第一个元素,其中包含 'S'。由于数组不是通过调用malloc()(或calloc(),或realloc())分配的,您不得尝试free()它。

给定p,您不能直接确定数组的大小(因为p 指向它的第一个元素,而不是整个数组)。您可以计算strlen(p) + 1,它可能与分配的大小相同——但如果字符串文字是"foo\0bar",那么它将是8个字节长,但strlen会停在第一个空字符并返回3。因此,如果您需要能够确定数组的大小,请不要通过将其地址分配给指针来丢弃该信息。

现在,如果你改为写:

char arr[] = "String";

初始化程序会将字符串文字的内容复制到数组arr,而arr 的大小将由编译器根据初始化程序的大小确定。同样,你不能尝试在这个数组上调用free()(或者更确切地说,在指向它的第一个元素的指针上),因为它不是用malloc()分配的。但是您可以通过sizeof arr 轻松确定其大小,这将产生7。 (sizeof p 会给你一个指针的大小,而不是它指向的数组的大小。)

最后,如果你使用malloc() 来分配一个数组:

#define message "String"
char *p = malloc(sizeof message + 1);
if (p == NULL) {
    fprintf(stderr, "malloc failed\n");
    exit(EXIT_FAILURE);
}
strcpy(p, message);

你仍然不能使用p(它只是一个指向单个字符的指针)来确定分配数组的大小——但是free() 能够弄清楚。如何?语言没有说,但任何实现都会在幕后保留额外的簿记信息,让free() 做正确的事情。

请注意,实现不一定记住您请求的大小。对malloc(7) 的调用实际上可能分配了16 个字节,而free 需要的唯一信息是基地址和实际分配的大小。

但是既然你写了对malloc的调用,如果你需要记住分配的大小,把它保存在某个地方:

// ...
const size_t allocated_bytes = sizeof message + 1;
char *p = malloc(allocated_bytes);
// ...

如果你有一个声明的数组对象,你可以使用sizeof 来确定它的大小。数组通常使用指针来操作,指针不保留数组的大小;你必须自己跟踪。 malloc() 和朋友们确实在幕后维护了一些尺寸信息,但只是为了让free() 正常工作;您(可能)自己无法访问该信息。如果您需要跟踪已分配数组的大小,则必须自己完成。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-01-13
    • 1970-01-01
    • 2015-03-28
    • 2021-02-02
    • 1970-01-01
    • 2014-07-27
    相关资源
    最近更新 更多