Why doesn't this function with strpbrk() work? - c++

Why does my program crash when I try to put delimiter into vector::operators?I'm trying to make a working calculator and I need to find all the operators of the input string in order. All the necessary libraries were included, and the code worked when I was only using 2 numbers and 1 operator. Is the problem that I used the loop incorrectly? I also have a similar function to find the numbers that works and crashes in the same spots.
vector<char>operators;
int main()
{
string input;
cin >> input;
find_operators(input);
}
void find_operators(string X)
{
char * cX = new char[X.length()];
strcpy(cX, X.c_str());
char * delimiter = strpbrk(cX,"+*-/");
operators.push_back(*delimiter); //this worked
while (delimiter != NULL)
{
delimiter = strpbrk(delimiter+1, "+-*/");
cout << "OK"; //makes it to this point then crashes
operators.push_back(*delimiter); //this doesn't work
}
delete[] cX;
}

You have a couple issues with your code. First:
char * cX = new char[X.length()];
strcpy(cX, X.c_str());
Is incorrect as you are not accounting for the null terminator that you need in cX. length() only returns the number of character in the string and does not count the null terminator. To fix that all you need to do is:
char * cX = new char[X.length() + 1];
strcpy(cX, X.c_str()); // ^^^^ add one to the size
The second issue you have is in:
while (delimiter != NULL)
{
delimiter = strpbrk(delimiter+1, "+-*/");
cout << "OK"; //makes it to this point then crashes
operators.push_back(*delimiter); //this doesn't work
}
You check if delimiter is not null and then you reassign it using strpbrk(). If strpbrk() returns NULL then operators.push_back(*delimiter) is going to fail because you are dereferencing a null pointer. You should be able to change your code to the following to get it to work:
//...
char * delimiter = strpbrk(cX,"+*-/"); // get operator
while (delimiter != NULL) // keep looping while we have an operator
{
operators.push_back(*delimiter); // add the operator to the vector
delimiter = strpbrk(delimiter+1, "+-*/"); // find the next operator
}
//...

Related

concatenate two char arrays into single char array using pointers

I am trying to take a text file with names (ex: john doe) and fin the first name and last name. Then, I want to take these two char arrays and concatenate them together using pointers. The code that is commented out is working code that takes the two char arrays and puts them into a single char array ie concatenating them together. This project requires that I use pointers, and that I use char arrays I am not asking for you to do it for me, but please help me realize what I am doing wrong. Thanks
EDIT: the error I am getting is a seg fault..so Im thining my playerPtr is going out of bounds somewhere??
void readPlayer(char *finName2, player *playerPtr)
{
player *playerHome = playerPtr;
ifstream fin;
char *tempfName= new char[20];
char *templName= new char[20];
char *tempName= new char[20];
char *tempNameHome = tempName;
fin.open(finName2);
if(!fin.good())
{
cout << "Error with player file!" << endl;
}
else
{
fin >> tempfName;
fin >> templName; //prime file
cout << tempfName << templName;
while(fin.good())
{
for(int i =0;i<5;i++)
{
//find the length
//int index =0, length=0;
while(*tempfName != '\0')
//while(tempfName[length] != '\0')
{
tempfName++;
}
strcopy(tempName,tempfName);
//now add space after first name
*tempName = ' ';
tempName++;
//tempfName[length] = ' ';
//tempfName++;
//length++;
while(*templName != '\0')
//while(templName[index] != '\0')
{
templName++;
//tempfName[length] = templName[index];
//length++;
//index++;
}
strcopy(tempName,templName);
//tempName++;
//tempfName[length]='\0';
strcopy((*playerPtr).name,tempName);
playerPtr++;
fin >> tempfName;
fin >> templName;
}
}
}
delete[] tempfName;
delete[] templName;
delete[]tempName;
}
Your tempfName & templName are incremented all the time and they move beyond their allocated memory. you need to reset their position.
Plus I can see that the
fin >> tempfName;
fin >> templName;
is inside the for loop, which means fin.Good is only checked once every 5 times.
Problems that I see (mentioned in comments too):
Incrementing tempFName and tempLName in loops
while(*tempfName != '\0')
{
tempfName++;
}
strcopy(tempName,tempfName);
At the end of the above loop, tempFName points to the end of the string - it points to the terminating null character. strcopy should copy nothing to tempName.
You have the same problem with the loop:
while(*templName != '\0')
{
templName++;
}
strcopy(tempName,templName);
Setting the value of *tempName after the first loop
//now add space after first name
*tempName = ' ';
tempName++;
This will be valid only if tempName points to the end of the copied string after the call to strcopy. If not, you are just setting the value of the first character in tempName to ' '. Incrementing tempName makes sense only of tempName points to the end of the copied string. Otherwise, it points to the second character.
As a result of the above errors, your code is subject to errors caused by out of bound memory access after the first iteration of the for loop. Nothing after that can be relied upon to behave in a reasonable manner.
I suggest the following changes to fix the above errors.
Don't increment the variables tempFName and tempLName at all
You don't need to at all.
Remove the lines:
while(*tempfName != '\0')
{
tempfName++;
}
just use:
strcopy(tempName,tempfName);
Use a temporary pointer to go to the end of tempName
After the first call to strcopy, use:
char* temp = tempName;
while ( *temp != '\0' ) ++temp;
*temp = ' ';
++temp;
*temp = '\0';
Use the temporary pointer for the second strcopy
Remove the lines:
while(*templName != '\0')
{
templName++;
}
Replace the line:
strcopy(tempName,templName);
with
strcopy(temp,tempfName);
Alternate strategy
If you implement your version of strcat, you can simply use:
tempName[0] = '\0';
strcat(tempName, tempFName);
strcat(tempName, " ");
strcat(tempName, tempLName);
That will remove much of the clutter in the for loop.

