本地指针、静态指针和动态内存分配指针。

6
void foo()
{
    char *c1 = "abc";
    static char *c2 = "abc";
    char *c3 = malloc(10);
    strcpy(c3, "abc");
}

在函数 foo 中,我假设: c1 是一个本地指针,应该在堆栈上; c2 是一个静态指针,应该在堆上; c3 在堆上。
根据我的假设,我绘制了一个关于指针和它们所指向的字符串字面量的图表。
 stack         rodata           heap
|    |       |       |         |    |
| c1 |------>| "abc" |<--------| c2 |
| .. |       |       | \       | .. |
|    |       |       |  `------| c3 |
|    |       |       |         |    |

我的假设和图表没错吧?

然而,我不太明白为什么c3应该位于堆上?c3只是一个指向地址(位于堆上)的char *,指向一块内存并不意味着c3本身就在堆上,对吗?


1
不,c3指向堆中的一个数组,该数组现在包含“abc”;c2应该驻留在BSS中。然而,c1位于堆栈上。 - tbert
1
c3在堆栈上,但指向堆。 - Matthias
6个回答

9
您的假设是不正确的。c3并没有指向字面量"abc",而是指向malloc返回的内存地址,您在其中进行了复制。
另外,c1c3都在自动存储区域(堆栈)中。它们是函数作用域内的指针。然而,c3所指向的对象位于动态存储区域(堆),但c3本身并不在其中。
更正确的图形如下:
 stack         rodata           heap        global
|    |       |       |         |       |   |      |
| c1 |------>| "abc" |<--------------------|  c2  |
| c3 |------------------------>| "abc" |
|    |       |       |         |       |
|    |       |       |         |       |

c2 在 .bss 段中吗?这是全局变量和静态变量所在的段吗? - Alcott
@Alcott c2 存储在静态存储区域(也就是静态变量和全局变量所在的地方)。 - Luchian Grigore
静态存储期对象所放置的位置,通常被编译器人员称为.bss。请查看我回答中的维基链接。 - Lundin
@Alcott:不是c2 不在 .bss 段中。.bss 段包含最初仅由零值位表示的静态分配变量。 - LihO
@LihO 但是c2是一个静态分配的变量。在这种情况下,它没有被初始化为零,而是被初始化为给定的地址。从我所看到的编译器实现来看,它们不区分设置为零和设置为给定值的静态对象,它们通常存储在相同的.bss段中。 - Lundin
如果我没记错的话,C++规范说全局变量在初始化时会被赋值为零,然后在该TU中的任何函数执行之前进行分配。 - Mooing Duck

4
实际变量位于堆栈上,因为它是局部变量。然而,指向的数据将位于堆上,因为指针是使用创建的。

请注意,在函数返回之前必须释放指向的内容,否则您的应用程序将出现内存泄漏,这绝不是一个好主意。


1
那么,当 foo() 结束时,c3 的生命周期将会终止,对吗?但是如果我不释放 c3,那么分配给 c3 的空间在 foo() 结束时将不会被回收,对吗? - Alcott
@Alcott 不会被释放,你会有内存泄漏。 - nos
@nos,是的,内存泄漏,这就是我所说的,:-)。 - Alcott

1
c1是一个本地指针,因此应该在堆栈上;
正确。虽然它还可以分配在CPU寄存器中。
c2是一个静态指针,它应该在堆上;
不是的。一个被声明为静态的变量或者一个被声明为全局的变量("文件范围")都有一些叫做“静态存储期”的东西。这意味着在程序被调用之前必须将其初始化为已知值。除非程序员显式地初始化它们,否则编译器将确保它们被设置为零。
为了使静态初始化更容易,所有这种具有静态存储期的对象通常都放置在RAM的一个单独的、专用部分中,通常称为.bss。.bss不太可能放在栈上。
c3在堆上。
实际指针C3在堆栈上,但它所指向的内容在堆上。唯一在堆上的变量是你用malloc()动态分配的那些变量。其他任何东西都不在那里。
我的假设和图形都对吗?

C1是正确的,指针指向存储在内存只读区域中的rodata字符串文字。

C2是不正确的,因为它驻留在.bss中。它指向rodata字符串文字,就像您的图表中一样。

C3是不正确的。当您strcpy()某些内容时,您会复制rodata中的字符串文字,并将该副本存储在堆中,即C3所指向的区域。 C3本身位于堆栈上。

编辑:不确定这个图表值多少钱,但是给你。

 stack      .bss       rodata        heap
|    |     |    |    |       |      |    |
| c1 |-------------->| "abc" |      |    |
|    |     | c2 |--->| "abc" |      |    |
| c3 |----------------------------->|abc |
|    |     |    |    |       |      |    |

唯一在堆上的变量是那些你用malloc()动态分配的变量。我不太理解这个说法。我认为,所有我用malloc分配的都只是内存,但变量只是标识符,它们可能指向malloc分配的内存,它们能成为堆上的变量吗? - Alcott
@Alcott 实际上,指针是在堆栈中分配的。它指向堆。拿这个结构体 typedef struct{ int x; } X_t; 举例,声明一个指向它的指针:X_t* ptr; 然后分配内存 ptr = malloc(sizeof(X_t)); 现在 ptr 在堆栈中分配,但结构体及其成员 x 在堆中分配。&ptr->x 将给出堆中的地址。 - Lundin
哈哈,太棒了,就是这样。 :-) - Alcott

1

c1c3是具有自动存储期的指针。这两个指针存在于您的foo()函数的堆栈上。它们所指向的内容是另一回事。

c2是静态的,并且存在于其他地方(通常不是堆,而是整个程序有效的某个数据段)。

c1c2都指向字符串字面量"abc"。通常,字符串字面量放置在只读段中,并且类似的字符串会合并,因此c1c2指向相同的内存。

c3指向由malloc()分配的内存,这种内存通常称为"堆"。然后,您将字符串"abc"复制到堆上的该内存中。


你最后一段可能是指c3,所以我稍微修改了一下。 - Lundin
呃,第三段还是不正确。你最好自己编辑一下,然后我会点赞 :) - Lundin

0

数据段是程序虚拟地址空间的一部分,其中包含由程序员初始化的全局变量和静态变量。

在C语言中,没有显式初始化器的静态分配对象会被初始化为零(对于算术类型)或空指针(对于指针类型)。 C语言的实现通常使用仅由零值位组成的位模式来表示零值和空指针值(尽管这不是C标准所要求的)。因此,bss节通常包括在文件范围内声明的所有未初始化变量(即在任何函数之外声明的变量)以及使用static关键字声明的未初始化局部变量。


-1

这是您的图表应该看起来的样子:

 heap       stack     rodata       bss
|     |    |     |    |     |    |     |
|"abc"|<-+ | c1  |--->|"abc"|<---| c2  |
|     |  | |     |    |     |    |     |
|     |  +-| c3  |    |     |    |     |
|     |    |     |    |     |    |     |

c3的声明与c1相同。它们都在堆栈上。


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