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!)
Related
I've found a very strange issue with both printf (and printf_s), and also std::cout. I'm not sure if it's some short of "glitch" or error in these functions, or if I'm doing something wrong. Since both functions do the same, I'm assuming I'm not doing it the right way.
I have the following structures in my program (By the way, this is a Visual C++ 2010 project):
#pragma pack(push, 1)
typedef struct nameentry
{
char NAME[17];
char EXT[4];
}NAMEENTRY;
#pragma pack(pop)
#pragma pack(push, 1)
typedef struct fileentry
{
unsigned int ID;
NAMEENTRY FILENAME;
unsigned int GPFID;
unsigned long long int FPOINTER;
size_t FILESIZE;
}FILEENTRY;
#pragma pack(pop)
Now I have the following portion of code:
NAMEENTRY fname = MCreateNameEntry("LONGFILE.JPG");
FILEENTRY* myfile_ = SearchFileByPkgID(0, fname);
printf("%s", myfile_->FILENAME.NAME);
So what this code is supposed to do is, create an instance of NAMEENTRY with NAME=LONGFILE, and EXT=JPG. Both character arrays are null terminated (last byte is a 0). Then create an instance of FILEENTRY with it's corresponding data from a database I'm developing, then print the name of the file from the FILEENTRY's NAMEENTRY structure.
After running the code, what I get instead of the name of the file, is... garbage. The classic garbage you get when trying to print text from a bad pointer. If I try to print any of the other fields, I also get wrong values.
So obviously, my first thought was that one of my functions were not returning the right value. So I started inspecting the code and, to my surprise, they are actually returning the right values and the structure is filled with the right data. I get the proper values in each field, every character array ends with a 0, etc.
So then I said... "What if I copy the entire block into another instance of FILEENTRY?", and I tried this:
NAMEENTRY fname = MCreateNameEntry("LONGFILE.JPG");
FILEENTRY* myfile_ = SearchFileByPkgID(0, fname);
FILEENTRY dMem;
memcpy(&dMem, myfile_, sizeof(FILEENTRY));
printf("%s", dMem.FILENAME.NAME);
And guess what? It works perfectly fine. I get the name of the file, no garbage. So I'm assuming, either the problem is inside of printf (I also tried std::cout with the same results), or I am doing something wrong when using these functions.
Well, that helps. Seems like the problem was trying to return a pointer to a local variable, as Igor Tandetnik suggested.
So as a workaround, I'm not sure if this is a proper way of handling this, instead of define a local variable, I'm using calloc to allocate a memory block for a FILEENTRY pointer, then fill it and return it. And yes, it seems to work this way.
This is the actual code of the function:
FILEENTRY* SearchFileByPkgID(int ID, NAMEENTRY fname)
{
FILEENTRY* myFile = (FILEENTRY*)calloc(sizeof(FILEENTRY),1);
std::vector<int> results;
unsigned int* dptable = GetDPTableByPkgId(ID);
bool found = false;
for(int x = 0; x < 1024; x++)
{
if(dptable[x] > 0)
{
fseek(PDBFILE, dptable[x], SEEK_SET);
fread(myFile, sizeof(FILEENTRY), 1, PDBFILE);
if(strcmp(myFile->FILENAME.EXT, fname.EXT) == 0)
if(myFile->FILENAME.NAME[0] == fname.NAME[0])
results.push_back(dptable[x]);
}
}
for(int y = 0; y < results.size(); y++)
{
fseek(PDBFILE, results[y], SEEK_SET);
fread(myFile, sizeof(FILEENTRY), 1, PDBFILE);
if(strcmp(myFile->FILENAME.NAME, fname.NAME) == 0)
{
found = true;
break;
}
}
results.clear();
if(found)
return myFile;
else
return 0L;
}
Any more suggestions are wellcome.
My program is
#include <iostream>
char * grabNumber ( char * begin )
{
// Interpret *begin as the start of a double and add the characters to a
// string retstr
char * begincpy = begin;
int foundDot = 0;
while ((*begin >= '0' && *begin <= '9') || *begin == '.')
{
if (*begin == '.')
{
if (foundDot == 0) foundDot = 1;
else break;
}
++begin;
}
long n = begin - begincpy; // # of characters parsed
char * retstr = malloc(sizeof(char) * (n + 1)); // string to be returned
for (long k = 0; k < n; ++k) retstr[k] = *begincpy++;
retstr[n] = '\0';
return retstr;
}
int main()
{
char str [] = "abc3.14def";
std::cout << grabNumber(str+3); // should print "3.14"
return 0;
}
and the errors I'm getting are
Line 20: warning: incompatible implicit declaration of built-in
function 'malloc' Line 21: error: 'for' loop initial declaration used
outside C99 mode
corresponding to the 2 lines
char * retstr = malloc(sizeof(char) * (n + 1)); // string to be returned
for (long k = 0; k < n; ++k) retstr[k] = *begincpy++;
See: http://codepad.org/c2tNGFEo
Also, is there a way that I can cut down on the redundancy of my algorithm, because it's checking for a . twice in each iteration of the while loop, and yet I can't think of a cleaner way to handle the fact that I need to stop the loop if we've run into a second .
I'm guessing you are trying to write C++ as you have included iostream and used std::cout. However the error message shows you are using a C compiler. I also guess that you wrote gcc myprogram.c. To get C++ compilation you either need to write g++ instead of gcc, or rename your file to have a .cc extension. (Preferably both).
To use malloc you need #include <cstdlib>.
Also you may need using namespace std; or using std::malloc; after that; and you will need to cast the value returned by malloc because C++ does not implicitly convert from void * to other pointer types.
However malloc is rarely used in C++ as it does not initialize non-trivial objects properly. Consider changing this code to:
char * retstr = new char[n+1];
then you won't need any extra includes.
But this is still a weak design as you are now relying on the caller to free the memory. In fact your main function has a memory leak as it does not free the memory.
In C++ it is better style to have memory managed by a container class that knows about memory management; so the programmer can't make any mistakes. (Incase you are wondering, this usually doesn't introduce any inefficiency and may even speed things up).
A much better approach would be to #include <string>, make the function return std::string, and change the last five lines of your function to:
return { begincpy, begin };
or if using a pre-C++11 compiler,
return std::string(begincpy, begin);
Let's start by observing that you are not writing C, you are writing C++. You should fix your compilation/project settings so you compile your files using the C++ compiler instead of the C compiler. This will fix the compilation error about the for loop also, as that is not valid in C before C-99.
Secondly, the first warning is actually due to a missing include. In C you would #include <stdlib.h> in C++ you'd #include <cstdlib> to get the definitions from the C standard library.
Can't seem to figure out why exactly this program won't work. It is supposed to store data from a csv file into a structure called SurnameInfo (when used with a loop that iterates through each line) but whenever I run it it gets to line 1280 of 151671 of the csv file, crashes, and gives the windows "program.exe has stopped working" popup. Anyone see anything that might cause this? Thanks!!
#include <iostream>
#include <fstream>
#include <cstring>
#include <cstdlib>
using namespace std;
const int MAXLINE = 1000;
const int MAXARRAY = 1000;
int numberOfNames;
struct SurnameInfo
{
char *name;
int count;
float pctrace[6];
};
SurnameInfo*surnames[MAXARRAY];
void processLine(char *line, int n)
{
surnames[n] = new SurnameInfo; //allocate memory
char * pch = strtok(line, ",");//start tokenizing
int len = strlen(pch); // name length
surnames[n]->name = new char[len+1]; //allocate memory
strcpy(surnames[n]->name, pch); // copy name
surnames[n]->count = atoi(strtok(NULL, ","));//get count
for (int i = 0; i < 6; i++)
{
pch = strtok(NULL, ",");
surnames[n]->pctrace[i] = pch[0] == '(' ? -1 : atof(pch);
}
}
void readLines()
{
char line[MAXLINE];
ifstream inputfile;
inputfile.open("names.csv");
if (!inputfile) return; // can't open
inputfile.getline(line, MAXLINE); //skip title
inputfile.getline(line, MAXLINE);
numberOfNames = 0;
while (!inputfile.eof()) //not end of file
{
processLine(line, numberOfNames++);
inputfile.getline(line, MAXLINE);
}
inputfile.close();
}
int main() {
readLines();
return 0;
}
I see a discrepancy in the code and the stuff that you are talking.
const int MAXARRAY = 1000; && SurnameInfo*surnames[MAXARRAY]; goes against 151671 of the csv file.
You are allocating 1000 and trying to push more to the heap unattended which means it starts eating the memory allocated to the program itself. Or it tries to access the area which it is not supposed to (may be program area of some other process is allocated), and thus pushes out a Segmentation Fault
Also, you need to have a way to destruct the Surnames that are dynamically fed.
My Suggestion :
Approach 1 : Read through the file first and get the number of lines. Allocate the respective memory to Surnames and proceed the way you are.
Though it requires one additional scan of file, but would solve your purpose. Time complexity goes very high if the file size is high.(May be you can cache stuff while reading , use vector?? (think on that))
Approach 2 : Implement a functionality similar to resize of vector.On every new addition to the Surnames, Free the previously allocated memory on heap and reallocate with the higher memory spec by deep copying and inserting new info.
Also,
surnames[n]->pctrace[i] = pch[0] == '(' ? -1 : atof(pch);
I am not very sure whether this would work correctly or not. Just for the sake of safety and more clear code, put that up in parenthesis . Something like this
surnames[n]->pctrace[i] = ((pch[0] == '(') ? -1 : atof(pch));
If this is your one of the first attempts on C++, this is nicely done. Cheers.
Hope the answer helps.
I created a function that returns an error code (ErrCode enum) and pass two output parameters. But when I print the result of the function, I don't get the correct values in the array.
// .. some codes here ..
ErrCode err;
short lstCnt;
short lstArr[] = {};
err = getTrimmedList(lstArr, &lstCnt);
// list returned array (for comparison)
for (int i=0; i<lstCnt; ++i)
printf("lstArr[%3d] = %d", i, lstArr[i]);
// .. some codes here ..
The getTrimmedList function is like this:
ErrCode getTrimmedList(short* vList, short* vCnt)
{
short cnt;
ErrCode err = foo.getListCount(FOO_TYPE_1, &cnt);
if (NoError!=err) return err;
short* list = new short [cnt];
short total = 0;
for (short i=0; i<cnt; ++i)
{
FooBar bar = foo.getEntryByIndex(FOO_TYPE_1, i);
if (bar.isDeleted) continue;
list[total] = i;
++total;
}
*vCnt = total;
//vList = (short*)realloc(index, sizeof(short)*total);
vList = (short*)malloc(sizeof(short)*total);
memcpy(vList, list, sizeof(short)*total)
// list returned array (for comparison)
for (int i=0; i<lstCnt; ++i)
printf("lstArr[%3d] = %d", i, lstArr[i]);
return NoError;
}
where:
foo is an object that holds arrays of FooBar objects
foo.getListCount() returns the number of objects with type FOO_TYPE_1
FOO_TYPE_1 is the type of object we want to take/list
foo.getEntryByIndex() returns the ith FooBar object with type FOO_TYPE_1
bar.isDeleted is a flag that tells if bar is considered as 'deleted' or not
What's my error?
Edit:
Sorry, I copied a wrong line. I commented it above and put the correct line.
Edit 2
I don't have control over the returns of foo and bar. All their function returns are ErrCode and the outputs are passed through parameter.
Couple of questions before I can answer your post...
Where is "index" defined in:
vList = (short*)realloc(index, sizeof(short)*total);
Are you leaking the memory associated with:
short* list = new short [cnt];
Is it possible you have accidentally confused your pointers in memory allocation? In any case, here is an example to go from. You have a whole host of problems, but you should be able to use this as a guide to answer this question as it was originally asked.
WORKING EXAMPLE:
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
int getTrimmedList(short** vList, short* vCnt);
int main ()
{
// .. some codes here ..
int err;
short lstCnt;
short *lstArr = NULL;
err = getTrimmedList(&lstArr, &lstCnt);
// list returned array (for comparison)
for (int i=0; i<lstCnt; ++i)
printf("lstArr[%3d] = %d\n", i, lstArr[i]);
// .. some codes here ..
return 0;
}
int getTrimmedList(short** vList, short* vCnt)
{
short cnt = 5;
short* list = new short [cnt];
short* newList = NULL;
short total = 0;
list[0] = 0;
list[1] = 3;
list[2] = 4;
list[3] = 6;
total = 4;
*vCnt = total;
newList = (short*)realloc(*vList, sizeof(short)*total);
if ( newList ) {
memcpy(newList, list, sizeof(short)*total);
*vList = newList;
} else {
memcpy(*vList, list, sizeof(short)*total);
}
delete list;
return 0;
}
You have serious problems.
For starters, your function has only one output param as you use it: vCnt.
vList you use as just a local variable.
realloc is called with some index that we kow nothing about, not likely good. It must be something got from malloc() or realloc().
The allocated memory in vList is leaked as soon as you exit getTrimmedList.
Where you call the function you pass the local lstArr array as first argument that is not used for anything. Then print the original, unchanged array, to bounds in cnt, while it has 0 size still -- behavior is undefined.
Even if you managed to pass that array by ref, you could not reassign it to a different value -- C-style arrays can't do that.
You better use std::vector that you can actually pass by reference and fill in the called function. eliminating the redundant size and importantly the mess with memory handling.
You should use std::vector instead of raw c-style arrays, and pass-by-reference using "&" instead of "*" here. Right now, you are not properly setting your out parameter (a pointer to an array would look like "short **arr_ptr" not "short *arr_ptr", if you want to be return a new array to your caller -- this API is highly error-prone, however, as you're finding out.)
Your getTrimmedList function, therefore, should have this signature:
ErrCode getTrimmedList(std::vector<short> &lst);
Now you no longer require your "count" parameters, as well -- C++'s standard containers all have ways of querying the size of their contents.
C++11 also lets you be more specific about space requirements for ints, so if you're looking for a 16-bit "short", you probably want int16_t.
ErrCode getTrimmedList(std::vector<int16_t> &lst);
It may also be reasonable to avoid requiring your caller to create the "out" array, since we're using smarter containers here:
std::vector<int16_t> getTrimmedList(); // not a reference in the return here
In this style, we would likely manage errors using exceptions rather than return-codes, however, so other things about your interface would evolve, as well, most likely.
I'm relatively new to C++ memory management, and I'm getting this weird error of heap corruption (plus an automatic breakpoint in Visual Studio before it). Here is the offending code:
z_world::z_world(char* name)
{
unsigned int i, skip;
char tmp;
//Load data from file
std::string* data = loadString(name);
//Base case if there is no world data
tiles = NULL;
w = 0;
h = 0;
if(data->length() > 0) {
//Set up the 'tiles' array
for(i = 0; i < data->length(); i++) {
if(data->at(i) == '\n')
h++;
if(h == 0)
w++;
}
tiles = new int[data->length()-h];
//Load Data
skip = 0;
for(i = 0; i < data->length(); i++) {
if(data->at(i) == '\n') {
skip++;
printf("\n");
continue;
}
tmp = data->at(i);
tiles[i+skip] = atoi(&tmp);
printf("%i ",tiles[i+skip]);
}
}
delete data;
}
Here's where I load in the string:
std::string* loadString(char* name)
{
ifstream in(name);
std::string* input = new string();
while(in) {
std::string line;
getline(in,line);
input->append(line);
input->append("\n");
}
in.close();
return input;
}
I get the breakpoint and error inside of "delete data;", which makes me think that "data" gets deleted somewhere before that, but I can't find where it would. For reference, this method is to create an object that contains world data for a game in the form of a virtual 2D integer array (for the ID's of the tiles).
Youre problem is probably here:
tiles[i+skip] = atoi(&tmp);
Problem 1:
It should be -skip
tiles[i - skip] =
Problem 2:
The atoi() command is being used incorrectly (tmp does not contain a string). But also I don't think atoi() is the appropriate method. I think what you are looking for is simple assignment. The conversion from char to int is automatic:
tiles[i - skip] = tmp;
Problem 3:
You are not using objects correctly. In this situation there is no need to generate dynamic objects and create a mess with dynamic memory management. It would be simpler to just to create automatic objects and pass those back normally:
std::string* loadString(char* name)
// ^ Don't do this.
std::string loadString(std::string const& name)
// ^^^^^^^ return a string by value.
// The compiler will handle memory management very well.
In general you should not be passing pointers around. In the few situations where you do need pointers they should be held within a smart pointer object or containers (for multiple objects) so that their lifespan is correctly controlled.
atoi(&tmp);
atoi expects a pointer to a null terminated string - not a pointer to a char
There's no need to dynamically allocate the string in the code you've shown. Change the loadString function to
std::string loadString(char* name)
{
ifstream in(name);
std::string input;
// ...
return input;
}
In the caller
std::string data = loadString( name );
Now there's no need to delete the string after you're done.
Instead of
int *tiles = NULL;
tiles = new int[data->length()-h];
use
std::vector<int> tiles;
tiles.resize(data.length() - h);
Also, if you do need to dynamically allocate objects you should be using smart pointers (std::unique_ptr and std::shared_ptr) instead of raw pointers.
There is a bug in
tiles[i+skip] = atoi(&tmp);
For example, for a string
Hello\n
World\n
and for the loop iteration at the point of i == 10, skip is already 1 (since we have encountered the first \n before) and you are writing to tiles[10 + 1], but tiles only has been allocated as an array with 10 elements.
May be the variable input is local to this function. So after returning from this the memory is freed. So, calling later delete on this string tries to free already freed memory.