Incrementing pointers for *char in a while loop

Here is what I have:
char* input = new char [input_max]
char* inputPtr = iput;
I want to use the inputPtr to traverse the input array. However I am not sure what will correctly check whether or not I have reached the end of the string:
while (*inputPtr++)
{
// Some code
}
or
while (*inputPtr != '\0')
{
inputPtr++;
// Some code
}
or a more elegant option?
Assuming input string is null-terminated:
for(char *inputPtr = input; *inputPtr; ++inputPtr)
{
// some code
}
Keep in mind that the example you posted may not give the results you want. In your while loop condition, you're always performing a post-increment. When you're inside the loop, you've already passed the first character. Take this example:
#include <iostream>
using namespace std;
int main()
{
const char *str = "apple\0";
const char *it = str;
while(*it++)
{
cout << *it << '_';
}
}
This outputs:
p_p_l_e__
Notice the missing first character and the extra _ underscore at the end. Check out this related question if you're confused about pre-increment and post-increment operators.
I would do:
inputPtr = input; // init inputPtr always at the last moment.
while (*inputPtr != '\0') { // Assume the string last with \0
// some code
inputPtr++; // After "some code" (instead of what you wrote).
}
Which is equivalent to the for-loop suggested by greatwolf. It's a personal choice.
Be careful, with both of your examples, you are testing the current position and then you increment. Therefore, you are using the next character!
Assuming input isn't null terminated:
char* input = new char [input_max];
for (char* inputPtr = input; inputPtr < input + input_max;
inputPtr++) {
inputPtr[0]++;
}
for the null terminated case:
for (char* inputPtr = input; inputPtr[0]; inputPtr++) {
inputPtr[0]++;
}
but generally this is as good as you can get. Using std::vector, or std::string may enable cleaner and more elegant options though.

