改变 C 字符串时避免内存泄漏

发布于 2024-07-29 07:35:58 字数 701 浏览 4 评论 0原文

出于教育目的,我在一些测试程序中使用 cstrings。 我想用“...”等占位符来缩短字符串。

也就是说,如果我的最大长度设置为 13,“Quite a long string” 将变为 “Quite a lo...”。此外,我不想销毁原始字符串 - 因此缩短的字符串必须是副本。

下面的(静态)方法是我想出的。 我的问题是:为我的缩短字符串分配内存的类也应该负责释放它吗? 我现在所做的是将返回的字符串存储在单独的“用户类”中,并推迟将内存释放给该用户类。

const char* TextHelper::shortenWithPlaceholder(const char* text, size_t newSize) {
    char* shortened = new char[newSize+1];

    if (newSize <= 3) {
        strncpy_s(shortened, newSize+1, ".", newSize);
    }
    else {
        strncpy_s(shortened, newSize+1, text, newSize-3);
        strncat_s(shortened, newSize+1, "...", 3);  
    }
    return shortened;
}

For educational purposes, I am using cstrings in some test programs. I would like to shorten strings with a placeholder such as "...".

That is, "Quite a long string" will become "Quite a lo..." if my maximum length is set to 13. Further, I do not want to destroy the original string - the shortened string therefore has to be a copy.

The (static) method below is what I come up with. My question is: Should the class allocating memory for my shortened string also be responsible for freeing it?
What I do now is to store the returned string in a separate "user class" and defer freeing the memory to that user class.

