I saw that a part of my code did not work as expected. It does not go into the if statement and give an error while it should. I tried to examine the code and found something very odd. When I add cout << i in the for loop to check, it starts to work well. Can anyone explain what is the problem with it? Note: airports is a vector of Airport objects. Airport::getName() returns its name as string.
string name = "smth";
//this is the loop with unexpected behaviour
for (int i = 0; i < airports.size(); i++)
{
//when the following line is taken outside the comment, it works well:
//cout << i;
if (isEqualNoCaseSense(name, airports[i].getName()))
{
cout << "Could not create airport " << name << ". It already exists." << endl;
return;
}
}
The function I used to compare strings without case sensitivity:
bool isEqualNoCaseSense(string str1, string str2)
{
if (str1.size() != str2.size())
return false;
char *lowerStr1 = new char[str1.size()];
char *lowerStr2 = new char[str2.size()];
for (int i = 0; i < str1.size(); i++)
{
lowerStr1[i] = tolower(str1[i]);
lowerStr2[i] = tolower(str2[i]);
}
if (strcmp(lowerStr1, lowerStr2) == 0)
{
delete[] lowerStr1;
delete[] lowerStr2;
return true;
}
else
{
delete[] lowerStr1;
delete[] lowerStr2;
return false;
}
}
EDIT: Correcting the compare function fixes it, but I still wonder why cout << i fixed it
The problem is that you haven't terminated your strings, but if you just write a sensible function it will work correctly:
bool isEqualNoCaseSense(const string& str1, const string& str2)
{
if (str1.size() != str2.size())
return false;
for (int i = 0; i < str1.size(); ++i)
if (tolower((unsigned char)str1[i]) != tolower((unsigned char)str2[i]))
return false;
return true;
}
No unnecessary allocation, and only loops through the strings up to the first mismatch.
EDIT: Correcting the compare function fixes it, but I still wonder why cout << i fixed it
Because the program had undefined behaviour, so anything can happen. Adding random, unrelated code might change the memory allocation patterns of the code, or cause the compiler to optimise it slightly differently. You'll go mad trying to reason about some kinds of undefined behaviour. Just avoid it.
strcmp expects NULL terminated strings. So you needed to reserve one more character for the lowerStr1, lowerStr2 arrays and set the last character of each array to NULL. Failing to do so, created undefined bahaviour to your program.
But you could bypass all these issues if you use strncmp function passing str1.size() as the third argument, since you already check if the two strings have equal size:
if (strncmp(lowerStr1, lowerStr2, str1.size()) == 0)
Related
I was working on a system that split a sentence to a 2D pointer.
I don't wanna use any kind of library or another ways like string, because I want to practice pointers and learn them.
char** sscanf(char* hstring)
{
int count = 0;
char* current = hstring;
while (*current)
{
if (*current == ' ')
{
count++;
}
while (*current == ' ')
{
current++;
}
if (*current)
break;
current++;
}
char** result = new char*[count];
current = hstring;
char* nstr = new char;
int c = 0, i = 0;
while (*current)
{
if (!*current) break;
cout << "t1";
if (*current == ' ')
{
*(++result) = nstr;
nstr = nullptr;
nstr = new char;
}
cout << "t2";
while (*current != '/0' && *current == ' ')
{
current++;
}
cout << "t3";
while (*current != '/0' && *current != ' ')
{
if (!*current) break;
*(++nstr) = *current;
current++;
}
cout << "t4";
*nstr = '/0';
cout << "t5";
}
return result;
}
But it's very strange, sometimes redirects me to
static size_t __CLRCALL_OR_CDECL length(_In_z_ const _Elem * const _First) _NOEXCEPT // strengthened
{ // find length of null-terminated string
return (_CSTD strlen(_First));
}
with error: Acces Violation, other times, choose a random line and call it Acces Breakout(sorry if I spelled wrong)
What I want from you is not to repair my code simply, I want some explanations, because I want to learn this stuff.
First, some advice
I understand that you are making this function as an exercise, but being C++ I'd like to warn you that things like new char*[count] are bad practices and that's why std::vector or std::array were created.
You seem confused about how dynamic allocation works. The statement char* nstr = new char; will create just one byte (char) in heap memory, and nothing is guaranteed to be adjacent to it. This means that ++nstr is a "invalid" operation, I mean, it's making the nstr point to the next byte after the allocated one, which can be some random invalid location.
There is a whole lot of other dangerous operations in your code, like calling new several times (which reserves memory) and not calling delete on them when you no longer use the reserved memory (aka. memory leaks). Having said that, I strongly encourage you to study this subject, for example starting with the ISO C++ FAQ on memory management.
Also, before digging into pointers and dynamic allocation, you should be more confortable with statements and flow control. I say this because I see some clear misunderstandings, like:
while (*current) {
if (!*current) break;
...
}
The check inside the if statement will certainly be false, because the while check is executed just before it and guarantees that the opposite condition is true. This means that this if is never evaluated to true and it's completely useless.
Another remark is: don't name your functions the same as standard libraries ones. sscanf is already taken, choose another (and more meaningful) one. This will save you some headaches in the future; be used to name your own functions properly.
A guided solution
I'm in a good mood, so I'll go through some steps here. Anyway, if someone is looking for an optimized and ready to go solution, see Split a String in C++.
0. Define the steps
Reading your code, I could guess some of your desired steps:
char** split_string(char* sentence)
{
// Count the number of words in the sentence
// Allocate memory for the answer (a 2D buffer)
// Write each word in the output
}
Instead of trying to get them right all at once, why don't you try one by one? (Notice the function's and parameter's names, clearer in my opinion).
1. Count the words
You could start with a simple main(), testing your solution. Here is mine (sorry, I couldn't just adapt yours). For those who are optimization-addicted, this is not an optimized solution, but a simple snippet for the OP.
// I'll be using this header and namespace on the next snippets too.
#include <iostream>
using namespace std;
int main()
{
char sentence[] = " This is my sentence ";
int n_words = 0;
char *p = sentence;
bool was_space = true; // see logic below
// Reading the whole sentence
while (*p) {
// Check if it's a space and advance pointer
bool is_space = (*p++ == ' ');
if (was_space && !is_space)
n_words++; // count as word a 'rising edge'
was_space = is_space;
}
cout << n_words;
}
Test it, make sure you understand why it works. Now, you can move to the next step.
2. Allocate the buffer
Well, you want to allocate one buffer for each word, so we need to know the size of each one of them (I'll not discuss whether or not this is a good approach to the split sentence problem..). This was not calculated on the previous step, so we might do it now.
int main()
{
char sentence[] = " This is my sentence ";
///// Count the number of words in the sentence
int n_words = 0;
char *p = sentence;
bool was_space = true; // see logic below
// Reading the whole sentence
while (*p) {
// Check if it's a space and advance pointer
bool is_space = (*p++ == ' ');
if (was_space && !is_space)
n_words++; // count as word a 'rising edge'
was_space = is_space;
}
///// Allocate memory for the answer (a 2D buffer)
// This is more like C than C++, but you asked for it
char **words = new char*[n_words];
char *ini = sentence; // the initial char of each word
for (int i = 0; i < n_words; ++i) {
while (*ini == ' ') ini++; // search next non-space char
char *end = ini + 1; // pointer to the end of the word
while (*end && *end != ' ') end++; // search for \0 or space
int word_size = end - ini; // find out the word size by address offset
ini = end; // next for-loop iteration starts
// at the next word
words[i] = new char[word_size]; // a whole buffer for one word
cout << i << ": " << word_size << endl; // debugging
}
// Deleting it all, one buffer at a time
for (int i = 0; i < n_words; ++i) {
delete[] words[i]; // delete[] is the syntax to delete an array
}
}
Notice that I'm deleting the allocated buffers inside the main(). When you move this logic to your function, this deallocation will be performed by the caller of the function, since it will probably use the buffers before deleting them.
3. Assigning each word to its buffer
I think you got the idea. Assign the words and move the logic to the separated function. Update your question with a Minimal, Complete, and Verifiable example if you still have troubles.
I know this is a Q&A forum, but I think this is already a healthy answer to the OP and to others that may pass here. Let me know if I should answer differently.
I just started learning about pointers so I'd thought I'd share what I'm trying to do. Of a character array (let's call it c and it's equal to "Hello"), I'm trying to return the memory location of a certain element. Say the memory of location of 'l'. Here's what I have so far:
#include <iostream>
using namespace std;
char* str_char(char* c_ptr, char c);
int main()
{
char *c = "Hello";
cout << str_char(c, 'l') << endl;
return 0;
}
char* str_char(char* c_ptr, char c)
{
for (int i = 0; i < sizeof(c_ptr); i++)
{
if (*(c_ptr + i) == c)
{
return (c_ptr + i);
break;
}
}
}
After I use the function, it outputs "llo".
You are on the right track. However, there are a few things that are not right.
Use of sizeof(c_ptr) is not right. It works for your case due to happy coincidence.
sizeof(c_ptr) is equal to sizeof(char*). It is not equal to the size of the array from the calling function.
There is a missing return statement at end of the function -- the case where c is not found in c_ptr.
There is no need of the break; after the return;.
Also, you can simplify the function a little bit.
Here's an updated version:
char* str_char(char* c_ptr, char c)
{
for (char* cp = c_ptr; *cp != '\0'; ++cp )
{
if (*cp == c)
{
return cp;
}
}
return nullptr;
}
The earlier answer has covered the bugs, so I'll just answer the actual question...
The function does return the location of an array element.
The << operator treats every char* as a pointer to a zero-terminated string, and outputting the result of str_char works exactly like outputting c; it prints every character after that location until it encounters a zero.
If you want to output the value of the location itself, you need to cast it to a different type:
cout << static_cast<void*>(str_char(c, 'l')) << endl;
as << has an overload for void* that outputs the address itself.
R Sahu gave you answer that is slightly modified version of your function . That is legal code, but that is basicly a C code, except use of nullptr. Returning nullptr is non-canon and if nupllptr will be returned, what << operator would do? That's undefined behavior.
it is often agreed pattern to leave iterator (pointer) pointing at end of line (at the terminating zero) to avoid crash you may cause by returning nullptr.
char* str_char(char* c_ptr, char c)
{
char* cp = c_ptr;
for (; *cp != '\0'; ++cp )
{
if (*cp == c)
{
return cp;
}
}
return cp;
// if cp was declared inside scope , we can't retirn it's value
}
Normally you do not need to write own string functions.. bth C library and C++ got them already covered. C++ got set of abstract algorithmic functions, in your case a strchr, find or find_if is prudent:
#include <algorithm>
#include <iostream>
using std::cout;
int main()
{
char *c = "Hello";
cout << std::strchr(c, 'l') << '\n';
// with pointer to null-terminated string, strlen searches
// for zero char and returns the count of characters before it
cout << std::find(c, c + strlen(c), 'l') << '\n';
// with declared array we can use std:begin and std::end
char carr[] = "Hello";
cout << std::find(std::begin(carr),std::end(carr) , 'l') << '\n';
// with pointer to null-terminated string a lambda expression
// can be used to stop at 0 as well as at first key
const char key = 'l';
cout << std::find_if(c, c + strlen(c),[=](const char& item)
{
return (item == key) || (item == '\0');
}) << '\n';
return 0;
}
An std::vector or ::array or other containers can be used with those templates as well.
The <algorithms> header would save tons of time you need to debug your custom-tailored functions for search, iteration, etc.
P.S. cout and cin deal with char and char* in special way.. char* is always pointer at string, char is always a character, not a number, so casts are required if you mean otherwise.
I am using the latest version of Code::Blocks. I have a function that passes in a string and a vector. The function compiles with no errors. However, when I run the debugger, it immediately leads me to line 118 (which I have noted) and gives me trouble. The error that comes up says "Cannot find bounds of current function".
Here is the function, which takes in a line of code of a variable declaration (like "var c=0"), and gets the variable of it and adds its value to the vector, v, a struct with an int value and string name:
char get_variable_declaration(string line, vector<variable> &v)
{
string b;
variable t;
char d[0];
int counter = 0;
int a;
for (int i = 0; i<line.size(); i++) {
if (line[i] == 'r' && counter != 1) {
b[0] = line [i+2];
counter ++;
}
if (line[i] == '=') {
b[1]=line[i+1];
}
}
t.name = b[0];
d[0] = b[1];
a = atoi (d);
t.value = a;
v.push_back (t);
return b[0];
//This function will take in a line of code
//that is confirmed to have a variable declaration
//it will add the variable to the list of
//vectors
}
Here is when it is called:
bool read_code(string file_name, vector<funct> &my_functions, vector<variable> & v)
{
vector<string> code;
string s;
std::size_t found;
bool flag;
funct new_function;
ifstream in;
in.open(file_name.c_str());
if(in.is_open())
{
//read in file line by line and put it into a vector called code
while(in.peek()!=EOF)
{
getline(in,s);
code.push_back(s);
}
in.clear();
in.close();
//read through each line of the code, determine if it's a variable or function (definition or call)
//here it makes reference to functions (listed following this one) which will actually decompose the line
//for information
for(int i=0;i<code.size();i++)
{
//check if it's a variable declaration
found = code[i].find("var");
if(found!=std::string::npos) //its a variable declaration
get_variable_declaration(code[i], v); //ERROR CANNOT FIND..
//check if it's a function. it'll go in the list of functions
found = code[i].find("funct");
if (found!=std::string::npos) //that means it's a function
{
new_function.funct_name=get_function_name(code[i]);
new_function.commands.clear();
i+=2; //skip over the open curly brace
flag=false;
while(!flag)
{
found = code[i].find("}");
if(found==std::string::npos)
{
new_function.commands.push_back(code[i]);
i++;
}
else
{
my_functions.push_back(new_function);
flag=true;
}
}
}
}
return true;
}
else
{
cout << "Cannot locate this file" << endl;
return false;
}
}
Disclaimer: Yes, this is a homework assignment. No, I am not looking for anyone to finish this assignment for me. But, I am still mostly a novice at coding, in need of some assistance, so I ask if you know what is going on, please help me address this issue. Thanks!
Edit: I have gotten this to work on another compiler w/o the text file I am reading from. Not sure if this is a universal issue, or one that the other compiler just didn't pick up on.
Multiple problems with this section of code:
string b;
for (int i = 0; i<line.size(); i++) {
if (line[i] == 'r' && counter != 1) {
b[0] = line [i+2];
counter ++;
}
if (line[i] == '=') {
b[1]=line[i+1];
}
}
Problems:
If the last character in line is 'r', undefined behavior can occur.
If the next to last character in line is 'r', undefined behavior can occur.
If the last character in line is '=', undefined behavior occurs.
Both assignments to b[0] and b[1] is undefined behavior. The b string is empty.
There are also other instances of undefined behavior that have been noted in the comments, which I won't duplicate.
I found the problem. To correctly use atoi, you cannot use a specific character from a string or character array. If you declare a char a[3], and you want to use atoi, you must use it like int value = atoi(a) and not value = atoi(a[2]). If you do not do it this way, it will cause a runtime error.
I am new to programming, so even after trying to google this error I couldn't find anything that was either relevant to my project or was simple enough for me to follow.
I have to make a function that reverses a string iteratively, then another function which does so recursively. The iterative function works perfectly fine:
string reverse(string str_input) {
string result = ""; //initialize a blank string to hold reversed string
for(int i = (str_input.length() - 1); i >= 0; i--) {
result += str_input.substr(i,1); //concatenates the string backwards
}
return result;
However when I tried to make it recursive, I got an invalid pointer error. I've copied my main I'm using for testing and the other function here:
string reverse_rec(string str_input, string result, int input_length);
int main() {
string str_input = "hello";
int input_length = (str_input.length() - 1);
string result = "";
cout<< reverse_rec(str_input, result, input_length) << endl;
return 0;
}
string reverse_rec(string str_input, string result, int input_length) {
if(input_length <= 0) {
return result;
} else {
reverse_rec(str_input, result += str_input.substr(input_length,1), --input_length);
}
}
Does anyone have any hints as to what might be causing this error? From what I've read, most of the people got this error when trying to delete things, but I don't seem to be deleting anything in this program...
Note: We have to do this by concatenating substrings and not by using arrays, as we haven't covered arrays in depth yet.
You forgot to return:
reverse_rec(str_input, result += str_input.substr(input_length,1), --input_length);
^^^^^
Such errors can be easily spotted by the compiler. You should enable warnings.
I am trying to sort a not so small vector of strings with self-defined comparing rule which is here:
bool lexGraph(string const &str1, string const &str2)
{
string::const_iterator i1 = str1.begin(), i2 = str2.begin();
while((i1 < str1.end()) && (i2 < str2.end()))
{
if(*i1 == ' ')
{
i1++;
continue;
}
if(*i2 == ' ')
{
i2++;
continue;
}
if(toupper(*i1) < toupper(*i2))
{
return true;
}
if(toupper(*i1) > toupper(*i2))
{
return false;
}
i1++, i2++;
}
return (str1.length() <= str2.length());
}
I use it in this loop:
vector<string> subset;
ifstream fin(input);
ofstream fout(output);
string buff;
for(long i = 0; i < 241; i++)
{
getline(fin,buff);
buff += '\n';
subset.push_back(buff);
}
sort(subset.begin(), subset.end(),lexGraph);
I found out that the overflow error occurs with vectors larger than 240. I found that this number can even become smaller if I use a smaller file. Also, strings are never really big. If I cut my function down to
bool lexGraph(string const &str1, string const &str2)
{
return (str1.length() <= str2.length());
}
the error still occurs. But it doesnt when I use STL sort without an extra parameter.
So, I cant figure where the leak is and I hope for some hint here.
You need a strict-weak ordering. Your function for ordering must return false when called with equal strings. If you compare with <=, it doesn't work. BTW: I believe that some standard library implementations have a diagnostic mode that could have caught this error for you. Use this, as there are enough ropes in C++ that you can shoot yourself in the foot with.