c++ read content of a pointer - c++

i'm new in c++ world, i just use it for litle app that help me in my work, now, i need to read the content of a folder, list the folder content, i've made a function that return a pointer with the name of every obj in the folder, but now, i don't know how to read the content of the pointer to just print it in a console, my function look like this
string* listdir (const char *path)
{
string* result = new string[50]; // limit to 50 obj
DIR *pdir = NULL;
pdir = opendir (path);
struct dirent *pent = NULL;
if (pdir == NULL)
{
printf ("\nERROR! pdir could not be initialised correctly");
return NULL;
}
int i = 0;
while (pent = readdir (pdir))
{
if (pent == NULL)
{
printf ("\nERROR! pent could not be initialised correctly");
return NULL;
}
//printf ("%s\n", pent->d_name);
result[i++]= pent->d_name;
}
closedir (pdir);
return result;
}
i've been trying to print the result of teh function
int main()
{
string *dirs;
dirs = listdir("c:\\");
int i = 0;
//while(dirs[i])
//{
//cout<<dirs[i]<<'\n';
//++i;
//}
}
but i really don't know what i'm doing, lol, some help would be perfect
thanks

Examine your while loop condition : dirs[i] is a std::string. You are using a string object in a boolean context : would you expect std::string to convert to bool ?
My recommendation : ditch the fixed sized array and go for std::vector.
void listdir(const char *path, std::vector<std::string> &dirs)
{
/* ... */
while (pent = readdir (pdir))
{
/* ... */
dirs.push_back(pent->d-name);
}
closedir(pdir);
}
int main()
{
std::vector<std::string> dirs;
listdir("c:\\", dirs);
for (std::vector<std::string>::const_iterator it = dirs.begin(), end = dirs.end(); it != end; ++it)
std::cout << *it << std::endl;
}

int main()
{
string *dirs;
dirs = listdir("c:\\");
for (int i = 0; i < 50 && dirs[i].size() > 0; ++i)
{
cout << dirs[i] << '\n';
}
}
dirs is a pointer to an array, so you can index it like an array. You created an array of 50, so you need to limit yourself to 50 here too. Since you might not have populated the whole array, the .size() check allows the printing loop to stop early.

There is some major confusion in your code, especially between arrays of characters, strings and arrays of strings. Also, there is a memory leak.
Here are my questions / concerns:
Issues / Concerns
The opendir function may be called
with a null parameter. You should
check for a null path before calling
opendir.
Returns NULL after declaring some
variables. IMHO, one should check
parameters before declaring
variables.
How does one know how many valid
entries are in the returned array?
If the size of the array (known only
to the listdir function) changes,
the users of the function are
doomed.
Is the type of pent->d_name the
same as string *?
The address of the directory name,
pent->d_name, is copied into the
results array, but not the content
of the directory name. The OS may
reuse this location without telling
you; so copying the address of the
text is not a good idea.
The main function does not delete
the memory allocated for the
results. This is known as a memory
leak.
Suggestions / Fixes
Use std::string within the
function. This takes care of
allocating memory for text.
Use std::vector<string> for the
results. This takes care of knowing
the quantity of directories and no
need to dynamically allocate or
deallocate memory.
Create a std::string from the
pent->d_name and use push_back
to append the string to the results.

In C++, dereferencing a pointer is achieved using the * operator, just like in 'C'.
However, there are a number of problems with your code, which I have addressed here because I was bored...
#include <string>
#include <iostream>
#include <list>
#include <dirent.h>
typedef std::list<std::string> dir_list;
bool listdir(const std::string& path, dir_list& result)
{
bool retval = true;
DIR* pdir = opendir(path.c_str());
if (pdir == NULL)
{
std::cerr << "ERROR! pdir could not be initialised correctly" << std::endl;;
retval = false;
}
else
{
for (dirent* pent = readdir(pdir); pent != NULL; pent = readdir(pdir))
{
if (pent == NULL && result.empty())
{
std::cerr << "ERROR! pent could not be initialised correctly" << std::endl;
retval = false;
}
if (result.size() < 50)
{// *really* limit to 50!
result.push_back(pent->d_name);
}
}
closedir(pdir);
}
return retval;
}
int main()
{
dir_list dirs;
if (listdir("C:/", dirs))
{
for (dir_list::const_iterator iter(dirs.begin()), end(dirs.end()); iter != end; ++iter)
{
std::cout << *iter << std::endl;
}
}
}
Since you're using C++, STL, its string and container classes will save you a World of pointer pain!

