Why can't my loop detect \0 symbol in const string? - c++

#include<iostream>
#include<string>
#include<sstream>
#include <typeinfo>
#include<cmath>
#include<vector>
#include <algorithm>
using namespace std;
class Mystring {
char *arr;
public:
Mystring(const char pointer[]) {
int i = 0;
while (pointer[i] != '\0') {
i++;
cout << pointer[i] << endl;
}
arr = new char[i];
i = 0;
while (pointer[i] != '\0') {
arr[i] = pointer[i];
i++;
}
}
friend ostream& operator<<(ostream& out, Mystring& str) {
int i = 0;
while (str.arr[i] != '\0') {
out << str.arr[i];
i++;
}
return out;
}
};
int main() {
Mystring string("Hello, world!");
cout << string << endl;
}
I'm trying to create my own string class. The length of "hello world!" is 13, but the length of arr turns out to be 17. It is filled with some strange chars for some reason I don't understand. When I'm trying to cout string it returns this: Hello, world!¤¤¤¤.

You forgot to add '\0' at the end of your arr[].
Moreover, the size or arrshould be increased to incorporate this \0, as noted in #user7860670's answer.
Moreover, using string as a variable name, in addition to using namespace std;, is confusing at least.
#include<iostream>
#include<string>
#include<sstream>
#include <typeinfo>
#include<cmath>
#include<vector>
#include <algorithm>
//using namespace std;
class Mystring {
char *arr;
public:
Mystring(const char pointer[]) {
int i = 0;
while (pointer[i] != '\0') {
i++;
std::cout << pointer[i] << std::endl;
}
arr = new char[i+1];
i = 0;
while (pointer[i] != '\0') {
arr[i] = pointer[i];
i++;
}
arr[i] = '\0';
}
friend std::ostream& operator<<(std::ostream& out, const Mystring& str) {
int i = 0;
while (str.arr[i] != '\0') {
out << str.arr[i];
i++;
}
return out;
}
};
int main() {
Mystring mstring("Hello, world!");
std::cout << mstring << std::endl;
}

Buffer size required to hold "Hello, world!" (including terminating null) is 14 while you only allocate space for 13 chars and completely omit terminating null. So during iteration inside of operator<< buffer index will go out of bounds which is Undefined Behavior.
You should allocate one extra byte and make sure that buffer ends with terminating null
arr = new char[i + 1];
i = 0;
while (pointer[i] != '\0')
{
arr[i] = pointer[i];
++i;
}
arr[i] = '\0';

Related

How to reverese a c-style string