creating a string split function in C++ [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Splitting a string in C++
Im trying to create a function that mimics the behavior of the getline() function, with the option to use a delimiter to split the string into tokens.
The function accepts 2 strings (the second is being passed by reference) and a char type for the delimiter. It loops through each character of the first string, copying it to the second string and stops looping when it reaches the delimiter. It returns true if the first string have more characters after the delimiter and false otherwise. The position of the last character is being saved in a static variable.
for some reason the the program is going into an infinite loop and is not executing anything:
const int LINE_SIZE = 160;
bool strSplit(string sFirst, string & sLast, char cDelim) {
static int iCount = 0;
for(int i = iCount; i < LINE_SIZE; i++) {
if(sFirst[i] != cDelim)
sLast[i-iCount] = sFirst[i];
else {
iCount = i+1;
return true;
}
}
return false;
}
The function is used in the following way:
while(strSplit(sLine, sToken, '|')) {
cout << sToken << endl;
}
Why is it going into an infinite loop, and why is it not working?
I should add that i'm interested in a solution without using istringstream, if that's possible.
It is not exactly what you asked for, but have you considered std::istringstream and std::getline?
// UNTESTED
std::istringstream iss(sLine);
while(std::getline(iss, sToken, '|')) {
std::cout << sToken << "\n";
}
EDIT:
Why is it going into an infinite loop, and why is it not working?
We can't know, you didn't provide enough information. Try to create an SSCCE and post that.
I can tell you that the following line is very suspicious:
sLast[i-iCount] = sFirst[i];
This line will result in undefined behavior (including, perhaps, what you have seen) in any of the following conditions:
i >= sFirst.size()
i-iCount >= sLast.size()
i-iCount < 0
It appears to me likely that all of those conditions are true. If the passed-in string is, for example, shorter than 160 lines, or if iCount ever grows to be bigger than the offset of the first delimiter, then you'll get undefined behavior.
LINE_SIZE is probably larger than the number of characters in the string object, so the code runs off the end of the string's storage, and pretty much anything can happen.
Instead of rolling your own, string::find does what you need.
std::string::size_type pos = 0;
std::string::size_type new_pos = sFirst.find('|', pos);
The call to find finds the first occurrence of '|' that's at or after the position 'pos'. If it succeeds, it returns the index of the '|' that it found. If it fails, it returns std::string::npos. Use it in a loop, and after each match, copy the text from [pos, new_pos) into the target string, and update pos to new_pos + 1.
are you sure it's the strSplit() function that doesn't return or is it your caller while loop that's infinite?
Shouldn't your caller loop be something like:
while(strSplit(sLine, sToken, '|')) {
cout << sToken << endl;
cin >> sLine >> endl;
}
-- edit --
if value of sLine is such that it makes strSplit() to return true then the while loop becomes infinite.. so do something to change the value of sLine for each iteration of the loop.. e.g. put in a cin..
Check this out
std::vector<std::string> spliString(const std::string &str,
const std::string &separator)
{
vector<string> ret;
string::size_type strLen = str.length();
char *buff;
char *pch;
buff = new char[strLen + 1];
buff[strLen] = '\0';
std::copy(str.begin(), str.end(), buff);
pch = strtok(buff, separator.c_str());
while(pch != NULL)
{
ret.push_back(string(pch));
pch = strtok(NULL, separator.c_str());
}
delete[] buff;
return ret;
}

Trying to create a program to read a users input then break the array into seperate words are my pointers all valid? [closed]

It's difficult to tell what is being asked here. This question is ambiguous, vague, incomplete, overly broad, or rhetorical and cannot be reasonably answered in its current form. For help clarifying this question so that it can be reopened, visit the help center.
Closed 11 years ago.
char **findwords(char *str);
int main()
{
int test;
char words[100]; //an array of chars to hold the string given by the user
char **word; //pointer to a list of words
int index = 0; //index of the current word we are printing
char c;
cout << "die monster !";
//a loop to place the charecters that the user put in into the array
do {
c = getchar();
words[index] = c;
} while (words[index] != '\n');
word = findwords(words);
while (word[index] != 0) //loop through the list of words until the end of the list
{
printf("%s\n", word[index]); // while the words are going through the list print them out
index ++; //move on to the next word
}
//free it from the list since it was dynamically allocated
free(word);
cin >> test;
return 0;
}
char **findwords(char *str)
{
int size = 20; //original size of the list
char *newword; //pointer to the new word from strok
int index = 0; //our current location in words
char **words = (char **)malloc(sizeof(char *) * (size +1)); //this is the actual list of words
/* Get the initial word, and pass in the original string we want strtok() *
* to work on. Here, we are seperating words based on spaces, commas, *
* periods, and dashes. IE, if they are found, a new word is created. */
newword = strtok(str, " ,.-");
while (newword != 0) //create a loop that goes through the string until it gets to the end
{
if (index == size)
{
//if the string is larger than the array increase the maximum size of the array
size += 10;
//resize the array
char **words = (char **)malloc(sizeof(char *) * (size +1));
}
//asign words to its proper value
words[index] = newword;
//get the next word in the string
newword = strtok(0, " ,.-");
//increment the index to get to the next word
++index;
}
words[index] = 0;
return words;
}
break the array into the individual words then print them out th
do {
c = getchar();
words[index] = c;
} while (words[index] != '\n');
you should also add '\0' at the end of your string (after the loop) in "words" array
You are not incrementing index this way you save only the last c
you should do while(word[index] != '\0') not while(word[index] != 0 ('\0' indicates end of line no 0)
while (word[index] != 0) //loop through the list of words until the end of the list
{
printf("%s\n", word[index]); // while the words are going through the list print them out
index ++; //move on to the next word
}
I think there is a bug memory leakage because you first allocate
char **words = (char **)malloc(sizeof(char *) * (size +1)); //when declaring
when declaring the variable, and after that you again allocate the same **words in the loop body:
char **words = (char **)malloc(sizeof(char *) * (size +1)); // in the while loop
The above line in the while loop with which you allocate the space to store the string should be (1)
//in the while loop should be
char *words[index] = (char *)malloc(sizeof(char ) * (size +1));
strcpy (words[index], str);
Or simply (2)
words[index] = str;
Because the str already points to a valid memory location which you assign to the array of pointers.
In the (1) method above you are allocating a block of memory of size+1 of type char and copying the string in str into words[index] with strcpy. For this you require to reserve a memory location into words[index] first and then perform the copy. If this is the case the the memory freeing is not at simple as free (word) instead each of the allocated block will need to be manually removed.
for (index = 0; words[index] != 0; index++)
{
free (words[index];
}
free (words);
In the (2) solution is in my opinion not a good one, because you have passed a pointer to a string and assign that pointer value to store the string. So both the str and the words[index] point to the same location. Now after the function returns if anybody frees str (if it was dynamically allocated) then the words[index] reference will become illegal.
EDIT:
Also you need to use
gets (words); or in using c++ cin >> words; or use getline, or simply increment the index counter in your code, and assign a null at the end to terminate the string.
in main function. You do not increment the index counter so all the characters are assigned in the same location.
I think everybody is trying to do it the hard way.
The std streams already break the input into words using the >> operator. We just need to be more careful on how we define a word. To do this you just need to define an ctype facet that defines space correctly (for the context) and then imbue the stream with it.
#include <locale>
#include <string>
#include <sstream>
#include <iostream>
// This is my facet that will treat the ,.- as space characters and thus ignore them.
class WordSplitterFacet: public std::ctype<char>
{
public:
typedef std::ctype<char> base;
typedef base::char_type char_type;
WordSplitterFacet(std::locale const& l)
: base(table)
{
std::ctype<char> const& defaultCType = std::use_facet<std::ctype<char> >(l);
// Copy the default value from the provided locale
static char data[256];
for(int loop = 0;loop < 256;++loop) { data[loop] = loop;}
defaultCType.is(data, data+256, table);
// Modifications to default to include extra space types.
table[','] |= base::space;
table['.'] |= base::space;
table['-'] |= base::space;
}
private:
base::mask table[256];
};
Now the code looks very simple:
int main()
{
// Create the facet.
std::ctype<char>* wordSplitter(new WordSplitterFacet(std::locale()));
// Here I am using a string stream.
// But any stream can be used. Note you must imbue a stream before it is used.
// Otherwise the imbue() will silently fail.
std::stringstream teststr;
teststr.imbue(std::locale(std::locale(), wordSplitter));
// Now that it is imbued we can use it.
// If this was a file stream then you could open it here.
teststr << "This, stri,plop";
// Now use the stream normally
std::string word;
while(teststr >> word)
{
std::cout << "W(" << word << ")\n";
}
}
Testing:
> ./a.out
W(This)
W(stri)
W(plop)
With a correctly imbues stream we can use the old trick of copying from a stream into a vector:
std::copy(std::istream_iterator<std::string>(teststr),
std::istream_iterator<std::string>(),
std::back_inserter(data)
);
Lots of issues:
In your first loop you are forgetting to increment index after each read character.
Also, if you have more than 100 characters, your program will likely crash.
getchar returns an "int". Not a char. Very important - especially if you input is redirected or piped in.
Try this instead:
int tmp;
tmp = getchar();
while ((index < 99) && (tmp >= 0) && (tmp != '\n'))
{
word[index] = (char)tmp;
tmp = getchar();
index++;
}
word[index] = 0; /* make life easier - null terminate your string */
Your "findwords" function scares the hell out of me. You haven't don't have enough points on S.O. for me to elaborate on the issues here. In any case
I'm tempted to open with some lame crack about the '80s calling and wanting their obsolete "C++ as a better C" code back, but I'll try to restrain myself and just give at least some idea of how you might consider doing something like this:
std::string line;
// read a line of input from the user:
std::getline(line, std::cin);
// break it up into words:
std::istringstream buffer(line);
std::vector<std::string> words((std::istream_iterator<std::string>(buffer)),
std::istream_iterator<std::string>());
// print out the words, one per line:
std::copy(words.begin(), words.end(),
std::ostream_iterator(std::cout, "\n"));

Access Violation With Pointers? - C++

I've written a simple string tokenizing program using pointers for a recent school project. However, I'm having trouble with my StringTokenizer::Next() method, which, when called, is supposed to return a pointer to the first letter of the next word in the char array. I get no compile-time errors, but I get a runtime error which states:
Unhandled exception at 0x012c240f in Project 5.exe: 0xC0000005: Access violation reading location 0x002b0000.
The program currently tokenizes the char array, but then stops and this error pops up. I have a feeling it has to do with the NULL checking I'm doing in my Next() method.
So how can I fix this?
Also, if you notice anything I could do more efficiently or with better practice, please let me know.
Thanks!!
StringTokenizer.h:
#pragma once
class StringTokenizer
{
public:
StringTokenizer(void);
StringTokenizer(char* const, char);
char* Next(void);
~StringTokenizer(void);
private:
char* pStart;
char* pNextWord;
char delim;
};
StringTokenizer.cpp:
#include "stringtokenizer.h"
#include <iostream>
using namespace std;
StringTokenizer::StringTokenizer(void)
{
pStart = NULL;
pNextWord = NULL;
delim = 'n';
}
StringTokenizer::StringTokenizer(char* const pArray, char d)
{
pStart = pArray;
delim = d;
}
char* StringTokenizer::Next(void)
{
pNextWord = pStart;
if (pStart == NULL) { return NULL; }
while (*pStart != delim) // access violation error here
{
pStart++;
}
if (pStart == NULL) { return NULL; }
*pStart = '\0'; // sometimes the access violation error occurs here
pStart++;
return pNextWord;
}
StringTokenizer::~StringTokenizer(void)
{
delete pStart;
delete pNextWord;
}
Main.cpp:
// The PrintHeader function prints out my
// student info in header form
// Parameters - none
// Pre-conditions - none
// Post-conditions - none
// Returns - void
void PrintHeader();
int main ( )
{
const int CHAR_ARRAY_CAPACITY = 128;
const int CHAR_ARRAY_CAPCITY_MINUS_ONE = 127;
// create a place to hold the user's input
// and a char pointer to use with the next( ) function
char words[CHAR_ARRAY_CAPACITY];
char* nextWord;
PrintHeader();
cout << "\nString Tokenizer Project";
cout << "\nyour name\n\n";
cout << "Enter in a short string of words:";
cin.getline ( words, CHAR_ARRAY_CAPCITY_MINUS_ONE );
// create a tokenizer object, pass in the char array
// and a space character for the delimiter
StringTokenizer tk( words, ' ' );
// this loop will display the tokens
while ( ( nextWord = tk.Next ( ) ) != NULL )
{
cout << nextWord << endl;
}
system("PAUSE");
return 0;
}
EDIT:
Okay, I've got the program working fine now, as long as the delimiter is a space. But if I pass it a `/' as a delim, it comes up with the access violation error again. Any ideas?
Function that works with spaces:
char* StringTokenizer::Next(void)
{
pNextWord = pStart;
if (*pStart == '\0') { return NULL; }
while (*pStart != delim)
{
pStart++;
}
if (*pStart = '\0') { return NULL; }
*pStart = '\0';
pStart++;
return pNextWord;
}
An access violation (or "segmentation fault" on some OSes) means you've attempted to read or write to a position in memory that you never allocated.
Consider the while loop in Next():
while (*pStart != delim) // access violation error here
{
pStart++;
}
Let's say the string is "blah\0". Note that I've included the terminating null. Now, ask yourself: how does that loop know to stop when it reaches the end of the string?
More importantly: what happens with *pStart if the loop fails to stop at the end of the string?
This answer is provided based on the edited question and various comments/observations in other answers...
First, what are the possible states for pStart when Next() is called?
pStart is NULL (default constructor or otherwise set to NULL)
*pStart is '\0' (empty string at end of string)
*pStart is delim (empty string at an adjacent delimiter)
*pStart is anything else (non-empty-string token)
At this point we only need to worry about the first option. Therefore, I would use the original "if" check here:
if (pStart == NULL) { return NULL; }
Why don't we need to worry about cases 2 or 3 yet? You probably want to treat adjacent delimiters as having an empty-string token between them, including at the start and end of the string. (If not, adjust to taste.) The while loop will handle that for us, provided you also add the '\0' check (needed regardless):
while (*pStart != delim && *pStart != '\0')
After the while loop is where you need to be careful. What are the possible states now?
*pStart is '\0' (token ends at end of string)
*pStart is delim (token ends at next delimiter)
Note that pStart itself cannot be NULL here.
You need to return pNextWord (current token) for both of these conditions so you don't drop the last token (i.e., when *pStart is '\0'). The code handles case 2 correctly but not case 1 (original code dangerously incremented pStart past '\0', the new code returned NULL). In addition, it is important to reset pStart for case 1 correctly, such that the next call to Next() returns NULL. I'll leave the exact code as an exercise to reader, since it is homework after all ;)
It's a good exercise to outline the possible states of data throughout a function in order to determine the correct action for each state, similar to formally defining base cases vs. recursive cases for recursive functions.
Finally, I noticed you have delete calls on both pStart and pNextWord in your destructor. First, to delete arrays, you need to use delete [] ptr; (i.e., array delete). Second, you wouldn't delete both pStart and pNextWord because pNextWord points into the pStart array. Third, by the end, pStart no longer points to the start of the memory, so you would need a separate member to store the original start for the delete [] call. Lastly, these arrays are allocated on the stack and not the heap (i.e., using char var[], not char* var = new char[]), and therefore they shouldn't be deleted. Therefore, you should simply use an empty destructor.
Another useful tip is to count the number of new and delete calls; there should be the same number of each. In this case, you have zero new calls, and two delete calls, indicating a serious issue. If it was the opposite, it would indicate a memory leak.
Inside ::Next you need to check for the delim character, but you also need to check for the end of the buffer, (which I'm guessing is indicated by a \0).
while (*pStart != '\0' && *pStart != delim) // access violation error here
{
pStart++;
}
And I think that these tests in ::Next
if (pStart == NULL) { return NULL; }
Should be this instead.
if (*pStart == '\0') { return NULL; }
That is, you should be checking for a Nul character, not a null pointer. Its not clear whether you intend for these tests to detect an uninitialized pStart pointer, or the end of the buffer.
An access violation usually means a bad pointer.
In this case, the most likely cause is running out of string before you find your delimiter.