const char* TextHelper::shortenWithPlaceholder(const char* text, size_t newSize) {
    char* shortened = new char[newSize+1];

    if (newSize <= 3) {
        strncpy_s(shortened, newSize+1, ".", newSize);
    }
    else {
        strncpy_s(shortened, newSize+1, text, newSize-3);
        strncat_s(shortened, newSize+1, "...", 3);  
    }
    return shortened;
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

倥絔 2024-08-05 07:35:58

此类函数的标准方法是让用户传入 char[] 缓冲区。 例如,您可以在像 sprintf() 这样的函数中看到这一点,这些函数将目标缓冲区作为参数。 这允许调用者负责分配和释放内存,将整个内存管理问题放在一个地方。

The standard approach of functions like this is to have the user pass in a char[] buffer. You see this in functions like sprintf(), for example, which take a destination buffer as a parameter. This allows the caller to be responsible for both allocating and freeing the memory, keeping the whole memory management issue in a single place.

要走就滚别墨迹 2024-08-05 07:35:58

为了避免缓冲区溢出和内存泄漏,在这种情况下,您应该始终使用 C++ 类,例如 std::string

只有最后一个实例才应将类转换为低级别的内容,例如 char*。 这将使您的代码简单且安全。 只需将代码更改为:

std::string TextHelper::shortenWithPlaceholder(const std::string& text,
                                               size_t newSize) {
    return text.substr(0, newSize-3) + "...";
}

在 C 上下文中使用该函数时,只需使用 cstr() 方法:

some_c_function(shortenWithPlaceholder("abcde", 4).c_str());

仅此而已!

一般来说,您不应该像使用 C 编程那样使用 C++ 进行编程。将 C++ 视为一种完全不同的语言更为合适。

In order to avoid buffer overflows and memory leaks, you should always use C++ classes such as std::string in this case.

Only the very last instance should convert the class into something low level such as char*. This will make your code simple and safe. Just change your code to:

std::string TextHelper::shortenWithPlaceholder(const std::string& text,
                                               size_t newSize) {
    return text.substr(0, newSize-3) + "...";
}

When using that function in a C context, you simply use the cstr() method:

some_c_function(shortenWithPlaceholder("abcde", 4).c_str());

That's all!

In general, you should not program in C++ the same way you program in C. It's more appropriate to treat C++ as a really different language.

小梨窩很甜 2024-08-05 07:35:58

我从来不高兴返回指向本地分配的内存的指针。 我喜欢对任何调用我的清理功能的人保持健康的不信任。

相反,您是否考虑过接受一个缓冲区并将缩短的字符串复制到其中?

例如。

const char* TextHelper::shortenWithPlaceholder(const char* text, 
                                               size_t textSize, 
                                               char* short_text, 
                                               size_t shortSize)

其中 short_text = 复制缩短字符串的缓冲区,shortSize = 提供的缓冲区大小。 您还可以继续返回指向 short_textconst char* ,以方便调用者(如果 shortSize 不够大,则返回 NULL到)。

I've never been happy returning pointers to locally allocated memory. I like to keep a healthy mistrust of anyone calling my function in regard to clean up.

Instead, have you considered accepting a buffer into which you'd copy the shortened string?

eg.

const char* TextHelper::shortenWithPlaceholder(const char* text, 
                                               size_t textSize, 
                                               char* short_text, 
                                               size_t shortSize)

where short_text = buffer to copy shortened string, and shortSize = size of the buffer supplied. You could also continue to return a const char* pointing to short_text as a convenience to the caller (return NULL if shortSize isn't large enough to).

夏了南城 2024-08-05 07:35:58

实际上,您应该只使用 std::string,但如果必须,请查看现有库以获取使用指南。

在C标准库中,最接近你正在做的事情的函数是

char * strncpy ( char * destination, const char * source, size_t num );

所以我会这样做:

const char* TextHelper::shortenWithPlaceholder(
    char * destination, 
    const char * source, 
    size_t newSize);

调用者负责内存管理 - 这允许调用者使用堆栈,或堆,或内存映射文件或保存该数据的任何来源。 您不需要记录您使用 new[] 来分配内存,并且调用者不需要知道使用 delete[] 而不是 < code>free 或 delete,甚至是较低级别的操作系统调用。 将内存管理留给调用者只会更灵活,并且更不容易出错。

返回指向目的地的指针只是允许您执行以下操作的一个好处:

char buffer[13];
printf("%s", TextHelper::shortenWithPlaceholder(buffer, source, 12));

Really you should just use std::string, but if you must, look to the existing library for usage guidance.

In the C standard library, the function that is closest to what you are doing is

char * strncpy ( char * destination, const char * source, size_t num );

So I'd go with this:

const char* TextHelper::shortenWithPlaceholder(
    char * destination, 
    const char * source, 
    size_t newSize);

The caller is responsible for memory management - this allows the caller to use the stack, or a heap, or a memory mapped file, or whatever source to hold that data. You don't need to document that you used new[] to allocate the memory, and the caller doesn't need to know to use delete[] as opposed to free or delete, or even a lower-level operating system call. Leaving the memory management to the caller is just more flexible, and less error prone.

Returning a pointer to the destination is just a nicety to allow you to do things like this:

char buffer[13];
printf("%s", TextHelper::shortenWithPlaceholder(buffer, source, 12));
风透绣罗衣 2024-08-05 07:35:58

最灵活的方法是返回一个包装分配内存的辅助对象,这样调用者就不必担心它。 该类存储指向内存的指针,并具有复制构造函数、赋值运算符和析构函数。

class string_wrapper
{
    char *p;

public:
    string_wrapper(char *_p) : p(_p) { }
    ~string_wrapper() { delete[] p; }

    const char *c_str() { return p; }

    // also copy ctor, assignment
};

// function declaration
string_wrapper TextHelper::shortenWithPlaceholder(const char* text, size_t newSize)
{
    // allocate string buffer 'p' somehow...

    return string_wrapper(p);
}

// caller
string_wrapper shortened = TextHelper::shortenWithPlaceholder("Something too long", 5);

std::cout << shortened.c_str();

大多数实际程序都使用 std::string 来实现此目的。

The most flexible approach is to return a helper object that wraps the allocated memory, so that the caller doesn't have to worry about it. The class stores a pointer to the memory, and has a copy constructor, an assignment operator and a destructor.

class string_wrapper
{
    char *p;

public:
    string_wrapper(char *_p) : p(_p) { }
    ~string_wrapper() { delete[] p; }

    const char *c_str() { return p; }

    // also copy ctor, assignment
};

// function declaration
string_wrapper TextHelper::shortenWithPlaceholder(const char* text, size_t newSize)
{
    // allocate string buffer 'p' somehow...

    return string_wrapper(p);
}

// caller
string_wrapper shortened = TextHelper::shortenWithPlaceholder("Something too long", 5);

std::cout << shortened.c_str();

Most real programs use std::string for this purpose.

甜妞爱困 2024-08-05 07:35:58

在您的示例中,调用者别无选择,只能负责释放分配的内存。

然而,这是一个容易出错的习惯用法,我不建议使用它。

允许您使用几乎相同代码的一种替代方法是将 shortened 更改为引用计数指针,并让方法返回引用计数指针而不是裸指针。

In your example the caller has no choice but to be responsible for freeing the allocated memory.

This, however, is an error prone idiom to use and I don't recommend using it.

One alternative that allows you to use pretty much the same code is to change shortened to a referenced counted pointer and have the method return that referenced counted pointer instead of a bare pointer.

夏至、离别 2024-08-05 07:35:58

我认为有两种同样常见的基本方法:
a) TextHelper 返回 c 字符串并忘记它。 用户必须删除内存。
b) TextHelper 维护已分配字符串的列表,并在销毁时释放它们。