I would like to reverse a c-style string and came up with the following code.
Play with the code
#include <iostream>
void reverse_c_str(char *c)
{
char *new_c = c;
for (; *new_c; ++new_c){} // find '\0'
--new_c; // new_c is now at the actual last char, which is 'g'
while (c < new_c) // swap chars
{
char tmp = *c;
*c = *new_c; // crash
*new_c = tmp;
--new_c;
++c;
}
}
int main()
{
char *c = "my string";
reverse_c_str(c);
std::cout << c << '\n';
}
Unfortunately, my code has an error, which I marked with // crash. Why does this line crash?
"my string" is a string literal, it is const.
It can be cast to a non-const char * for reasons of compatibility with C, but modifying a constant invokes undefined behavior. In some cases the OS will prevent it (as when it's stored in a read-only section), which is probably what you're observing.
Make a copy of the string in automatic storage, then you'll be able to modify it:
int main()
{
char c[] { "my string" };
reverse_c_str(c);
std::cout << c << '\n';
}
And of course there is a templated soultion:
#include <cstring>
template<std::size_t N>
void reverse_c_str(char (&str)[N]) {
std::size_t len = N-1;
for (std::size_t i = 0; i < len/2; ++i) {
str[i] ^= str[len-i-1];
str[len-i-1] ^= str[i];
str[i] ^= str[len-i-1];
}
}
int main() {
char c[] {"123"};
reverse_c_str(c);
char c2[] {""};
reverse_c_str(c2);
char c3[] {"4321"};
reverse_c_str(c3);
return 0;
}
Use std::swap and std::strlen. I made example for you here.
#include <iostream>
#include <cstring>
void reverse_c_str(char *c) {
int length = std::strlen(c);
for (int i = 0; i < length / 2; i++)
std::swap(c[i], c[length - i - 1]);
}
int main()
{
char c[] { "my string" };
reverse_c_str(c);
std::cout<<c << std::endl;
return 0;
}
Output:
gnirts ym
Another one version of function reverse_c_str
void reverse_c_str(char *c) {
if(*c) {
for(auto begin = c, end = c + std::strlen(c) - 1;
begin < end; ++begin, --end
) {
std::swap(*begin, *end);
}
}
}

error: conversion from ‘const char [5]’ to non-scalar type ‘String’ requested

I am trying to create a class String which can be assigned by operator=. But the compiler shows an error:
error: conversion from ‘const char [5]’ to non-scalar type ‘String’ requested
Can anyone help me to fix it?
#include <iostream>
using namespace std;
class String
{
private:
char string[];
public:
void operator=(const char str[])
{
for (int i = 0; ; i++) {
if (str[i] == '\0') {
string[i] = str[i];
break;
} else {
string[i] = str[i];
}
}
}
friend ostream &operator<<(ostream &output, const String& str)
{
output << str.string;
return output;
}
};
int main()
{
String str1 = "test";
cout << str1 << endl;
}
String str1 = "test"; does not use operator= at all. It is just syntax sugar for String str1("test");, which uses a conversion constructor that you have not defined yet, hence the compiler error. You need to add such a constructor.
Also, char string[]; is not a valid variable declaration for an array. You need to specify a size for the array, and then make sure the class never exceeds that size.
For example
#include <iostream>
#include <cstring>
using namespace std;
class String {
private:
char string[256];
public:
String(const char *str = NULL) {
if (str) strncpy(string, str, sizeof(string)-1);
string[sizeof(string)-1] = '\0';
}
String& operator=(const String &str) {
if (this != &str) {
memcpy(string, str.string, sizeof(string));
}
return *this;
}
friend ostream& operator<<(ostream &output, const String& str) {
output << str.string;
return output;
}
};
int main() {
String str1 = "test";
cout << str1 << endl;
}
However, in this situation, using a dynamically allocated array makes more sense than using a fixed array. Just be sure to follow the Rule of 3 for proper memory management.
Try this instead:
#include <iostream>
#include <cstring>
using namespace std;
class String {
private:
char *string;
int length;
int capacity;
public:
String(const char *str = NULL)
: string(NULL), length(0), capacity(0)
{
if ((str) && (*str != '\0')) {
length = capacity = strlen(str);
string = new char[length + 1];
memcpy(string, str, length + 1);
}
}
String(const String &str)
: string(NULL), length(0), capacity(0)
{
if (str.string) {
length = capacity = str.length;
string = new char[length + 1];
memcpy(string, str.string, length + 1);
}
}
~String() {
delete[] string;
}
String& operator=(const String &str) {
if (this != &str) {
int len = str.length;
if (capacity >= len) {
memcpy(string, str.string, len + 1);
}
else {
int cap = int(double(len) * 1.5);
char *temp = new char[cap + 1];
memcpy(temp, str.string, len + 1);
delete[] string;
string = temp;
capacity = cap;
}
length = len;
}
return *this;
}
friend ostream& operator<<(ostream &output, const String& str) {
if (str.string) {
output.write(str.string, str.length);
}
return output;
}
};
int main() {
String str1 = "test";
cout << str1 << endl;
}
You need to add a ctor to your class. You are using the assignment operator to try to construct your String object. Add this to your class.
String(const char str[]) {
for (int i = 0; ; i++) {
if (str[i] == '\0') {
string[i] = str[i];
break;
} else {
string[i] = str[i];
}
}
}

Receiving an exception in overloaded operator >> section

So I've overloaded an operator ">>" for my class MyString. It was supposed to read information from text file to a custom-made string, however I'm getting an exception when I'm trying to fill string char by char. Text.txt contains simply "xyz"
Main.cpp:
#include "MyString.h"
#include <fstream>
#include <iostream>
//TO-DO 1 CharArr for all
int main() {
MyString NewString;
ifstream In("Text.txt");
In >> NewString;
cout << NewString << endl;
system("pause");
return 0;
}
Constructors:
MyString::MyString() {
StringLength = 0;
Pointer = nullptr;
}
MyString::MyString(const char* String) {
for (int i = 0; String[i]; i++)
StringLength++;
Pointer = new char[StringLength + 1];
char *Source = (char *)String;
char *Destination = (char *)Pointer;
for (int i = 0; i < StringLength + 1; i++)
Destination[i] = Source[i];
}
Operator:
istream &operator>>(istream &In, MyString &String) {
int FileStringLength = 0;
char Character;
if (String.Pointer != nullptr)
delete[] String.Pointer;
while (In.get(Character) && Character != '\n')
FileStringLength++;
if (FileStringLength < 1000 && FileStringLength != 0) {
String.StringLength = FileStringLength;
In.clear(), In.seekg(0, ios::beg);
for (int i = 0; In.get(Character) && Character != '\n'; i++)
String.Pointer[i] = Character; // I get an exception here
}
else if (!FileStringLength) {
cout << "File is empty." << endl;
}
else {
cout << "File contains too many characters." << endl;
}
return In;
};
if (String.Pointer != nullptr)
delete[] String.Pointer;
freed the storage that
String.Pointer[i] = Character; // I get an exception here
was going to use.
Solution:
Don't delete[] the storage. Instead write over it until the storage is full, then allocate a new, larger buffer for storage, copy the old buffer into the new buffer, and then free the old buffer.

How to split an array of characters without using any basic function

I have this function: int split(char* str, char s), so how to split the str without using strtok() or other functions?
E.g: str = "1,2,3,4,5", s = ','
After split(str, s), output will be:
1
2
3
4
5
Sorry guys, the int return -1 if str == NULL and return 1 if str != NULL.
How about this? I'm not sure what the int return type means in the function so I made it the count of splits.
#include <stdio.h>
int split(char* str, char s) {
int count = 0;
while (*str) {
if (s == *str) {
putchar('\n');
count++;
} else {
putchar(*str);
}
str++;
}
return count;
}
I didn't write code for years, but that should do?
while (*str) // as long as there are more chars coming...
{
if (*str == s) printf('\n'); // if it is a separator, print newline
else printf('%c',*str); // else print the char
str++; // next char
}
string split(const char* str, char s) {
std::string result = str;
std::replace(result.begin(), result.end(), s, '\n');
result.push_back('\n'); // if you want a trailing newline
return result;
}
Another approach...
#include <iostream>
using namespace std;
void split(char* str, char s){
while(*str){
if(*str==s){
cout << endl;
}else{
cout << *str;
}
str++;
}
cout << endl;
}
int main(){
split((char*)"herp,derp",',');
}
and another one with iterator
#include <iostream>
using namespace std;
int main() {
string s="1,2,3,4,5";
char cl=',';
for(string::iterator it=s.begin();it!=s.end();++it)
if (*it == cl)
cout << endl;
else cout << *it;
return 0;
}
http://ideone.com/RPrls7

Palindrome finder: non-alphanumeric character deletion problems

So I'm having a substantial amount of trouble with this one bit of code. I've included the whole program for context, but my issue lies in the cleanUp function, wherein I (attempt to) remove all characters that are not 'A' through 'Z'.
Any tips?
#include <iostream>
#include <cstdlib>
#include <string>
#include <stdio.h>
#include <ctype.h>
using namespace std;
bool again(string title); // Checks if you want to run again.
void makeUpper(char word[]);
void getReverse(char word[], char reversed[]);
char * find(char *str, char what);
bool equal(char word[], char reversed[]);
int size(char word[]);
char * cleanUp(char *str);
int main()
{
char word[256] = "Hello?? There!", reversedWord[256];
do
{
cout<<"Please enter the string to check: ";
makeUpper(word);
cout << word;
cleanUp(word);
getReverse(word,reversedWord);
if(equal(word, reversedWord))
cout<<"You have a palindrome!"<<endl;
else
cout<<"You do not have a palindrome!"<<endl;
} while(again("Do you want to do this again? "));
return 0;
}
bool again(string title)
{
string answer;
cout<<endl<<title;
getline(cin,answer);
return toupper(answer[0]) == 'Y';
}
void makeUpper(char word[])
{
char *ptr = word;
while (*ptr) {
*ptr = toupper(*ptr);
ptr++;
}
cout << "In uppercase:: " << word << endl;
}
char * cleanUp(char * astrid)
{
char *new_astrid;
for (*astrid; *astrid != '\0'; astrid++)
{
cout << "First loop";
if (isalpha(*astrid))
{
*new_astrid = *astrid;
new_astrid = ++new_astrid;
cout << "Here!";
}
}
cout << *new_astrid;
return *new_astrid;
}
void getReverse(char word[], char reversed[])
{
char *ptr_ind = find(word, '\0'), *ptr_ind_2 = reversed;
while(ptr_ind != word-1)
{
*ptr_ind_2 = *ptr_ind;
ptr_ind--;
ptr_ind_2++;
}
*ptr_ind_2 = '\0';
}
char * find(char *str, char what)
{
char *ptr = str;
while(*ptr != what && *ptr != '\0')
ptr++;
return *ptr == what ? ptr : NULL;
}
bool equal(char word[], char reverse[])
{
int total;
char * ptr;
ptr = word;
if((total = size(word)) != size(reverse))
return false;
for(char * ptr2 = reverse; *ptr != '\0' && *ptr == *ptr2; ptr++, ptr2++);
return *ptr == '\0';
}
int size(char word[])
{
int total = 0;
char * ptr = word;
while(*ptr != '\0') //while(!ptr)
{
ptr++;
total++;
}
return total;
}
There are several errors in your code.
new_astrid is not initialized and when you call *new_astrid = *astrid you try to copy a character to uninitialized memory, which will crash the program.
You also return the dereferenced pointer to new_astrid but the function prototype of cleanUp says that you return a pointer to char.
You should initialize new_astrid with new char[strlen(astrid)]. But then your code will result in memory leaks, since you increase the pointer (new_astid = ++new_astrid). So you should store the pointer first, so you can delete it later.
Instead of using raw pointers, i would suggest you use std::strings.
My suggestion for a palindrome tester would be:
#include <iostream>
#include <string>
#include <locale>
bool isPalindrome(std::string word)
{
std::locale loc;
for (std::string::size_type i = 0; i < word.length() / 2 + 1; ++i)
{
if (std::toupper(word[i],loc) != std::toupper(word[word.length() - i - 1],loc))
{
return false;
}
}
return true;
}
int main(int , char **)
{
std::string str = "Abba";
//Remove all non alpha values from string
str.erase(std::remove_if(str.begin(), str.end(), [](char const c){return !std::isalpha(c);}), str.end());
if (isPalindrome(str) == false)
{
std::cout << str << " is no palindrome" << std::endl;
}
else
{
std::cout << str << " is a palindrome" << std::endl;
}
return 0;
}
The erasion of non alpha values in the string is from this question.