Program crashes when using string instead of char array - c++

I was trying to write a program to enter texts like passwords (display "*" instead of the character which in input by the user).
The problem is, when I use char arrays to store the password, the program works fine, but when I use a string class variable for the same purpose, my program crashes while displaying the string.
Here is the code:
// *********THIS CODE WORKS FINE***********
#include <iostream>
#include<string>
#include<conio.h>
int main()
{
using namespace std;
int i=0;
cout<<"Enter a password,press ENTER to finish writing"<<endl;
char passw[20];
passw[i]=getch();
while(passw[i]!=13)
{
i++;
cout<<"*";
passw[i]=getch();
}
passw[i+1]='\0';
cout<<"\nPassword is "<<passw;
return 0;
}
Now when I replace char passw[20] with string passw:
//**********THIS CODE CRASHES!!*********
#include <iostream>
#include<string>
#include<conio.h>
int main()
{
using namespace std;
int i=0;
cout<<"Enter a password,press ENTER to finish writing"<<endl;
string passw;
passw=getch();
while(passw[i]!=13)
{
i++;
cout<<"*";
passw[i]=getch();
}
cout<<"\nPassword is "<<passw;
return 0;
}
Can anybody explain why this is happening?
Just started with strings and there's just too much for me to know about strings :)

In string you can use string concatenation.
you can save your char and add it to your string variable.
char ch;
string pass;
ch=getch();
pass=pass+ch;

You're accessing a location (passw[i]) that is not yet existing in your std::string and you're not storing it correctly to a string. You should use the method passwd.push_back(char) to store it.
Check for reference: http://en.cppreference.com/w/cpp/string/basic_string/push_back

A string works differently compared to a char array. You probably know that char passw[20] in your first example creates an array with space for up to 20 chars. These can then be accessed using operator[] without a problem. A string, unlike a basic array, is a dynamic data structure, meaning it grows and shrinks over time as your program runs. Its content can also be accessed using operator[], however, this will not append to the string, it can only be used to change what is already there.
It's important to note that in c++, not all operations will perform what is known as bounds checking. In your second example, you access the i-th character of your string and write a character to that location. However, what would happen if you try to write, for instance, to the 10-th character of a string that only has space for 5? When doing so via operator[], the program will write to whatever location the 10-th character would be at, even though that part of memory is actually not part of the string. Anything could be there and will be overwritten by that operation. As you can probably imagine, this is not desirable at all and can lead to all sorts of problems later down the line. If you're lucky, your program will crash immediately on the write. It did not in your case, however, that's not a good thing; writing out of bounds can cause a heap corruption (the heap being a certain part of memory) which generally leads to a crash at some point down the line.
It's fine to use operator[] to change a strings characters if and only if you know whatever index you're accessing is within the strings size. Otherwise, you need to append to the string using an appropriate member function. These will enlarge the string if necessary.
You always need to pay attention when working with dynamic memory and what parts of it you're accessing (in your first example, for instance, you would also write out of bounds if your password is too long).

Related

Segmentation Fault when trying to copy string from one to another at particular lengths?