现在这取决于您的使用模式。 b) 对我来说似乎有风险:如果 TextHelper 必须取消分配字符串,那么在用户处理完缩短的字符串之前,它不应该这样做。 您可能不知道这一点何时到来,因此您使 TextHelper 保持活动状态,直到程序终止。 这会导致内存使用模式等同于内存泄漏。 我建议仅当字符串在语义上属于提供它们的类时才使用 b),类似于 std::string::c_str()。 您的 TextHelper 看起来更像是一个工具箱,不应与已处理的字符串相关联,因此如果我必须在两者之间进行选择,我会选择 a)。 考虑到固定的 TextHelper 接口,您的用户类可能是最好的解决方案。

There are two basic ways that I consider equally common:
a) TextHelper returns the c string and forgets about it. The user has to delete the memory.
b) TextHelper maintains a list of allocated strings and deallocates them when it is destroyed.

Now it depends on your usage pattern. b) seems risky to me: If TextHelper has to deallocate the strings, it should not do so before the user is done working with the shortened string. You probably won't know when this point comes, so you keep the TextHelper alive until the program terminates. This results in a memory usage pattern equal to a memory leak. I'd recommend b) only if the strings belong semantically to the class that provides them, similar to the std::string::c_str(). Your TextHelper looks more like a toolbox that should not be associated with the processed strings, so if I had to choose between the two, I'd go for a). Your user class is probably the best solution, given a fixed TextHelper interface.

烟雨凡馨 2024-08-05 07:35:58

编辑:不,我错了。 我误解了你想要做什么。 调用者必须删除实例中的内存。

C++ 标准规定删除 0/NULL 不会执行任何操作(换句话说,这样做是安全的),因此无论您是否调用过该函数,都可以删除它。 编辑:我不知道这是怎么被遗漏的......你的另一个选择是放置删除。 在这种情况下,即使形式不好,您也应该使用placement new 将分配/释放保持在同一位置(否则不一致会使调试变得荒谬)。

也就是说,您如何使用该代码? 我不知道什么时候你会多次调用它,但如果你这样做,如果你不记得每个不同的内存块,就会有潜在的内存泄漏(我认为)。

我只会使用 std::auto_ptrBoost::shared_ptr。 它在退出时会删除自身,并且可以与 char* 一起使用。

您可以做的另一件事是考虑 TextHelper 的分配方式。 这是一个理论演员:

TextHelper(const char* input) : input_(input), copy(0) { copy = new char[sizeof(input)/sizeof(char)]; //mess with later }
~TextHelper() { delete copy; }

Edit: No, I'm wrong. I misunderstood what you were trying to do. The caller must delete the memory in your instance.

The C++ standard states that deleting 0/NULL does nothing (in other words, this is safe to do), so you can delete it regardless of whether you ever called the function at all. Edit: I don't know how this got left out...your other alternative is placement delete. In that case, even if it is bad form, you should also use placement new to keep the allocation/deallocation in the same place (otherwise the inconsistency would make debugging ridiculous).

That said, how are you using the code? I don't see when you would ever call it more than once, but if you do, there are potential memory leaks (I think) if you don't remember each different block of memory.

I would just use std::auto_ptr or Boost::shared_ptr. It deletes itself on the way out and can be used with char*.

Another thing you can do, considering on how TextHelper is allocated. Here is a theoretical ctor:

TextHelper(const char* input) : input_(input), copy(0) { copy = new char[sizeof(input)/sizeof(char)]; //mess with later }
~TextHelper() { delete copy; }
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文