I'm putting all file names (in this case all files in the folder /Windows/Fonts/) into the char File_Names. Menawhile the names are rendered as well, but after I have put everything in the char (when while ends), I try to access it and use one of the names in something else (this happens after while), but every single pointer I try only gives the last file from the folder (this means, it only has the last pointer). How do I keep all different file names and can use them after while?
Here is a fragment from my program:
//-------------------- List files start
int File_Name_Y = Font_Window_Y+35-Mouse_Scroll*10;
char *File_Names[4096];
struct dirent *entry;
DIR *dir = opendir("/Windows/Fonts");
int t = 1;
while ((entry = readdir(dir)) != NULL)
{
t += 1;
File_Names[t] = entry->d_name;
if (File_Name_Y <= Font_Window_Y+H-65 && File_Name_Y >= Font_Window_Y+30)
{
draw_text (Font_Window_X+15, File_Name_Y, File_Names[t], {1, 1, 1}, 1.5);
}
File_Name_Y += 20;
}
closedir(dir);
//------------------ List files end
if (use_button (SelectFontButton))
{
if (Mouse_Pressed)
{
Mouse_Pressed = 0;
char Font_Path[1024];
strcpy (Font_Path, "/Windows/Fonts/");
strcat(Font_Path, File_Names[3]);
strcpy (Global_Font, Font_Path);
printf (Global_Font);
}
}
You are storing the exact same value, entry->d_name in every entry. Of course they're all going to point to the same string.
You may be thinking that readdir changes the value of entry->d_name to something different every time. But it doesn't. Every time, it sets the value of entry->d_name to point to the very same buffer that readdir uses to hold the entry name.
You need to save the actual name, not a pointer to a buffer controlled by some other piece of code. Allocating an array of pointers is insufficient -- where are the actual entry names going to be stored?
Related
So first all I'll preface this with: I just started using c++.
I have a structure that I store the pointer to in an unordered_map, setting members' values in the struct pointer as I get them through my process. Then I no longer need them in a map so I transfer then to a vector and loop through them.
Though on the second loop, it outputs my index (1) but the next statement of making a local pointer var for the struct at that index breaks it and the code terminates without any errors. since there are no errors then a try/catch doesn't give me anything either.
// Wanted to create a structure to handle the objects easier instead
// of multiple vectors for each property
struct appData {
std::string id = "";
std::string name = "";
std::string vdf_file = "";
std::string vdf_path = "";
};
// Relevant parts of my main()
int main() {
// Map that stores all the struct pointers
std::unordered_map<std::string, appData*> appDatas;
char memory[sizeof(appData)];
void* p = memory;
// New instance of appData
appData *tempAppData = new(p) appData();
tempAppData->appid = "86901";
// Add tempAppData to map with string key
appDatas["86901"] = tempAppData;
...
std::vector<appData*> unhashed_appDatas;
for (auto const& pair: appDatas) {
unhashed_appDatas.push_back(pair.second);
}
...
for (unsigned int x = 0; x < unhashed_appDatas.size(); x++) {
// Output index to see where it was messing up
std::cout << x << std::endl;
!! // This is where the issue happens on the second loop (see output)
appData *thisAppData = unhashed_appDatas[x];
std::string id = thisAppData->appid;
std::cout << id << std::endl;
/* ...
Do more stuff below
*/
}
...
return 0;
}
Terminal Output:
0 // Initial index of x
86901 // Id of first item
1 // New index of x on second loop before pointer var is created
// Nothing more is printed and execution terminates with no errors
My knowledge of c++ is pretty lacking, started it couple days ago, so the few things within my knowledge I've tried: moving the *thisAppData variable outside of the loop, using a for(var: vector) { ... }, and a while loop. I can assume that the issue lies with the pointer and the local variable when inside the loop.
Any help/input about how I could better approach this or if there's an issue with my code would be appreciated :)
Edit: Changed code to use .size() instead of sizeof() per #Jarod42 answer, though main issue persists
Edit2: Turns out it was my own mess-up, imagine that. 4Am brain wasn't working too well- posted answer regarding what I did incorrectly. Thanks to everyone who helped me
sizeof is the wrong tool here:
for (unsigned int x = 0; x < sizeof(unhashed_appDatas); x++) {
// ^^ wrong: give **static** size of the structure
// mainly 3 members (data, capacity, size), so something like `3*sizeof(void*)`
it should be
for (unsigned int x = 0; x < unhashed_appDatas.size(); x++) {
After many hours of trial and error I have determined the issue (aside from doing things in a way I should, which I've since corrected) it was something I messed up on that caused this issue.
TLDR:
Items wouldn't exist that I assumed did and tried to read files with a blank path and parse the contents that didn't exist.
Explaination:
In the first loop, the data I was getting was a list of files from a directory then parsing a json-like file that contained these file names and properties associated with them. Though, the file list contained entries that weren't in this other data file (since I had no check if they existed) so it would break there.
Additionally in the last loop I would get a member from a struct that would be the path of a file to read, but it would be blank (unset) because it didn't exist in data file so std::ifstream file(path); would break it.
I've since implemented checks for each key and value to ensure it will no longer break because of that.
Fixes:
Here are some fixes that were mentioned that I added to the code, which did help it work correctly in the end even if they weren't the main issue that I myself caused:
// Thanks to #EOF:
// No longer "using placement new on a buffer with automatic storage duration"
// (whatever that means haha) and was changed from:
char memory[sizeof(appData)];
void* p = memory;
appData *tempAppData = new(p) appData();
// To:
appData *tempAppData = new appData();
// Thanks to #Jarod42:
// Last for loop limit expression was corrected from:
for (unsigned int x = 0; x < sizeof(unhashed_appDatas); x++) {
}
// To:
for (unsigned int x = 0; x < unhashed_appDatas.size(); x++) {
}
// I am still using a map, despite comment noting to just use vectors
// (which I could have, but just would prefer using maps):
std::unordered_map<std::string, appData*> appDatas;
// Instead of doing something like this instead (would have arguably have been easier):
std::vector<std::string> dataKeys = { "1234" };
std::vector<appData*> appDatas = { ... };
auto indx = find(dataKeys.begin(), dataKeys.end(), "1234");
indx = (indx != dataKeys.end() ? indx : -1);
if (indx == -1) continue;
auto dataItem = appDatas[indx];
//
I appreciate everyone's assistance with my code
I ame trying to get filenames from a directory and put it in a char* array for latter use. But this dont seem to work the way i want to. When printing it only showes the last filename on all spots.
So my question howe can i add the file names in every spot inside the char*[]?
/*Placed outside*/
int i = 0;
char* Files[20] = {};
/*Placed outside*/
while (handle != INVALID_HANDLE_VALUE)
{
char buffer[4100];
sprintf_s(buffer, "%ls", search_data.cFileName);
Files[i] = buffer;
i++;
if (FindNextFile(handle, &search_data) == FALSE)
/*Printing I use ImGui*/
#define IM_ARRAYSIZE(_ARR) ((int)(sizeof(_ARR)/sizeof(*_ARR)))
static int listbox_item_current = 1;
ImGui::ListBox("", &listbox_item_current, Files, i, 4);
You could use C++ standard filesystem, but for that I guess you would need C++17 (or atleast VS15), not really sure.
You would have to include:
#include <experimental/filesystem>
#include <filesystem>
using namespace std::experimental::filesystem::v1;
Using it should be simple:
int i = 0;
const char * directoryToSearch = "C:\etc\etc";
for (const auto & file : directory_iterator(directoryToSearch)) {
files[i] = new char[file.path().stem().string().length() + 1];
strcpy(files[i], file.path().stem().string().c_str());
++i;
}
Indeed, you should clean up the array after you're done using it. Don't forget, not many compilers support this at the moment.
When printing it only shows the last filename on all spots. That is just normal: you store the filename on each iteration in the same buffer and just copy the address of the buffer into your array. Unrelated to the question, as buffer is an automatic variable declared inside a loop (block scoped), using it outside of the loop is Undefined Behaviour, so you end with an array of dangling pointers.
The correct way would be to either use a 2D-array char Files[MAX_PATH][20]; and store a file name in each slot, or use dynamic memory allocate by new (or malloc at a lower level). For the second option, you can do it by hand, allocating memory for each file name - and remember to free anything at the end, or you can let the standard library manage it for you by using:
std::vector<std::string> Files;
...
while(...) {
...
File.push_back(search_data.cFileName);
Dear ImGui provides a ListBoxctor that allows to pass an opaque data storage along with an extractor, and it can be used here:
bool string_vector_items_getter(void* data, int idx, const char** out_text) {
std::vector<std::string> *v = reinterpret_cast<std::vector<std::string> >(data);
if (idx < 0 || idx >= v.size()) return false;
*out_text = v[idx].c_str();
return true;
}
and then:
ImGui::ListBox("", &listbox_item_current, &string_vector_items_getter, &Files, i, 4);
(beware: untested code!)
I'm brand new to C++ and I'm trying to learn on my own, so I'm running into a lot of simple issues. Right now I want to look at a directory, containing files that end in ".img", and push only the file name and creation time into a struct vector.
How does a push_back into a vector work? Does each struct pushed back populate into the vector or is it possible to make a mistake and be overwriting the previous push_back? I don't know if I'm over thinking this...
Could someone please take a look at my code and let me know if I'm doing this correctly or where I may need to make changes? Thank you so much!
void filterContextByHsiTime(const char *context_path, int hsi_create_time) {
struct contextFileStruct { //Define Struct containing key elements of a file
double createTime;
string fileName;
};
std::vector<contextFileStruct> contextVector; //Initialize resizable vector array of type contextFileStruct
contextFileStruct tempStruct;
_finddata_t allFiles; //Structure that holds file specific elements
int findFirstContextImage = _findfirst(context_path, &allFiles); //Int variable holding result of the findfirst function
//where -1 = no image fround and any other number = image found
tempStruct.fileName = allFiles.name;
tempStruct.createTime = allFiles.time_create;
contextVector.push_back(tempStruct);
if (findFirstContextImage != -1 ) {
int findNextContextImage = 0; //iterator for next context image in dir
while (findNextContextImage != -1) {
findNextContextImage = _findnext(findFirstContextImage, &allFiles);
tempStruct.fileName = allFiles.name;
tempStruct.createTime = allFiles.time_create;
contextVector.push_back(tempStruct);
}
_findclose(findFirstContextImage); //Close findfirst file function to prevent memory leaks
}
else {
cout << "There are no .img files in this directory!" << endl; //error handling for when there are no context files
}
}
I was looking at it incorrectly in the debugger. Instead of reviewing the populating of the vector, I was just looking at the struct populating and then adding the next file, so it looked as if it was overwriting. Silly error on my part. My code is working as I intended, thanks!
I have this piece of code which iterates over a directory, gets the filenames and pushes them in a vector.
std::vector<const char*> afp_filenames;
if ((dir = opendir (path.c_str())) != NULL) {
while ((ent = readdir (dir)) != NULL) {
if ( (ent->d_name[0] == '.') ) continue;
afp_filenames.push_back((const char*)ent->d_name);
cout <<afp_filenames[i]<<"\n";
i++;
}
}
cout prints the correct filenames. The issue comes afterwards when I print again the values stored in the vector. I get the same result when using an iterator.
for (j = 0; j < i; j++) {
cout <<afp_filenames[j]<<"\n";
}
Here is a sample of what I get in the console.
for the first loop:
afp_PortaAPorta_5_318_383.dat
afp_PortaAPorta_5_407_663.dat
afp_PortaAPorta_5_485_510.dat
afp_781_5_472_371.dat
afp_781_5_115_617.dat
afp_781_5_358_509.dat
afp_781_5_276_348.dat
and for the second one:
2.dat
.dat
afpò
9.dat
2.dat
5_356_572.dat
5_291_435.dat
afp_781_5_243_558.dat
afp_PortaAPorta_5_352_716.dat
afp_781_5_111_541.dat
You are storing const char *s in the vector but the pointers are no longer valid by the time you get to the second loop and so cout is trying to print out whatever is left in the memory they were pointing to.
From readdir:
On success, readdir() returns a pointer to a dirent structure. (This structure may be statically allocated; do not attempt to free(3) it.)
The lifetime of the struct returned by the system call seems to be managed by the kernel C library.
You are storing const char * pointers pointing to memory returned by readdir.
Per man 3 readdir:
The data returned by readdir() may be overwritten by subsequent calls to readdir() for the same directory stream.
Therefore, your ent->d_name entries are being overwritten each loop.
Use a vector<std::string> instead so that you store copies of the string (as an added benefit, you won't have to worry about allocating/deallocating the strings since it will be handled for you).
Probably you just need to copy the string as the pointer you get from d_name may end up pointing to garbage or something else some time later. Make a vector of std::strings and push strings into it.
I am working on a query processor that reads in long lists of document id's from memory and looks for matching id's. When it finds one, it creates a DOC struct containing the docid (an int) and the document's rank (a double) and pushes it on to a priority queue. My problem is that when the word(s) searched for has a long list, when I try to push the DOC on to the queue, I get the following exception:
Unhandled exception at 0x7c812afb in QueryProcessor.exe: Microsoft C++ exception: std::bad_alloc at memory location 0x0012ee88..
When the word has a short list, it works fine. I tried pushing DOC's onto the queue in several places in my code, and they all work until a certain line; after that, I get the above error. I am completely at a loss as to what is wrong because the longest list read in is less than 1 MB and I free all memory that I allocate. Why should there suddenly be a bad_alloc exception when I try to push a DOC onto a queue that has a capacity to hold it (I used a vector with enough space reserved as the underlying data structure for the priority queue)?
I know that questions like this are almost impossible to answer without seeing all the code, but it's too long to post here. I'm putting as much as I can and am anxiously hoping that someone can give me an answer, because I am at my wits' end.
The NextGEQ function reads a list of compressed blocks of docids block by block. That is, if it sees that the lastdocid in the block (in a separate list) is larger than the docid passed in, it decompresses the block and searches until it finds the right one. Each list starts with metadata about the list with the lengths of each compressed chunk and the last docid in the chunk. data.iquery points to the beginning of the metadata; data.metapointer points to wherever in the metadata the function currently is; and data.blockpointer points to the beginning of the block of uncompressed docids, if there is one. If it sees that it was already decompressed, it just searches. Below, when I call the function the first time, it decompresses a block and finds the docid; the push onto the queue after that works. The second time, it doesn't even need to decompress; that is, no new memory is allocated, but after that time, pushing on to the queue gives a bad_alloc error.
Edit: I cleaned up my code some more so that it should compile. I also added in the OpenList() and NextGEQ functions, although the latter is long, because I think the problem is caused by a heap corruption somewhere in it. Thanks a lot!
struct DOC{
long int docid;
long double rank;
public:
DOC()
{
docid = 0;
rank = 0.0;
}
DOC(int num, double ranking)
{
docid = num;
rank = ranking;
}
bool operator>( const DOC & d ) const {
return rank > d.rank;
}
bool operator<( const DOC & d ) const {
return rank < d.rank;
}
};
struct listnode{
int* metapointer;
int* blockpointer;
int docposition;
int frequency;
int numberdocs;
int* iquery;
listnode* nextnode;
};
void QUERYMANAGER::SubmitQuery(char *query){
listnode* startlist;
vector<DOC> docvec;
docvec.reserve(20);
DOC doct;
//create a priority queue to use as a min-heap to store the documents and rankings;
priority_queue<DOC, vector<DOC>,std::greater<DOC>> q(docvec.begin(), docvec.end());
q.push(doct);
//do some processing here; startlist is a pointer to a listnode struct that starts the //linked list
//point the linked list start pointer to the node returned by the OpenList method
startlist = &OpenList(value);
listnode* minpointer;
q.push(doct);
//start by finding the first docid in the shortest list
int i = 0;
q.push(doct);
num = NextGEQ(0, *startlist);
q.push(doct);
while(num != -1)
{
q.push(doct);
//the is where the problem starts - every previous q.push(doct) works; the one after
//NextGEQ(num +1, *startlist) gives the bad_alloc error
num = NextGEQ(num + 1, *startlist);
//this is where the exception is thrown
q.push(doct);
}
}
//takes a word and returns a listnode struct with a pointer to the beginning of the list
//and metadata about the list
listnode QUERYMANAGER::OpenList(char* word)
{
long int numdocs;
//create a new node in the linked list and initialize its variables
listnode n;
n.iquery = cache -> GetiList(word, &numdocs);
n.docposition = 0;
n.frequency = 0;
n.numberdocs = numdocs;
//an int pointer to point to where in the metadata you are
n.metapointer = n.iquery;
n.nextnode = NULL;
//an int pointer to point to the uncompressed block of data, if there is one
n.blockpointer = NULL;
return n;
}
int QUERYMANAGER::NextGEQ(int value, listnode& data)
{
int lengthdocids;
int lengthfreqs;
int lengthpos;
int* temp;
int lastdocid;
lastdocid = *(data.metapointer + 2);
while(true)
{
//if it's not the first chunk in the list, the blockpointer will be pointing to the
//most recently opened block and docpos to the current position in the block
if( data.blockpointer && lastdocid >= value)
{
//if the last docid in the chunk is >= the docid we're looking for,
//go through the chunk to look for a match
//the last docid in the block is in lastdocid; keep going until you hit it
while(*(data.blockpointer + data.docposition) <= lastdocid)
{
//compare each docid with the docid passed in; if it's greater than or equal to it, return a pointer to the docid
if(*(data.blockpointer + data.docposition ) >= value)
{
//return the next greater than or equal docid
return *(data.blockpointer + data.docposition);
}
else
{
++data.docposition;
}
}
//read through the whole block; couldn't find matching docid; increment metapointer to the next block;
//free the block's memory
data.metapointer += 3;
lastdocid = *(data.metapointer + 3);
free(data.blockpointer);
data.blockpointer = NULL;
}
//reached the end of a block; check the metadata to find where the next block begins and ends and whether
//the last docid in the block is smaller or larger than the value being searched for
//first make sure that you haven't reached the end of the list
//if the last docid in the chunk is still smaller than the value passed in, move the metadata pointer
//to the beginning of the next chunk's metadata; read in the new metadata
while(true)
// while(*(metapointers[index]) != 0 )
{
if(lastdocid < value && *(data.metapointer) !=0)
{
data.metapointer += 3;
lastdocid = *(data.metapointer + 2);
}
else if(*(data.metapointer) == 0)
{
return -1;
}
else
//we must have hit a chunk whose lastdocid is >= value; read it in
{
//read in the metadata
//the length of the chunk of docid's is cumulative, so subtract the end of the last chunk
//from the end of this chunk to get the length
//find the end of the metadata
temp = data.metapointer;
while(*temp != 0)
{
temp += 3;
}
temp += 2;
//temp is now pointing to the beginning of the list of compressed data; use the location of metapointer
//to calculate where to start reading and how much to read
//if it's the first chunk in the list,the corresponding metapointer is pointing to the beginning of the query
//so the number of bytes of docid's is just the first integer in the metadata
if( data.metapointer == data.iquery)
{
lengthdocids = *data.metapointer;
}
else
{
//start reading from the offset of the end of the last chunk (saved in metapointers[index] - 3)
//plus 1 = the beginning of this chunk
lengthdocids = *(data.metapointer) - (*(data.metapointer - 3));
temp += (*(data.metapointer - 3)) / sizeof(int);
}
//allocate memory for an array of integers - the block of docid's uncompressed
int* docblock = (int*)malloc(lengthdocids * 5 );
//decompress docid's into the block of memory allocated
s9decompress((int*)temp, lengthdocids /4, (int*) docblock, true);
//set the blockpointer to point to the beginning of the block
//and docpositions[index] to 0
data.blockpointer = docblock;
data.docposition = 0;
break;
}
}
}
}
Thank you very much, bsg.
QUERYMANAGER::OpenList returns a listnode by value. In startlist = &OpenList(value); you then proceed to take the address of the temporary object that's returned. When the temporary goes away, you may be able to access the data for a time and then it's overwritten. Could you just declare a non-pointer listnode startlist on the stack and assign it the return value directly? Then remove the * in front of other uses and see if that fixes the problem.
Another thing you can try is replacing all pointers with smart pointers, specifically something like boost::shared_ptr<>, depending on how much code this really is and how much you're comfortable automating the task. Smart pointers aren't the answer to everything, but they're at least safer than raw pointers.
Assuming you have heap corruption and are not in fact exhausting memory, the commonest way a heap can get corrupted is by deleting (or freeing) the same pointer twice. You can quite easily find out if this is the issue by simply commenting out all your calls to delete (or free). This will cause your program to leak like a sieve, but if it doesn't actually crash you have probably identified the problem.
The other common cause cause of a corrupt heap is deleting (or freeing) a pointer that wasn't ever allocated on the heap. Differentiating between the two causes of corruption is not always easy, but your first priority should be to find out if corruption is actually the problem.
Note this approach won't work too well if the things you are deleting have destructors which if not called break the semantics of your program.
Thanks for all your help. You were right, Neil - I must have managed to corrupt my heap. I'm still not sure what was causing it, but when I changed the malloc(numdocids * 5) to malloc(256) it magically stopped crashing. I suppose I should have checked whether or not my mallocs were actually succeeding! Thanks again!
Bsg