【问题标题】:In C, what happens to memory values after a pointer's value is changed?在 C 中,指针的值更改后内存值会发生什么变化?
【发布时间】:2021-06-29 13:35:07
【问题描述】:

在下面的代码中,我通过值将指针传递给我希望将 N 个字符串连接到原始字符串的函数。

按照我的编写方式,我通过计算要在原始字符串长度上添加多少字节来分配新内存。

然后填充这个新字符串并返回指向该字符串的指针。

所以,假设原始指针msg0x000001,它指向字符串"Hello\0" 的起始字符。

然后在函数中,一个新的指针strNew最终指向"Hello world, poop\0",其值为0x0000f5,新字符串的内存所在的位置。

然后,作为函数的返回,msg 指针的值现在是 0x0000f5

我的问题是,位于0x000001 的内存会发生什么变化?它包含"Hello\0" 的字节,但不再有指向它的指针。它会收集垃圾吗?这是个问题吗?我应该用“”字符以某种方式覆盖内容吗?

如果没有,如何从 strcatcat() 函数中释放它?

这个想法是不必担心字符数组的大小足以让字符串开始。这不合理吗?

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

char * strcatcat(char * orig, const char* strArgs, ...){
    
    va_list valist;
    
    unsigned long initStrLength = strlen(orig);
    
    // work out how much me to realloc
    unsigned long moreBytes = 0;
        
    va_start(valist, strArgs);
    const char* str = strArgs;
    while (str != NULL ) {
        moreBytes += strlen(str);
        str = va_arg(valist, const char *);
    }
    
    // define a new char pointer to populate, of defined size
    char * strNew = NULL;
    strNew = (char *) malloc((moreBytes + initStrLength+1));
    
    // copy the original string into the start
    strcpy(strNew, orig);
    
    //reset, then go through and concat into new string
    va_start(valist, strArgs);
    str = strArgs;
    while (str != NULL ) {
    
        strcat(strNew, str);
        
        str = va_arg(valist, const char *);
            
    }
    
    // close list
    va_end(valist);
        
    // return this pointer
    return strNew;
    
}

int main()
{
 
    char * msg = "Hello";
    msg = strcatcat(msg, " World, ", "poop", NULL);
    printf("%s\n", msg);
    return 0;
}

编辑:谢谢大家,这已经清除了。我习惯了更高级别的语言,如 PHP、C# 等,并阅读了有关指针算法的信息,弹出了这个问题,我找不到不关注指针而不是指针值的答案。

未来人们的 TLDR - 如果没有调用者管理它,我给出的示例会导致内存泄漏。 main 中的指针需要复制才能解除分配。

【问题讨论】:

  • 什么也没发生。据说那个记忆已经“泄露”了。您不再有任何对它的引用,因此您无法使用它,但它会继续消耗资源,直到进程退出。
  • 但是在这种情况下,您的原始指针没有分配在堆上,所以....这根本不是问题。

标签: c pointers memory-management string-literals


【解决方案1】:

我的问题是,位于 0x000001 的内存会发生什么?它包含“Hello\0”的字节,但不再有指向它的指针。

什么都没有发生...指针只不过是一些内存的地址,因此更改指针的值只会更改它指向的字节,它不会影响存储在指向的字节中的值。

它会被垃圾回收吗?

C 没有内置的垃圾收集器;你负责管理内存。

有问题吗?

也许……这取决于情况。在您的 strcatcat() 函数的情况下,指针 orig 如您所见,按值传递。这意味着函数获得了它自己的指针副本,并且如果函数改变了orig 的值,调用者的指针副本根本不会改变。由于strcatcat() 没有分配orig 指向的内存,它不负责释放它...调用者(或分配或负责该块的任何人)应该这样做。

如果您的程序分配内存块并且从不释放它们,这肯定是一个问题,因此总体上您应该有一个明确的内存管理策略。但除非你很清楚调用strcatcat() 将释放传入的块,否则该函数不应触及原始块。

