c++vectorcorruptionpush-back

The Data Inside C++ Vector Changes when I use push_back()


I wrote a C++ function that uses the recursive_directory_iterator to get information on all files and folders within the parent folder. I then wrote a class called DataFolder to store each file's attributes such as file size, path, name, datenum, etc. Finally, I used a vector object to store a collection of DataFolder objects.

For the vector object I use push_back to add the DataFolder objects. The first assignment works, however on subsequent attempts the assignments are made without memory or access issues, but the data contained in all of the previously stored items are corrupted. Only the most recent addition is unchanged.

How do I add DataFolder objects to my C++ STL vector object and preserve the data previously stored within the vector?

This is the function I created: directory_base.

#include <iostream>
#include <filesystem>
#include <ctime>
#include <vector>
#include "CDir.h"

using namespace std;
using namespace std::filesystem;

// CDIR List folder.
// CDIR NAME lists the files in a folder.NAME must be specified as a
// character vector or string scalar.
//
// NAME can include a relative path, but the relative path must be in the
// current folder.Otherwise, NAME must include a full path.
//
// To list filesand folders at a remote location, NAME must contain a
// full path specified as a uniform resource locator(URL).
//
// Pathnamesand asterisk wildcards may be used in NAME.A single asterisk
// in the path touching only file separators will represent exactly one
// folder name.A single asterisk at the end of an input will represent
// any filename.An asterisk followed or preceded by characters will
// resolve to zero or more characters.A double asterisk can only be used
// in the path and will represent zero or more folder names.It cannot
// touch a character other than a file separator.For example, DIR* .m
// lists all files with a.m extension in the current folder.DIR*/*.m
//   lists all files with a .m extension exactly one folder under the
//   current folder. CDIR **/* .m lists all files with a.m extension zero or
// more folders under the current folder.
//
// D = CDIR('NAME') returns the results in an M - by - 1
// structure with the fields :
// name        -- Filename
// folder      -- Absolute path
// date        -- Modification date
// bytes       -- Number of bytes allocated to the file
// isdir-- 1 if name is a folder and 0 if not
// datenum     -- Modification date as a MATLAB serial date number.
// This value is locale - dependent.
//
// See also WHAT, CD, TYPE, DELETE, LS, RMDIR, MKDIR, DATENUM.

// Copyright 1984 - 2019 The MathWorks, Inc.
// Built - in function.

static const char* convertIntToMonth(int imon)
{
    if (imon == 1)
    {
        return "Jan";
    }
    else if (imon == 2)
    {
        return "Feb";
    }
    else if (imon == 3)
    {
        return "Mar";
    }
    else if (imon == 4)
    {
        return "Apr";
    }
    else if (imon == 5)
    {
        return "May";
    }
    else if (imon == 6)
    {
        return "Jun";
    }
    else if (imon == 7)
    {
        return "Jul";
    }
    else if (imon == 8)
    {
        return "Aug";
    }
    else if (imon == 9)
    {
        return "Sep";
    }
    else if (imon == 10)
    {
        return "Oct";
    }
    else if (imon == 11)
    {
        return "Nov";
    }
    else if (imon == 12)
    {
        return "Dec";
    }
    else if (imon < 1 || imon > 12)
    {
        return "OOR";
    }

    return "INV";
}

static double dateNumber(double year, int month, double day)
{
    double cumdays[] = { 0, 0,31,59,90,120,151,181,212,243,273,304,334 };
    double retval = 365 * year + cumdays[month] + day;
    retval += year / 4;
    return retval;
}

const vector<DataFolder>& directory_base(const char* path, vector<DataFolder>& datfldr)
{
    int ii = 0;
    
    cout << "Let put out the files!" << endl;
    for (recursive_directory_iterator i(path), end; i != end; ++i)
    {
        if (!is_directory(i->path()))
        {
            if (is_regular_file(i->path()))
            {
                DataFolder df{};
                filesystem::path df_name = i->path();
                string fn = df_name.string();
                df.name = new char[fn.length()];
                df.name = const_cast<char*>(fn.c_str());
                struct stat attrib {};
                struct tm *clock = new tm();
                stat(df.name, &attrib);
                errno_t errNo = localtime_s(clock, &(attrib.st_mtime));
                size_t fsz = file_size(df.name);
                df.bytes = static_cast<int>(file_size(df_name));
                
                string tmp = to_string(clock->tm_mday) + "-" + 
                convertIntToMonth(clock->tm_mon+1) + "-" + to_string(clock->tm_year+1900) + " 
                " + to_string(clock->tm_hour) + ":" + to_string(clock->tm_min) + ":" + 
                to_string(clock->tm_sec);

                df.date = new char[tmp.length()];
                df.date = const_cast<char*>(tmp.c_str());
                
                df.datenum = dateNumber(static_cast<double>(clock->tm_year)+1900, clock-
                >tm_mon+1, clock->tm_mday);

                filesystem::path df_fldr = i->path().parent_path();
                string ffldr = df_fldr.string();
                df.folder = new char[ffldr.length()];
                df.folder = const_cast<char*>(ffldr.c_str());
                df.isdir = false;
                
                cout << i->path().filename() << "\n";
                
                cout << "\nData Folder Values" << endl;
                cout << "df.name:       " << df.name << endl;
                cout << "df.date:       " << df.date << endl;
                cout << "df.bytes:      " << df.bytes << endl;
                cout << "df.datenum:    " << df.datenum << endl;
                cout << "df.folder:     " << df.folder << endl;
                cout << "df.df.isdir:   " << df.isdir << endl;
                datfldr.push_back(df);
            }
        }
    }

    return datfldr;
}

I expect the data to appear like this:

expected data folder storage result

Instead, I got this:

Actual Data Folder Storage Result


Solution

  • The problem are these three lines:

    string ffldr = df_fldr.string();
    df.folder = new char[ffldr.length()];
    df.folder = const_cast<char*>(ffldr.c_str());
    

    The first line creates a local object, which will go out of scope and be destructed once the if (is_regular_file(i->path())) block ends.

    The second line allocates memory for a string of ffldr.length() minus one characters, because C-style string character arrays needs space for a null-terminator character.

    The third line overwrites the pointer you just created, loosing the memory you allocated. The pointer will also become invalid once the block ends, and the life-time of ffldr ends.

    The natural C++ solution is of course to make DataFolder::folder a std::string.

    If it can't be that for some reason, you first need to allocate ffldr.length() + 1 characters and use std::strcpy to copy the string.