your while statement looks strange, it looks like it's expecting dirs[i] to be a pointer, but I believe dirs[i] will be a non-pointer type. Maybe change it to (assuming string is std::string):
while(i < 50 && dirs[i].length() > 0)
{
cout<<dirs[i]<<'\n';
++i;
}

Related

How do I reverse a c string without the use of strlen?

I'm trying to implement a void function that takes a c string as its only parameter and reverses it and prints it. Below is my attempt at a solution however I'm not sure how to go about this problem.
void printBackwards(char forward[]) {
int i = 0;
char backwards[];
while (forward[i] != '\0') {
backwards[i] = forward[-i - 1];
i++;
}
cout << backwards;
}
Under such a condition, I guess you are expected to use recursion.
void printBackwards(char forward[]) {
if (!forward[0])
return;
printBackwards(forward + 1);
cout << forward[0];
}
Not being able to use strlen, we'll calculate it ourselves using a simple for loop. Then dynamically allocate a suitable buffer (add one character for the null terminating char, and I "cheated" by using calloc to zero the memory so I don't have to remember to set the null terminator. Then anoher simple loop to copy the original into the result in reverse.
#include <stdlib.h>
#include <stdio.h>
char *rev(char *s) {
size_t i;
char *s2 = s; // A pointer to the beginning as our first loop modifies s
for (i = 0; *s; s++, i++);
char *result = calloc(0, i + 1);
if (!result) return NULL; // In case calloc didn't allocate the requested memory.
for (size_t j = 0; j < i; j++)
result[j] = s2[i - j - 1];
return result;
}
Assuming you want to reverse the string rather than just printing it in reverse order, you first need to find the last character location (actually the position of the null terminator). Pseudo-code below (since this is an educational assignment):
define null_addr(pointer):
while character at pointer is not null terminator:
increment pointer
return pointer
Then you can use that inside a loop where you swap the two characters and move the pointers toward the center of the string. As soon as the pointers become equal or pass each other the string is reversed:
define reverse(left_pointer):
set right_pointer to null_addr(left_pointer)
while right_pointer > left_pointer plus one:
decrement right_pointer
swap character at left_pointer with character at right_pointer
increment left_pointer
Alternatively (and this appears to be the case since your attempt doesn't actually reverse the original string), if you need to print the string in reverse order without modifying it, you still find the last character. Then you run backwards through the string printing each character until you reach the first. That can be done with something like:
define print_reverse(pointer):
set right_pointer to null_addr(pointer)
while right_pointer > pointer:
decrement right_pointer
print character at right_pointer
That's probably better than creating a new string to hold the reverse of the original, and then printing that reverse.
One thing you should keep in mind. This very much appears to be a C-centric question, not a C++ one (it's using C strings rather than C++ strings, and uses C header files). If that's the case, you should probably avoid things like cout.
By using abstractions, like , your code will be much better at communication WHAT it is doing instead of HOW it is doing it.
#include <iostream>
#include <string>
#include <ranges>
int main()
{
std::string hello{ "!dlrow olleH" };
for (const char c : hello | std::views::reverse)
{
std::cout << c;
}
return 0;
}
Use a template
#include <iostream>
template<int N, int I=2>
void printBackwards(char (&forward)[N]) {
std::cout << forward[N-I];
if constexpr (I<N) printBackwards<N, I+1>(forward);
}
int main() {
char test[] = "elephant";
printBackwards(test);
}
While there seems to be several working answers, I thought I'd throw my hat in the stack (pun intended) since none of them take advantage of a FILO data structure (except #273K's answer, which uses a stack implicitly instead of explicitly).
What I would do is simply push everything onto a stack and then print the stack:
#include <stack>
#include <iostream>
void printBackwards(char forward[]) {
// Create a stack to hold our reversed string
std::stack<char> stk;
// Iterate through the string until we hit the null terminator
int i = 0;
while (forward[i] != '\0'){
stk.push(forward[i]);
++i;
}
// Iterate through the stack and print each character as we pop() it
while (stk.size() > 0){
std::cout << stk.top();
stk.pop();
}
// Don't forget the newline (assuming output lines should be separated)
std::cout << '\n';
}
int main(int argc, char* argv[]){
char s[] = "This is a string";
printBackwards(s);
return 0;
}
Hi guys as promised I have come back to add my own answer. This is my own way using array subscripts and using what I currently know.
#include <iostream>
using namespace std;
void printBackwards(char[]);
int main()
{
char word[] = "apples";
printBackwards(word);
return 0;
}
void printBackwards(char word[]) {
char* temp = word;
int count = 0;
while (*temp++ != '\0') {
count++;
}
for (int i = count - 1; i >= 0; i--) {
cout << word[i];
}
}
You can make a fixed-size buffer and create new ones if needed. Fill it reverse by moving the string offset back with every inserted character. Chars exceeding the buffer are returned to be processed later, so you can make a list of such buffers:
template<int SIZE>
struct ReversedCStr
{
static_assert(SIZE > 10); // just some minimal size treshold
// constexpr
ReversedCStr(char const* c_str, char const** tail = nullptr) noexcept
{
for(buffer[offset] = '\0'; *c_str != '\0';)
{
buffer[--offset] = *c_str++;
if(offset == 0) break;
}
if(tail) *tail = c_str;
}
//constexpr
char const* c_str() const noexcept { return buffer.data()+offset;};
private:
size_t offset = SIZE -1;
std::array<char,SIZE> buffer;
};
The tag is 'C++' so I assume you use C++ not C. The following code is C++11 so it should fit in every modern project. I posted the working example on godbolt.org.
It doesn't allocate memory, and is completely exception-free. The maximum memory wasted is {buffer_size + sizeof(char*)*number_of_chunks}, and can be easily turned into a list of reversed chunks like this:
char const* tail;
std::vector<ReversedCStr<11>> vec;
for(vec.emplace_back(str,&tail); *tail != '\0';)
vec.emplace_back(tail,&tail);

C++ - Unexpected output from std::string

I'm writing a function which returns a string. But something weird happened. The output of the result string was printed as an unexpected thing from the console.
It becomes Chinese or something else or EMPTY STRING depending on the machine (TESTED). But this only happens when the input string is super long. It works normally for strings with a smaller size.
Is there a better way to append char to a string? It's because I suspect the problem was caused by the way how I added chars to the end of a string.
From Console
From Debugger
main.cpp
#include <iostream>
#include <string>
#include <algorithm>
using namespace std;
bool checkPalindrome(string s)
{
return (s == std::string(s.rbegin(), s.rend()));
}
string longestPalindrome(string s)
{
if (s.size() > 1000 || s.empty())
return "";
string result = "";
string sub = "";
char* ptr = &sub[0];
for (int i = 0; i < s.length(); ++i) {
sub += s[i];
while (true) {
string temp = ptr;
if (checkPalindrome(temp)) {
ptr = &sub[0];
if (temp.length() > result.length()) {
result = temp;
break;
}
break;
}
else {
ptr++;
}
if (ptr == &sub[sub.length()-1]) {
ptr = &sub[0];
break;
}
}
}
std::cout << "end of function" << std::endl;
return result;
}
int main()
{
string output = longestPalindrome("babaddtattarrattatddetartrateedredividerb");
std::cout << output << std::endl;
return 0;
}
The expression char* ptr = &sub[0]; gives you a pointer to a char. However, when you perform sub += s[i]; you may require the string's internal storage to grow to accommodate the new character. If you keep adding to it, eventually it will happen. This will invalidate ptr and render it unusable until it's reassigned.
When such a reallocation does happen a larger buffer is allocated, the previous value is moved from the shorter buffer to the larger buffer then the shorter buffer is destroyed to be replaced by the larger one. But ptr still points to where the previous shorter buffer's data used to be. It's now pointing to an element of a destroyed object. When you then do string temp = ptr; you risk initializing a string from an invalidated pointer which is undefined behavior.
One relatively simple solution woult be to stick with indices instead of pointers. Indices by their nature don't get invalidated as long as they are within the bounds of the string's size. Another possible solution might be to use reserve to pre-allocate a large enough capacity that it never has to reallocate.

Error in output of function to search for files in directory C++

I have built this function to search for files in a certain directory.
Everything works well, but when I am printing the vectors, the output of vectors is wrong. Inside the while loop, the vectors are filled with the right data, but when I output them outside of the while loop (in the next for loop), the data is not the same anymore.
I cannot figure out what is wrong. Any idea?
void search(const fs::path& directory, const fs::path& file_name, string input, string &compFileName, string &Fpath)
{
string t;
auto d = fs::recursive_directory_iterator(directory);
auto found = std::find_if(d, end(d), [&](const auto & dir_entry)
{
Fpath = dir_entry.path().string();
t = dir_entry.path().filename().string();
return t.find(file_name.string()) != std::string::npos;
}
);
if (found == end(d))
cout << "File was not found" << endl;
else
{
int count = 0;
vector<LPCSTR> pfilesFound; //path
vector<LPCSTR> nfilesFound; //name
while (found != end(d))
{
count++;
LPCSTR cFpath = Fpath.c_str();//get path and insert it in the shellexecute function
LPCSTR ct = t.c_str();
pfilesFound.push_back(cFpath);
nfilesFound.push_back(ct);
d++;
found = std::find_if(d, end(d), [&](const auto & dir_entry)
{
Fpath = dir_entry.path().string();
t = dir_entry.path().filename().string();
return t.find(file_name.string()) != std::string::npos;
});
}
cout << "We found the following items" << endl;
int count2 = 0;
for (std::vector<LPCSTR>::const_iterator i = nfilesFound.begin(); i != nfilesFound.end(); ++i)
{
count2++;
std::cout << count2 << "- " << *i << endl;
}
}
}
You are storing pointers to string buffer that get invalidated every time you change the source string. So both vectors are basically filled with dangling pointers. You need to store strings instead like this: vector<::std::string> pfilesFound;.
LPCSTR cFpath = Fpath.c_str();
This is not creating a copy of FPath, it just gives the pointer to raw memory where the actual string is stored.
Fpath = dir_entry.path().string();
Now Fpath has different value, and so does the internal raw memory and the pointer that you stored is pointing to different value now.
When t.find(file_name.string()) != std::string::npos; is hit, Fpath is modified here as well which would be referred by all stored pointers in vector.

Thread 1: EXC_BAD_ACCESS (code=1, address=0x0) xCode C++

So I am trying to open a file and parse a file in C++ (using initializeSimulation(). I can open the file successfully, but when I try to parse the file and store specific values from the file into my variables, I get the THREAD 1 error. The DataParser.cpp file is one I am required to use and cannot modify, I am to simply call it. I think the issue is with my function call to getData() (using pointers) but I cannot seem to figure out why. The way my variables are set up now is working weirdly for some reason, until i try to store m_sMaxVal. When I set them all up as pointers, I was getting a different error. I'm new to C++ but please let me know if you need any more information, and thanks in advance.
Simulation.cpp
#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#include "Simulation.hpp"
#include "dDataParser.h"
Simulation::Simulation() {}
Simulation::~Simulation(){}
void Simulation::initializeSimulation() {
char *fileName;
char *m_sType;
char m_sMaterial;
int m_iID;
char m_sUnits;
double m_sMinVal;
double *m_sMaxVal;
cout << "Enter the name of the data file:" << endl;
cin >> fileName ;
//cout << fileName << "\n" ;
parser = new EnviroSimDataParser(fileName);
parser ->getSensorData(m_sType, &m_sMaterial, &m_iID, &m_sUnits, &m_sMinVal, m_sMaxVal);
}
DataParser.cpp
EnviroSimDataParser::EnviroSimDataParser(char *fileName)
{
char line[128];
strcpy(m_sFileName, fileName);
inFile = new ifstream();
inFile->open(m_sFileName, fstream::in); // Open the data file
m_iNumSensors = 0; // Number of sensors in the file
m_iNumDisplays = 0; // Number of display devices in the file
m_iNextSensor = 0; // Next sensor number to read
m_iNextDisplay = 0; // Next display number to read
if(inFile->is_open())
{
cout << "file opened successfully1 \n";
// Count the number of sensors
while(getNextLine(line, 127))
{
if(strcmp(line, "<SENSOR>") == 0)
m_iNumSensors++;
if(strcmp(line, "<DISPLAY_DEVICE>") == 0)
m_iNumDisplays++;
}
inFile->close();
cout << "file closed successfully1 \n";
}
else
{
cout << "Failed to open file. Application terminated...\n";
exit(0);
}
}
//-----
bool DataParser::getData(char *type, char *material, int *ID,
char *units, double *minVal, double *maxVal)
{
int sNum = 0;
char line[128];
// See if we have read all sensors
if(m_iNextSensor >= m_iNumSensors) return false;
// Reopen the file
inFile = new ifstream();
inFile->open(m_sFileName, fstream::in);
if(inFile->is_open())
{
// Read to the the current sensor count
while(getNextLine(line, 127))
{
if(strcmp(line, "<SENSOR>") == 0) // Got one
{
if(sNum == m_iNextSensor)
{
// Get data on this one
while(getNextLine(line, 127))
{
// Get the type
if(strcmp(line, "<TYPE>") == 0)
{
if(getNextLine(line, 127))
{
strcpy(type, line); // Set the type
}
else
return false; // Oops!
}
else if(strcmp(line, "<MATERIAL>") == 0)
{
if(getNextLine(line, 127))
{
strcpy(material, line); // Set the material
}
else
return false; // Oops!
}
else if(strcmp(line, "<ID>") == 0)
{
if(getNextLine(line, 127))
{
*ID = atoi(line); // Set the ID
}
else
return false; // Oops!
}
else if(strcmp(line, "<UNITS>") == 0)
{
if(getNextLine(line, 127))
{
strcpy(units, line); // Set the units
}
else
return false; // Oops!
}
else if(strcmp(line, "<MINIMUM_VALUE>") == 0)
{
if(getNextLine(line, 127))
{
*minVal = atof(line); // Set the minimum value
}
else
return false; // Oops!
}
else if(strcmp(line, "<MAXIMUM_VALUE>") == 0)
{
if(getNextLine(line, 127))
{
*maxVal = atof(line); // Set the minimum value
}
else
return false; // Oops!
}
else if(strcmp(line, "</SENSOR>") == 0)
{
m_iNextSensor++; // Increment for next sensor
return true; // Got it
}
} // end while
} // end if(sNum == m_iNextSensor)
else
{
sNum++; // Check the next one
}
}
}
inFile->close();
} // end if file open
return false; // If we get here we have got all the sensors
}
edit: the debugger stops at the line of
*maxVal = atof(line); //Set the minimum value
When a function or method has a parameter that is a pointer to a variable, you need to allocate memory for that variable. There are a few ways to do that, but the easiest one is to declare an automatic variable like this:
double someVar = 0.0;
and call the function that requires a pointer like this:
SomeFunctionThatTakesADoublePointer(&someVar);
You could allocate the memory on the heap by doing:
double* someVar = new double;
and call the function like this:
SomeFunctionThatTakesADoublePointer(someVar);
This comes with a couple of requirements, though. First, you need to check that the allocation succeeded by wrapping the function call in a conditional, like this:
if (someVar != nullptr)
{
SomeFunctionThatTakesADoublePointer(someVar);
}
And you have to free the memory when you're done using it, or it will leak:
if (someVar != nullptr)
{
SomeFunctionThatTakesADoublePointer(someVar);
// ... do some calculating with *someVar
delete someVar;
}
Things get trickier with character strings. In C++ you should avoid character strings and use std::string from the standard library. (You'll need to #include <string> to use it.) If you must work with character strings because you're calling a function which you don't control and it requires a string, you need to allocate a string that has enough memory for all the characters and the terminating nul character. You can do that on the stack if you know the length of the string is limited by doing something like:
char someString [ MAX_ALLOWED_STRING_LENGTH + 1 ];
where MAX_ALLOWED_STRING_LENGTH is a constant defined somewhere in your program. Then you can call a function or method which takes a character string like this:
SomeFunctionThatTakeACharacterString(someString);
Again, you could allocate the memory using new [], but you have to ensure it's not nullptr before using it and delete [] it when you're done.

Call a c program from C++ and pass arguments

I have a c++ program, and at some point in my program I need to call a c program and pass some arguments to it.
I am working in linux env.
the file simpsh is a compiled c file in the same dir.
resulting_simpsh_command is a string with data of this type
--creat --trunc --wronly f1 and so far.
When I check the values that I recieve in C program, it shows this instead
void execute_simpsh(string resulting_simpsh_command)
{
pid_t pid = fork();
if(pid == -1)
perror("Fork failed!");
else if(pid ==0)
{
char* args[256];
string simpsh ="./simpsh";
args [0] = (char*) simpsh.c_str();
string temp_option_holder="";
int nextCommand=1;
for(int i=0;i<resulting_simpsh_command.length();i++)
{
if(resulting_simpsh_command[i] !=' ')
{
temp_option_holder += resulting_simpsh_command[i];
}
else
{
cout<<"saving to argument: "<<temp_option_holder<<endl;
args [nextCommand] = (char*) temp_option_holder.c_str();
temp_option_holder="";
nextCommand +=1;
}
}
cout<<"command numbers "<<nextCommand<<endl;
args [nextCommand + 1] = NULL;
if(execvp(args[0],args) == -1)
cout<<"Failed to open simpsh, maybe you didnt compile?"<<endl;
exit(1);
}
else
{
//not important for now
}
}
A lot of the args array will be invalid pointers due to your (mis)use of c_str.
The pointer becomes invalid as soon as the string's buffer is reallocated.
Some of the args pointers could also point to the same thing even though they're valid.
Two options for fixing this (off the top of my head):
Dynamic allocation:
args[nextCommand] = strdup(temp_option_holder.c_str());
which requires deallocation later.
Or, if you can live with limiting the argument length, you can use another array and avoid the memory management (but then you need to worry about overflows instead):
char arg_strings[256][ARGUMENT_LENGTH] = {0};
char* args[256] = {0};
// ...
assert(temp_option_holder.length() < ARGUMENT_LENGTH);
strncpy(arg_strings[nextCommand], temp_option_holder.c_str(), ARGUMENT_LENGTH);
args[nextCommand] = arg_strings[nextCommand];
C++ has so many nice things beyond std::cout. Use them!
The core of this solution is std::replace()ing the spaces in your command line with zero bytes, then taking pointers to the beginning of each (C) string in the command line (conveniently using the zero bytes from the replace() as terminators), pushing those pointers into a vector we will then pass to execve().
#include <string>
#include <vector>
#include <algorithm>
#include <iostream>
// Usually I would recommend using const std::string &
// to avoid the copy, but we are going to do violence
// on the argument so we really *want* a writeable copy.
void execute_simpsh( string cmd )
{
pid_t pid = fork();
if ( pid == -1 )
{
perror("Fork failed!");
}
else if ( pid == 0 )
{
// This will hold our pointers -- and is not limited
// to a maximum amount other than available RAM
std::vector< char * > args( { "./simpsh" } );
if ( ! cmd.empty() )
{
// Tokenize -- std::string may contain null chars!
std::replace( cmd.begin(), cmd.end(), ' ', '\0' );
// std::string is zero-terminated in memory, but that
// zero char is not considered by .find() so we add one,
// making the loop below easier.
cmd.append( '\0' );
size_t i = 0;
// Gather pointers to each argument. find_first_not_of()
// skips leading (and embedded) ex-spaces.
while ( ( i = cmd.find_first_not_of( '\0', i ) ) != std::string::npos )
{
args.push_back( cmd.data() + i );
// find() skips to the end of the current argument.
i = cmd.find( '\0', i );
}
}
std::cout << "Tokens: " << args.size() << "\n";
// args.data() is std::vector's underlying array
// of the pointers we pushed. How convenient...
for ( auto token : args.data() )
{
std::cout << " " << token << "\n";
}
// ...as "array of char *" is exactly what we need.
if ( execvp( args[0], args.data() ) == -1 )
{
std::cout << "Failed to open simpsh" << std::endl;
}
std::exit( 1 );
}
else
{
// ...
}
}
Note that this solution will mercilessly clobber any spaces in your command line, including those in quotation marks, and does not handle newlines, tabs etc. -- then again, your original solution did not behave differently either, so I will leave extending the capabilities to you.