#include <iostream>
using namespace std;
int main() {
string s,s_new;
cin>>s;
int len=s.length();
cout<<len<<"\n";
for(int i=0;i<len;i++){
s_new[i]=s[i];
}
cout<<s[len-1]<<"\n";
cout<<s_new[len-1];
return 0;
}
I am trying to copy string 's' to another string 's_new'. String 's' is taken as an input from user.The code snippet outputs the string length for reference and the other two lines prints the
last character from 's' and 's_new'
But for a particular length this program is creating segmentation fault in various IDE.
For example, I got segmentation fault for length 25 in Ideone. Also in onlineGDB I got segmentation fault for length 1961.These lengths were not always constant in these IDE.
I was only using lower_case alphabetical characters for these strings and i used randomly generated strings of various lengths for these tests.
I didnot receive any error when I used character arrays for the same job.
I wanted to know if this issue was because of the code or If there is some problem in using the string STL like I did ?
s_new[i]=s[i]; has undefined behavior since
s_new.size() == 0.
You need to resize it to do what you are doing:
s_new.resize(len);
for(int i=0;i<len;i++){
s_new[i]=s[i];
}
for a particular length this program is creating segmentation fault in various IDE
Writing out-of-bounds always has undefined behavior. In this case, what happens is most likely this:
A std::string often uses small string optimization where the complete string data is stored internally in the std::string. For longer strings a pointer to the string data is used instead (but the user of the class won't notice this). When you do s_new[i] = ... and the string length is 0 and you pass the small string optimization threshold, you start overwriting other internal data in std::string or something else in the memory stored after the std::string.

Reversing input, output string is unexpectedly empty

#include <iostream>
#include <string>
using namespace std;
int main()
{
string s1;
cin>>s1;
int n=s1.size();
string s2;
for(int a=0;a<n;a++)
{
s2[a]=s1[n-1-a];
}
cout<<s2;
}
However I am not getting any output, But I can print elements of reversed string. Can anybody please help.
string s2; // no size allocated
for(int a=0;a<n;a++)
{
s2[a]=s1[n-1-a]; // write to somewhere in s2 that doesn't exist yet
You are writing into elements of s2 that you never created. This is Undefined Behaviour, and anything may happen, including appearing to work normally. In this case, you are probably overwriting some random place in memory. It might crash, or that memory might really exist but not break anything else right away. You might even be able to read that back later, but it would only seem to work by pure accident.
You could catch this problem by always using s2.at(a) to access the data, as it will do a range check for you. ([] does not do a range check). There's a cost to this of course, and sometimes people will skip it in circumstances where they are certain the index cannot be out of bounds. That's debateable. In this case, even though you were probably sure you got the maths right, it still would have helped catch this bug.
You need to either create that space up front, i.e. by creating a string full of the right number of dummy values, or create the space for each element on demand with push_back. I'd probably go with the first option:
string s2(s1.size(), '\0'); // fill with the right number of NULs
for(int a=0;a<n;a++)
{
s2.at(a)=s1.at(n-1-a); // overwrite the NULs
You might want to choose a printable dummy character that doesn't appear in your test data, for example '#', since then it becomes very visible when you print it out if you have failed to correctly overwrite some element. E.g. if you try to reverse "abc" but when you print it out you get "cb#" it would be obvious you have some off-by-one error.
The second option is a bit more expensive since it might have to do several allocations and copies as the string grows, but here's how it would look:
string s2; // no space allocated yet
for(int a=0;a<n;a++)
{
s2.push_back(s1.at(n-1-a)); // create space on demand
I assume you are doing this as a learning exercise, but I would recommend against writing your own reverse, since the language provides it in the library.
You are utilizing something called Undefined Behaviour in your code. You try to access element of s2 at a position, but your string does not have that many chars (it's empty).
You can use std::string::push_back function to add a character on the last postion, so your code would look like this:
for(int a=0;a<n;a++)
{
s2.push_back(s1[n-1-a]);
}
EDIT, to address the other question, your console window probably closes before you can notice. That's why "you don't get any output".
Try this: How to stop C++ console application from exiting immediately?
You can use the inbuilt reverse function in c++
#include<bits/stdc++.h>
using namespace std;
int main()
{
string s1 = "string1";
reverse(s1.begin(),s1.end());
cout << s1;
return 0;
}
Hope that helps :)
You can just construct the string with reverse iterators:
std::string reverse ( std::string const& in )
{
return std::string { in.crbegin(), in.crend() };
}

Old contained is also available(at some indices) even after copying new string to the existing string

I have learned that the work of strcpy is to copy the content of one string into another. Till now I was thinking that when we use strcpy then the old content is totally deleted and the new contained is copied. Kindly see the following program:
#include<iostream>
#include<string.h>
using namespace std;
int main()
{
char str[]="abcdef";
strcpy(str,"xyz");
cout<<str[4];
cout<<str;
return 0;
}
The output is exyz. Please visit this online compiler to see the compiled code.
My first problem is that, that when a new string is copied then the old contained must be deleted but this is not happening here.
Then I concluded that since the new string length is smaller than the existing one therefore only first three contained are deleted and rest are left.
But when I have written cout<<str[3]; then nothing happened.
Why on cout<<str[4]; we are getting e but on cout<<str[3]; we are not getting d.
'd' is overwritten by the null terminator, so str[3] is just a zero. str[4] then contains 'e'. strcpy() only modifies as many characters as it needs to, and it has no idea how long str is, so it cannot zero out the rest.
If you want to truly zero out the array, then use a memset() on it.

Dynamically allocated strings in C

I was doing a relatively simple string problem in UVa's online judge to practice with strings since I've been having a hard time with them in C. The problem basically asks to check if a string B contains another string A if you remove the 'clutter' and concatenate the remaining characters, for example if "ABC" is contained in "AjdhfmajBsjhfhC" which in this case is true.
So, my question is how can I efficiently allocate memory for a string which I don't know its length? What I did was to make a string really big char Mstring[100000], read from input and then use strlen(Mstring) to copy the string the a properly sized char array. Something like :
char Mstring[100000];
scanf("%s",Mstring);
int length = strlen(Mstring);
char input[length+1]={0};
for(int i = 0; i<length;i++){
input[i]=Mstring[i];
}
Is there a better/standard way to do this in C? I know that C does not has a great support for strings, if there is not a better way to do it in C maybe in C++?
If you have the option of using C++ (as you mentioned), that is going to make your life a lot easier. You can then use a STL string (std::string) which manages dynamically sized strings for you. You can also drop the old scanf() beast and use std::cin.
Example:
#include <iostream>
#include <string>
void main()
{
std::string sInput;
std::getline(std::cin, sInput);
// alternatively, you could execute this line instead:
// std::cin >> sInput;
// but that will tokenize input based on whitespace, so you
// will only get one word at a time rather than an entire line
}
Describing how to manage strings that can grow dynamically in C will take considerably more explanation and care, and it sounds like you really don't need that. If so, however, here is a starting point: http://www.strchr.com/dynamic_arrays.

Able to Access Elements with Index Greater than Array Length

The following code seems to be running when it shouldn't. In this example:
#include <iostream>
using namespace std;
int main()
{
char data[1];
cout<<"Enter data: ";
cin>>data;
cout<<data[2]<<endl;
}
Entering a string with a length greater than 1 (e.g., "Hello"), will produce output as if the array were large enough to hold it (e.g., "l"). Should this not be throwing an error when it tried to store a value that was longer than the array or when it tried to retrieve a value with an index greater than the array length?
The following code seems to be running when it shouldn't.
It is not about "should" or "shouldn't". It is about "may" or "may not".
That is, your program may run, or it may not.
It is because your program invokes undefined behavior. Accessing an array element beyond the array-length invokes undefined behavior which means anything could happen.
The proper way to write your code is to use std::string as:
#include <iostream>
#include <string>
//using namespace std; DONT WRITE THIS HERE
int main()
{
std::string data;
std::cout<<"Enter data: ";
std::cin>>data; //read the entire input string, no matter how long it is!
std::cout<<data<<std::endl; //print the entire string
if ( data.size() > 2 ) //check if data has atleast 3 characters
{
std::cout << data[2] << std::endl; //print 3rd character
}
}
It can crash under different parameters in compilation or compiled on other machine, because running of that code giving undefined result according to documentaton.
It is not safe to be doing this. What it is doing is writing over the memory that happens to lie after the buffer. Afterwards, it is then reading it back out to you.
This is only working because your cin and cout operations don't say: This is a pointer to one char, I will only write one char. Instead it says: enough space is allocated for me to write to. The cin and cout operations keep reading data until they hit the null terminator \0.
To fix this, you can replace this with:
std::string data;
C++ will let you make big memory mistakes.
Some 'rules' that will save you most of the time:
1:Don't use char[]. Instead use string.
2:Don't use pointers to pass or return argument. Pass by reference, return by value.
3:Don't use arrays (e.g. int[]). Use vectors. You still have to check your own bounds.
With just those three you'll be writing some-what "safe" code and non-C-like code.