For context, I'm rewriting the string class in C++ to use on microcontrollers, specifically Arduino, so that it doesn't use the standard library functions not supported by Arduino.
I've looked at several answers here that show how to pop a char off a char*. However, within my function it doesn't seem to correctly edit the char*.
My string class
#include <stdlib.h> // malloc
namespace micro_std {
class string {
private:
const int MAX_SIZE = 4096; // Maximum size on 16bit controllers.
char* data = nullptr;
int _length = 0;
public:
string(char* data) {
this->data = data;
for (int i = 0; data[i] != '\0'; i++)
_length++;
}
int size(void) const { return _length; }
int length(void) const { return _length; }
int max_size(void) const { return MAX_SIZE; }
bool empty(void) const { return _length == 0; }
char at(int index) const { return data[index]; }
char back(void) const { return data[_length - 1]; }
char front(void) const { return data[0]; }
char* str(void) const { return data; }
void clear(void) { _length = 0; data = nullptr; }
// removed for brevity //
char pop_back(void) {
_length--;
char character = data[_length];
data[_length] = '\0';
return character;
}
// removed for brevity //
};
}
And how I'm testing my code, specifically the pop_back function.
#include <stdio.h> // printf
#include "micro_std/string.h"
int main(int argc, char** argv) {
micro_std::string x = "abcdef";
// Testing pop_back function
printf("%d %s\n", x.length(), x.str());
for (int i = 0; i < 5; i++) {
char res = x.pop_back();
printf("%d %c %s\n", x.length(), res, x.str());
}
//printf("%s\n", x.str());
return 0;
}
And, if needed, my compiler arguments
g++ -w -std=c++2a -O3 program.cpp -o program
Running this program gives me the following output:
6 abcdef
5 f abcdef
4 e abcdef
3 d abcdef
2 c abcdef
1 b abcdef
Instead of the output I want:
6 abcdef
5 f abcde
4 e abcd
3 d abc
2 c ab
1 b a
Where the output is formatted like "(length) (char popped) (result string)". Why isn't the data member data being altered when calling the pop_back function?
Why isn't the data member data being altered when calling the pop_back function?
Even if the code compiles (which it shouldn't, since you are trying to construct x with a string literal, which is a const char[] array that cannot be assigned to a non-const char*), x would be pointing its data member at a string literal, thus data[_length] = '\0'; inside of pop_back() would invoke Undefined Behavior trying to alter read-only memory.
To make your code work, you MUST make a copy of the input data, eg:
#include <stdlib.h> // malloc
namespace micro_std {
class string {
private:
const int MAX_SIZE = 4096; // Maximum size on 16bit controllers.
char _data[MAX_SIZE];
int _length = 0;
public:
string(const char* str) {
for (int i = 0; str[i] != '\0'; ++i) {
data[i] = str[i];
++_length;
}
data[_length] = '\0';
}
...
void clear(void) { _length = 0; }
...
};
}
Related
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);
}
}
}
Here is my code
int main(int argc, char *argv[]) {
char const *strings[10] = {"dhh", "aci", "cdh"};
join_def(strings, 'l');
return EXIT_SUCCESS;
}
// part 1 read lines
void join_def(char const **strings, char delim) {
char *t = new char[100];
//int length = 0;
t[0] = '\0';
int x = sizeof(strings);
std::cout << delim << std::endl;
for (int i = 0; i < x; i++) {
int size = 0;
while(strings[i][size]!='\0'){
size++;
std::cout << strings[i][size] << std::endl;
}
}
}
I have spent hours now I just can't get to concatenate it
For this task, I cannot use cstring or anything other than iostream so please don't suggest.
The output needs to be a c-string = "dhhlacilcdh"
First, you cannot determine the number of elements in an array passed to a function, as that array will decay to a simple pointer. So, your sizeof(strings) expression will evaluate (at compile time) to the (fixed) size, in bytes, of a pointer. For the function to be 'aware' of how many elements there are in an array, it needs to be explicitly told (by way of an extra argument).
Second, you have your i' and size indexes the wrong way round in the std::cout << strings[i][size] << std::endl; line and, further, you increment size before printing the relevant character, whereas it should be incremented after you've printed it.
The code below also does the actual concatenation of the strings, and the modified join_def function now returns a pointer to that result (which must be freed when you're finished with it);
#include <iostream>
char* join_def(char const** strings, char delim, int x)
{
char* t = new char[100];
int length = 0;
t[0] = '\0';
//int x = sizeof(strings);
std::cout << delim << std::endl;
for (int i = 0; i < x; i++) {
int size = 0;
while (strings[i][size] != '\0') {
std::cout << strings[i][size] << std::endl;
t[length++] = strings[i][size]; // Append this character
size++;
}
t[length++] = delim; // Append delimiter
}
t[length] = '\0'; // Append nul-terminator
return t;
}
int main()
{
char const* strings[10] = { "dhh", "aci", "cdh" };
char* result = join_def(strings, 'l', 3);
std::cout << result << std::endl;
free(result);
return 0;
}
Note, also, that I have moved the join_def function code to before the main (which calls it). If you don't do this, then will at least have to provide a (forward) declaration of that function before main (just a char* join_def(char const** strings, char delim, int x); on its own will do).
Feel free to ask for further clarification and/or explanation.
I'm not exactly sure what you're trying to do, but maybe this helps?
#include <iostream>
// part 1 read lines
void join_def(char const **strings, char delim)
{
char *t = new char[100];
//int length = 0;
t[0] = '\0';
int x = 0;
for (int i = 0; strings[i] != nullptr; i++)
x += sizeof(strings[i]) - 1;
std::cout << delim << std::endl;
for (int i = 0; strings[i] != nullptr; i++)
{
int size = 0;
while (strings[i][size] != '\0')
{
size++;
std::cout << strings[i][size] << std::endl;
}
}
}
int main(int argc, char *argv[])
{
char const *strings[] = {"dhh", "aci", "cdh", nullptr};
join_def(strings, 'l');
return EXIT_SUCCESS;
}
this is what you are looking for?
look that I remove all the std::endl because it like '\n'
also i moved your size++ after the std::cout
#include <iostream>
// part 1 read lines
void join_def(char const **strings, char delim,int length) {
char *t = new char[100];
//int length = 0;
t[0] = '\0';
int x = length;
for (int i = 0; i < x; i++) {
int size = 0;
while(strings[i][size]!='\0'){
std::cout << strings[i][size]; //<--print befure increment size
size++;
}
std::cout << delim;
}
}
int main(int argc, char *argv[]) {
char const *strings[] = {"dhh", "aci", "cdh"};
join_def(strings,'|',3); //<- need to send the length of the char* array
return EXIT_SUCCESS;
}
I have a problem about initializing char* a = new char[size]. Here is my code.
class Practice
{
public:
Practice(const char* a);
~Practice();
const char* getString() const;
private:
char* mString;
int mSize;
};
#include "Practice.h"
Practice::Practice(const char * a)
:mSize(0)
,mString(nullptr)
{
while (a[mSize] != '\0')
{
mSize++;
}
mString = new char[mSize];
for (int i = 0; i < mSize; i++)
{
mString[i] = a[i];
}
}
Practice::~Practice()
{
delete[] mString;
}
const char* Practice::getString() const
{
return mString;
}
int main()
{
Practice p("Hello");
std::cout << p.getString() << std::endl;
return 0;
}
I expected the result is Hello.
But the result was like Hello²²²²▌▌▌▌▌▌▌■a%{▌.
I thought I initialized the mString member variable through mString = new char[mSize]. But it was not working the way I thought.
Can anybody enlighten me what's wrong with my code and fix it?
mString is not NUL-terminated.
Your're checking a constructor parameter for \0. But you are not allocating mString to put the same \0 there, and don't copy \0 from a.
So, resulting mString is not properly NULL-terminated and it would be read beyond end.
Reading beyond end of allocated is undefined behavior. In particular case, it is likely that either mString would output until some accidental zero, or crash would occur.
while (a[mSize] != '\0')
{
mSize++;
}
This sets mSize equal to the length of a, excluding the terminating '\0'. You should include the terminator in your copy:
while (a[mSize++])
{
}
or simply:
mSize = strlen(a) + 1;
You are not null-terminating your mString data, but it is expecting to be null-terminated when you pass it to std::cout. Without that terminator, std::cout reads into surrounding memory until it encounters a random null byte (or crashes with a read access error). That is why you are seeing std::cout output random garbage after your data.
You are also not following the Rule of 3/5/0, as you are missing a default constructor, copy and move constructors, and copy and move assignment operators.
Try this:
class Practice
{
public:
Practice(const char* a = nullptr);
Practice(const Practice &src);
Practice(Practice &&src);
~Practice();
Practice& operator=(Practice src);
const char* getString() const;
private:
char* mString;
int mSize;
};
#include "Practice.h"
#include <utility>
Practice::Practice(const char * a)
: mSize(0)
, mString(nullptr)
{
if (a)
{
while (a[mSize] != '\0')
{
++mSize;
}
}
mString = new char[mSize + 1];
for (int i = 0; i < mSize; ++i)
{
mString[i] = a[i];
}
mString[mSize] = '\0';
}
Practice::Practice(const Practice &src)
: mSize(src.mSize)
, mString(nullptr)
{
mString = new char[mSize + 1];
for (int i = 0; i < mSize; ++i)
{
mString[i] = src.mString[i];
}
mString[mSize] = '\0';
}
Practice::Practice(Practice &&src)
: mSize(src.mSize)
, mString(src.mString)
{
src.mString = nullptr;
src.mSize = 0;
}
Practice::~Practice()
{
delete[] mString;
}
Practice& Practice::operator=(Practice src)
{
std::swap(mString, src.mString);
std::swap(mSize, src.mSize);
return *this;
}
const char* Practice::getString() const
{
return mString;
}
#include <iostream>
#include "Practice.h"
int main()
{
Practice p("Hello");
std::cout << p.getString() << std::endl;
return 0;
}
I had an interview task to remove substring from a current string without using string functions or additional memory...I tried just with strlen but didn't find the way to change it in place...
#include <stdio.h>
#include <string.h>
#include <iostream>
using namespace std;
int main()
{
char * str = "this that there";
char * substr = "th";
removeSubstr(str,substr);
cout<<str; //should be now "is at ere"
return 0;
}
void removeSubstr(char * str, const char * substr){
}
Since you remove characters from the original string, the string is shrinking, so you do not need additional space. You just have to copy characters from a higher index (source) to a lower index (destination).
If the source index points to a position that starts with the searched substring, you must skip it.
The skip function simply compares the beginning of the source string against the substring, and returns the source (if it does not start with the substring) or the source plus the length of substring (if it starts with the substring).
Demo
#include <iostream>
char* skip_if( char* s, const char* ss )
{
char* p = s;
const char* pp = ss;
while( *p == *pp && *p )
p++, pp++;
return *pp ? s : p;
}
void remove( char* s, const char* ss )
{
char *ps = s; // source
char *pd = s; // destination
while( *ps )
{
ps = skip_if( ps, ss );
*pd++ = *ps++;
}
*pd = 0;
}
int main()
{
using namespace std;
char str[] = "this that there this that there";
const char* substr = "th";
remove( str, substr );
cout << str;
return 0;
}
The string functions exist for a reason. If you are not to use them, can you create them from scratch?
If so, here is my solution to the problem. Very educational to write custom string functions with the const requirements.
As mentioned in the comments, this won't work unless the string passed in can be modified, so I made it a non-constant string.
Let me know if this meets the interview challenge:
#include <iostream>
bool myStrnCmp(char *str1, const char *str2, int len) {
char *cptr1 = (char *) str1;
char *cptr2 = (char *) str2;
for (int i = 0; i < len; i++) {
if (*(cptr1 + i) != *(cptr2 + i))
return false;
}
return true;
}
int mystrlen(const char* str) {
int i = 0;
while(*(str + i) != '\0')
i++;
return i;
}
int findSubStr(char *str, const char *substr) {
int position = 0;
int len = mystrlen(substr);
while(*(str + position) != '\0') {
for (int i = 0; i < len; i++) {
if (myStrnCmp(str + position + i, substr, len))
return position + i;
}
++position;
}
return -1;
}
void myStrCpy(char *str, const char *substr) {
memmove(str, substr, mystrlen(substr) + 1);
}
void removeSubstr(char *str, const char *substr) {
int position = findSubStr(str, substr);
while(position >= 0) {
myStrCpy(str + position, str+position+mystrlen(substr));
position = findSubStr(str, substr);
}
}
int main() {
char str[]{"this that there"};
char* substr = "th";
removeSubstr(str,substr);
std::cout<<str; //should be now "is at ere"
return 0;
}
Basically what the title says. I have been trying to write my own string class using only char arrays and, while my code works when I run it in Visual Studio, I have trouble with it when using gcc .The problem seems to be coming when i try to delete in my getData function(can be seen below) The exception I get is:
Exception thrown at 0x6262436B (ucrtbased.dll) in string.exe: 0xC0000005: Access violation reading location 0xCCCCCCBC. occurred
My code :
Header:
#pragma warning(disable:4996)
#ifndef STRING_STRING_H
#define STRING_STRING_H
#include<iostream>
#include<cstring>
#include<fstream>
class String {
private:
char *data; //holds the text
size_t maxSize; //maximum number of chars in data
size_t currentSize; //current number of chars in data
void getData(const char *, size_t maxSize); //sets currentSize to the other char* size and
// copies the content of the other char* to data
public:
String(); //default constructor
~String(); //destructor
String(const String &); //copy-constructor(from String)
String(const char *); //copy-constructor(from char*)
String operator=(const String &); //operator= (from string)
String operator=(const char *); //operator=(from char*)
size_t length() const; //currentSize getter
void addChar(const char); //adds a char to the data array
void getLine(std::ifstream&,const char); // reads line till deliminator and stores it in this string object(all data previously stored is lost)
size_t find(const char*); //searches for text in the string and if found returns the starting position , if not found returns -1;
void print() const; //prints the string object to console
char* toChar() const; //returns a new allocated char pointer with the text inside (must be deleted afterwards)
};
#endif //STRING_STRING_H
cpp:
#include "String.h"
String::String() {
currentSize = 0;
maxSize = 16;
try {
data = new char[maxSize];
data[0] = '\0';
}
catch (std::bad_alloc &) {
std::cerr << "Not enough memory" << std::endl;
throw;
}
}
String::~String() {
delete[] data;
}
size_t String::length() const {
return currentSize;
}
String::String(const String &other) {
this->maxSize = other.maxSize;
getData(other.data, maxSize);
}
String::String(const char *other) {
this->maxSize = strlen(other) *2;
getData(other, maxSize);
}
void String::getData(const char *dataSource, size_t maxSize) {
currentSize = strlen(dataSource);
try {
char *newData = new char[maxSize];
delete[] data;
data = newData;
strcpy(data, dataSource);
}
catch (std::bad_alloc &) {
std::cerr << "Not enough memory" << std::endl;
throw;
}
}
String String::operator=(const String &other) {
if (this != &other) {
maxSize = other.maxSize;
getData(other.data, maxSize);
}
return *this;
}
String String::operator=(const char *other) {
if (this->data != other) {
maxSize = strlen(other) *2;
getData(other, maxSize);
}
return *this;
}
void String::addChar(const char newChar) {
if (maxSize == currentSize+1) {
maxSize *= 2;
getData(this->data, maxSize);
}
data[currentSize++] = newChar;
}
void String::getLine(std::ifstream & is, const char delim='\n')
{
char temp;
while (!is.eof())
{
is.get(temp);
if (temp == delim)
break;
else
addChar(temp);
}
return;
}
size_t String::find(const char * text)
{
size_t currPos=-1;
bool found = 0;
for (size_t i = 0; i < currentSize; i++)
{
if (data[i] == text[0])
{
for (size_t j = i+1; j < currentSize; j++)
{
if (data[j] == text[j - i])
found = 1;
else
{
found = 0;
break;
}
}
if (found == 1)
{
currPos = i;
break;
}
}
}
return currPos;
}
void String::print() const
{
for (size_t i = 0; i < currentSize; i++)
{
std::cout << data[i];
}
std::cout << std::endl;
}
char * String::toChar() const
{
char* text= new char[currentSize+1];
for (size_t i = 0; i < currentSize; i++)
{
text[i] = data[i];
}
text[currentSize + 1] = 0;
return text;
}
Your problem is caused by calling delete [] on uninitialized memory.
In the copy constructor, the data member is not initialized before calling getData(). In getData(), you are using delete [] data;.
You could initialize data to nullptr in the constructors to avoid the problem.
It's always a good idea to initialize all variables to some sensible value before the body of the constructor. E.g. you can implement the copy constructor as:
String::String(const String &other) : currentSize(0),
maxSize(other.maxSize),
data(nullptr)
{
getData(other.data, maxSize);
}
Similarly, implement the constructor from char const* as:
String::String(const char *other) : currentSize(0),
maxSize(strlen(other) *2),
data(nullptr)
{
getData(other, maxSize);
}