0

I'm trying to build an tree kind structure, which will store tokens for an IMAP command. I'm trying to add strings to them, and free them. But valgrind complains, and I do not know why.

#include <iostream>
#include <algorithm>
#include <vector>

#include <string.h>

typedef enum : uint8_t
{
    TT_STRING,
    TT_INT32,
    TT_INT64,
    TT_CHAR,
    TT_PAIR
} TokenType;

typedef struct
{
    int32_t p_From;
    int32_t p_To;
} Pair;

struct Token
{
    union {
        char *t_String;
        int32_t t_Int32;
        int64_t t_Int64;
        char t_Char;
        Pair t_Pair;
    };
    TokenType t_Type;
    std::vector<Token> t_Children;
};

typedef struct Token Token;

void _token_free(Token &token)
{
    if (token.t_Type == TT_STRING)
    {
        delete token.t_String;
    }

    for_each(token.t_Children.begin(), token.t_Children.end(), [=](Token &t){
        _token_free(t);
    });
}

Token _token_str_new(const std::string &str)
{
    Token token;
    token.t_Type = TT_STRING;
    token.t_String = new char[str.size() + 1];
    memcpy(reinterpret_cast<void *>(token.t_String), str.c_str(), str.size() + 1);
    return token;
}

int main() {
    Token token;

    token.t_Int64 = 123;
    token.t_Type = TT_INT64;
    token.t_Children = {
            _token_str_new("Hello World")
    };

    _token_free(token);

    return 0;
}

Valgrind says:

Mismatched free() / delete / delete []
_token_free(Token&)
_token_free(Token&)::{lambda(Token&)#1}::operator()(Token&) const
_token_free(Token&)::{lambda(Token&)#1} std::for_each<__gnu_cxx::__normal_iterator, _token_free(Token&)::{lambda(Token&)#1}>(__gnu_cxx::__normal_iterator<Token*, std::vector>, _token_free(Token&)::{lambda(Token&)#1}, _token_free(Token&)::{lambda(Token&)#1})
_token_free(Token&)
main
Address 0x4dc0c80 is 0 bytes inside a block of size 12 alloc'd
operator new[](unsigned long)
_token_str_new(std::__cxx11::basic_string<char, std::char_traits, std::allocator> const&)
main
Asteroids With Wings
  • 17,071
  • 2
  • 21
  • 35
skywa04885
  • 13
  • 1
  • 3
    `t_String` was allocated with `new[]`, but deleted with `delete`. You need to use the `delete[]` form. (Or better, use [smart pointers](https://en.cppreference.com/w/cpp/memory/unique_ptr) and/or [standard containers](https://en.cppreference.com/w/cpp/container/vector) so you don't need to manually handle memory management.) – 0x5453 Jul 23 '20 at 19:16
  • 1
    Unrelated: C++ learned a lot from C. One of the things it learned was the `typedef` struct trick to keep from having to type `struct` or `enum` all the time even though everyone knows darn well it's a `struct` or `enum`. C++ baked the trick in. `struct Pair { members... };` is all you need to do to be able to use `Pair`. – user4581301 Jul 23 '20 at 19:48
  • When SO tells you to do more explaining and less code dumping, instead of giving us _Lorem Ipsum_, follow the advice... – Asteroids With Wings Jul 23 '20 at 20:38
  • Also, your `Token`, class has no copy constructor, but `token.t_Children = {` will copy it, potentially leading to double delete. [What is the Rule of 3?](https://stackoverflow.com/questions/11024438/rule-of-three-in-c?rq=1) [What is the Rule of Zero?](https://stackoverflow.com/q/44997955/845092) – Mooing Duck Jul 23 '20 at 21:01

1 Answers1

5

When you assign to token.t_String in _token_str_new, you use new[]. _token_free uses delete, which causes the mismatch.

You need to use delete [] token.t_String in _token_free.

1201ProgramAlarm
  • 32,384
  • 7
  • 42
  • 56