简体   繁体   中英

C++: SIGTRAP error in custrom String class

I have scoured the internet for the past two days and can find no reason for this to be occurring.

My string class works in the way one would expect: it starts with an initial block of contiguous memory on the heap (128 bytes), and then resizes if need be.

All of my calls to new are matched with a single call to delete[] on the corresponding pointer.

The SIGTRAP error only occurs as the destructor is being called (which uses delete[] a final time).

In order to produce a minimal reproducible example, I have reduced the code (about 1400 lines) to what is seen below (and what seems to be causing the error).

#include <string>
#include <iostream>
#include <cstring>

static int MIN_SIZE = 128;

    char *setchr_c(char *str, const char ch, size_t pos) {
        if (str == nullptr) {
            return nullptr;
        }
        *(str + pos) = ch;
        return str;
    }

    class String {
    private:
        char *data = nullptr;
        char *string = nullptr;
        size_t length_w_null = 0;
        size_t size = 0;
        size_t space_front = 0;
        size_t space_back = 0;
        bool is_empty = true;

        void set_size(bool def_do = true) {
            if (def_do) {
                do {
                    size *= 2;
                } while (size < length_w_null);
                return;
            }
            while (size < length_w_null) {
                size *= 2;
            }
        }

        void constructor(const char *str, bool after_empty = false) {
            size = MIN_SIZE;
            length_w_null = strlen(str) + 1;
            set_size(false);
            if (after_empty) { // this is to avoid re-assigning the memory if not necessary
                if (size != MIN_SIZE) {
                    // free(data);
                    printf("CTOR AFTER EMPTY DELETE, DELETING: %x\n", data);
                    delete[] data;
                    // data = (char *) malloc(size);
                    printf("CTOR AFTER EMPTY NEW, SIZE: %llu, ", size);
                    data = new char[size];
                    printf("ADDRESS: %x\n", data);
                }
            } else {
                // data = (char *) malloc(size);
                printf("CTOR ELSE NEW, SIZE: %llu, ", size);
                data = new char[size];
                printf("ADDRESS: %x\n", data);
            }
            memset(data, '\0', size);
            string = data + get_first_pos();
            setchr_c(string, '\0', length_w_null - 1);
            strcpy(string, str);
            space_front = get_first_pos();
            space_back = is_even(length_w_null) ? space_front : space_front + 1;
            is_empty = false;
        }

        void empty_constructor() {
            // data = (char *) malloc(MIN_SIZE);
            printf("EMPTY CTOR NEW, MINSIZE: %llu, ", MIN_SIZE);
            data = new char[MIN_SIZE];
            printf("ADDRESS: %x\n", data);
            memset(data, '\0', size);
            string = nullptr;
            length_w_null = 0;
            size = MIN_SIZE;
            space_front = MIN_SIZE / 2;
            space_back = MIN_SIZE / 2;
            is_empty = true;
        }

        static bool is_even(size_t num) {
            return num % 2 == 0;
        }

        [[nodiscard]] unsigned long get_first_pos() const {
            size_t new_len = length_w_null;
            if (!is_even(new_len)) {
                new_len++;
            }
            unsigned long pos = size / 2 - (new_len) / 2;
            return pos;
        }

    public:
        static const size_t nopos = -1;
        String() {
            empty_constructor();
        }

        String(char ch) {
            if (ch == '\0') {
                empty_constructor();
            } else {
                const char str[2]{ch, '\0'};
                constructor(str);
            }
        }

        String(const char *str) {
            if (str == nullptr) {
                return;
            }
            if (strlen(str) == 0) {
                empty_constructor();
            } else {
                constructor(str);
            }
        }

        void append_back(const char *str) {
            if (str == nullptr) {
                return;
            }
            if (is_empty) {
                constructor(str, true);
                return;
            }
            size_t l = strlen(str);
            size_t old_l = length_w_null - 1;
            length_w_null += l;
            if (l + 1 > space_back) {
                set_size();
                char *old_data = data;
                char *old_str = string;
                // data = (char *) malloc(size);
                printf("APPEND BACK NEW, SIZE: %llu, ", size);
                data = new char[size];
                printf("ADDRESS: %x\n", data);
                memset(data, '\0', size);
                string = data + get_first_pos();
                strcpy(string, old_str);
                // free(old_data);
                printf("APPEND BACK DELETE OLD_DATA, DELETING: %x\n", old_data);
                delete[] old_data;
                space_front = get_first_pos();
                space_back = is_even(length_w_null) ? space_front : space_front + 1;
            }
            strcpy(string + old_l, str);
            space_back -= l;
        }

        void append_front(const char *str) {
            if (str == nullptr) {
                return;
            }
            if (is_empty) {
                constructor(str, true);
                return;
            }
            size_t l = strlen(str);
            length_w_null += l;
            char gone = *string;
            if (l > space_front) {
                set_size();
                char *old_data = data;
                char *old_str = string;
                // data = (char *) malloc(size);
                printf("APPEND FRONT NEW, SIZE: %llu, ", size);
                data = new char[size];
                printf("ADDRESS: %x\n", data);
                memset(data, '\0', size);
                string = data + get_first_pos();
                strcpy(string + l, old_str);
                // free(old_data);
                printf("APPEND FRONT DELETE OLD_DATA, DELETING: %x\n", old_data);
                delete[] old_data;
                strcpy(string, str);
                space_front = get_first_pos();
                space_back = is_even(length_w_null) ? space_front : space_front + 1;
            } else {
                strcpy(string - l, str);
                string -= l;
                space_front -= l;
            }
            setchr_c(string, gone, l);
        }

        void push_back(char ch) {
            if (ch == 0) {
                return;
            }
            const char str[2] = {ch, '\0'};
            append_back(str);
        }

        void push_front(char ch) {
            if (ch == 0) {
                return;
            }
            const char str[2] = {ch, '\0'};
            append_front(str);
        }

        void clear() noexcept {
            // free(data);
            printf("CLEAR DELETE, DELETING: %x\n", data);
            delete[] data;
            empty_constructor();
        }

        size_t get_size() const {
            return size;
        }

        size_t get_length() const {
            return length_w_null == 0 ? 0 : length_w_null - 1;
        }

        const char *c_str() const noexcept {
            return string;
        }

        ~String() {
            // free(data);
            printf("DTOR DELETE, DELETING: %x\n", data);
            delete[] data;
        }

        friend std::ostream& operator<<(std::ostream& os, const String& str);
    };
    std::ostream& operator<<(std::ostream& os, const String& str) {
        os << str.string;
        return os;
    }