我应该用“”字符以某种方式覆盖内容吗?

你为什么要这样做?即使在调用您的函数之后,调用者是否也可能希望将原始块用于其他事情?

当您释放它们时,它们不会停止存在;他们总是在那里。分配只是为特定目的保留给定字节范围的过程,以便其他一些代码不会尝试同时使用相同的字节。当您释放内存时,字节仍然存在,但您释放的块可用于其他用途。如果数据在某种程度上是敏感的,那么在释放块之前重写数据是有意义的,例如密码或某种个人信息。但是只要你不需要保护一个块中的数据,那么在释放它之前不需要清除内存。

【讨论】:

  • 非常感谢。那么我将如何释放一个给定的 char 数组,它最初不是由 malloc() 创建的,所以不能被 free() 分配?
【解决方案2】:

首先,如果您为“Hello\0”动态分配内存并且您没有释放它,您将造成内存泄漏。这是一个如何制作的学校示例。

这导致了下一个认识:C 中没有垃圾收集器,除非您创建一个。你对一切负责。这就是 C 语言的美妙之处,因为你也可以控制一切。您有权决定针对您的特定问题的最佳方法。

下一个问题是,从函数内部你永远不知道内存是如何分配的。我可以在调用者级别创建char example[100] = "My value";,并将其分配在堆栈上。如果你试图从你的函数内部释放它,程序将会失败。

解决这个问题的基本方法很少:

  • 调用方提供缓冲区,如果缓冲区不够大,函数会失败
  • 该函数分配内存并且不接触输入,调用者对其接收到的一切负责。
  • 更高级别的字符串抽象(我会说是 C++ 中的一个类,但让我们说一些具有一些相关操作的类型)一个例子可能是 Glib 字符串https://developer.gnome.org/glib/stable/glib-Strings.html ,但还有许多其他实现。

它们各有优缺点(防止泄漏、内存碎片等)。这由 C 程序员决定哪种方法最适合您的情况。

【讨论】:

  • 谢谢,glib 字符串的链接正是我需要了解它是如何完成的。
  • 开源真是太好了,您可以实际研究源代码。享受吧。
【解决方案3】:

我的问题是,位于 0x000001 的内存会发生什么?它 包含“Hello\0”的字节,但不再有指向它的指针。 它会收集垃圾吗?这是个问题吗?我应该覆盖 内容以某种方式带有“”字符?

对于 C 语言的初学者来说,没有垃圾收集器。其次,您可能不会更改字符串文字。任何更改字符串文字的尝试都会导致未定义的行为。

在此声明中

char * msg = "Hello";

声明了一个指向字符串文字"Hello" 的第一个字符的指针。字符串字面量本身具有静态存储持续时间,并且在程序完成执行之前一直处于活动状态,这与指针 msg 的值是否更改无关。

你可以这样想

char unnamed_string_literal[] = { 'H', 'e', 'l', 'l', 'o', '\0' };

int main( void )
{
    char *msg = unnamed_string_literal;
    //...
}

你不应该为字符串文字而烦恼。

例如考虑以下有效程序。

#include <stdio.h>

int main(void) 
{
    char *msg = "Hello";
    
    printf( "%s ", msg );
    
    msg = "World!";
    
    puts( msg );
    
    return 0;
}

程序输出是

Hello World!

本质上,这个程序与下面的程序相似(除了问题上下文中的一些不重要的细节)

#include <stdio.h>

char word1[] = { 'H', 'e', 'l', 'l', 'o', '\0' };
char word2[] = { 'W', 'o', 'r', 'l', 'd', '!', '\0' };

int main(void) 
{
    char *msg = word1;
    
    printf( "%s ", msg );
    
    msg = word2;
    
    puts( msg );
    
    return 0;
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-01-06
    • 2013-03-14
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-07-14
    • 2020-04-05
    相关资源
    最近更新 更多