using namespace std;
int main() {
    String bro;
    char array[] = "eeee;;dlfkjas;j;a;lAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAsdjfk";
    bro.clear();
    for (const char &ch : array) {bro.push_front(ch);}
    bro.clear();
    bro.append_back("Let us see!");
    cout << bro << endl;
    return 0;
}

I added in a few printf() statements to check the addresses, and there is nothing obviously wrong.

A sample output I get is:

EMPTY CTOR NEW, MINSIZE: 128, ADDRESS: 9b651920
CLEAR DELETE, DELETING: 9b651920
EMPTY CTOR NEW, MINSIZE: 128, ADDRESS: 9b651920
APPEND FRONT NEW, SIZE: 256, ADDRESS: 9b6519b0
APPEND FRONT DELETE OLD_DATA, DELETING: 9b651920
CLEAR DELETE, DELETING: 9b6519b0
EMPTY CTOR NEW, MINSIZE: 128, ADDRESS: 9b651920
Let us see!
DTOR DELETE, DELETING: 9b651920

... before the program crashes.

Compiler: MinGW on Windows.

user4581301 has been kind enough to check my code and has pointed out the blatant fault in my empty_constructor() member function: I use memset() to set the values of size characters (which can be larger than 128) in my data pointer, which only has 128 bytes allocated to it (initially).

By changing size to MIN_SIZE as the memset() argument, everything has been resolved.

Many thanks to user4581301.

I will be accepting this answer as soon as I can to close the question.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM