Dynamic array of fixed length strings - c++

How do I create a dynamic array of fixed length strings?
I created class AString which has pointers to struct _str which has fixed-length array data.
How to assign values, and what is wrong?
#include "stdafx.h"
#include <iostream>
struct _str {
char data[20];
};
class AString {
public:
AString();
~AString();
void Add(_str string);
private:
_str *str;
int count;
};
AString::AString() {
std::cout << "Constructor" << std::endl;
str = nullptr;
count = 0;
}
AString::~AString() {
std::cout << "Destructor" << std::endl;
if (str != nullptr) delete[] str;
}
void AString::Add(_str string) {
_str *str2 = new _str[count+1];
for (int i=0;i<count;i++) {
str2[i] = str[i];
}
delete[] str;
str = str2;
count++;
str[count-1] = string;
}
int _tmain(int argc, _TCHAR* argv[])
{
AString astring;
_str str1;
str1.data="0123456789012345678"; // cannot convert from 'const char[20]' to 'char[20]'
astring.Add(str1);
std::cin.get();
return 0;
}
str1.data="0123456789012345678";: cannot convert from 'const char[20]' to 'char[20]'
Want to:
not use _str str1;, and use char str1[20];

As for me, I used this:
strcpy(str1.data, "0123456789012345678");
Here is the main:
int main(int argc, char* argv[])
{
AString astring;
_str str1;
//str1.data=const_cast<char>("0123456789012345678"); // cannot convert from 'const char[20]' to 'char[20]'
strcpy(str1.data, "0123456789012345678");
astring.Add(str1);
std::cout << str1.data;
std::cin.get();
return 0;
}
The result is as follows:
Constructor
0123456789012345678

First of all, I would recomend you yo use std::string or std::array. But if you forced to use char[], I would recomend you to use strncpy instead of operator =, so it would looks like this
strncpy(str1.data,"0123456789012345678", 20); // cannot convert from 'const char[20]' to 'char[20]'

To copy char* or block of memory, it is better to use memcpy!
void* memcpy (void* destination, const void* source, size_t length);
It copies the values of length bytes starting the location pointed to by source directly to the memory block pointed to by destination. Note that, the underlying type of the objects pointed to by both the source and destination pointers dose not matter.
You can also use strncpy.
char* strncpy(char* destination, const char* source, size_t length);
It works properly for your code but if there is/are some 0 valued bytes, the strncpy consider it as null-termination and coping continued with '0' i.e. (pad) until length is satisfied.
try
memcpy(str1.data, "0123456789012345678", 20);

You can't assign to arrays - that's just the way it is.
You could use strncpy in main, or get an assignable array with std::array<char, 20>, but if there was a need to do this by hand, I would add constructors (and hide the implementation details) in order to keep things safe:
class _str {
public:
typedef char base[20];
_str() { data[0] = 0; }
_str(const base& in) { strncpy(data, in, 20); }
const base& get() const { return data; }
private:
base data;
};
and then you can
AString astring;
_str str1 = "01234567890123456789"; // Fine
astring.Add(str1);
std::cout << str1.get();
_str str2 = "012345678901234567890"; // Compilation error
_str str3 = "0"; // Compilation error

Related

How to convert my string into array of chars

Here is a problem. When I try to convert it by using strncpy_s, array has some type of "trash data" from memory in the end of it. Even when I fill buffer with "\0". How to convert it clear?
typedef class Ryadok {
private:
int LengthOf = 0;
char text[20];
string* address;
public:
Ryadok(string strin) {
this->text[0] = '\0';
memset(text, '\0', sizeof(text));
strncpy_s(text, strin.c_str(), sizeof(text) - 1);
this->address = &strin;
for (int i = 0; i < sizeof(strin); i++) {
cout << this->text[i];
}
}
~Ryadok() {
}
}*cPtr;
int main()
{
Ryadok example("sdsdfsdf");
}
The idea to use c_str() function to convert the std::string to a a-string. Then we can simply call strcpy() function to copu the c-string into char array
std::string s = "Hello World!";
char cstr[s.size() + 1];
strcpy(cstr, s.c_str()); // or pass &s[0]
std::cout << cstr << '\n';
return 0;
When using the strncpy_s function you tell it to copy as many chars as will fit into your buffer "text". Since the string you create the "example" instance with is shorter, the copy function will keep going after the end of the actual string.
That is where your garbage comes from. Even worse you risk a Segmentation Fault this way. Your code might access parts of the RAM it is not allowed to read from. That will cause it to crash.
You are right though to copy the data pointed to by the return of c_str(). The pointer returned by c_str() points to data that belongs to the std::string object and might be changed or even invalidated by that object. (Read more here)
Here's a modified version of your code that should avoid the garbage:
typedef class Ryadok {
private:
int LengthOf = 0;
char text[20];
string* address;
public:
Ryadok(string strin) {
this->text[0] = '\0';
memset(text, '\0', sizeof(text));
if(strin.length()+1 <= sizeof(text)) {
strncpy_s(text, strin.c_str(), strin.length()+1);
} else {
//some error handling needed since our buffer is too small
}
this->address = &strin;
for (int i = 0; i < sizeof(strin); i++) {
cout << this->text[i];
}
}
~Ryadok() {
}
}*cPtr;
int main()
{
Ryadok example("sdsdfsdf");
}

Why does my char* copier return different things?

Writing a simple string copier and testing it in the main() fucntion. What's odd is that sometimes the program returns
"HelloHello"
like it should, but maybe every third time I run it, the program prints out:
"Hello!Hello!▌▌▌▌▌▌▌▌▌▌▒"UòB╚"
Why is the tail of garbage data only sometimes being added to the end of my second string?
#include <iostream>
using namespace std;
int strlength(const char* c)
{
int size = 0;
while (*c) {
++c;
++size;
}
return size;
}
char* mystrdup(const char* c)
{
int size = strlength(c);
char* result = new char;
copy(c, c + size, result);
return result;
}
void print_array(const char* c)
{
int size = strlength(c);
while (*c) {
cout << *c;
++c;
}
}
int main()
{
char test[] = "Hello!";
char* res = mystrdup(test);
print_array(test);
print_array(res);
}
The program has undefined behavior because you are allocating not enough memory for the result string.
char* mystrdup(const char* c)
{
int size = strlength(c);
char* result = new char;
^^^^^^^^^^^^^^^^^^^^^^^
copy(c, c + size, result);
return result;
}
Moreover you are not copying the terminating zero to the result string.
At least the two functions strlength and mystrdup can look the following way
size_t strlength( const char *s )
{
size_t size = 0;
while ( s[size] ) ++size;
return size;
}
char * mystrdup( const char *s )
{
size_t size = strlength( s ) + 1;
char *result = new char[size];
copy( s, s + size, result );
return result;
}
Of course instead of the standard algorithm std::copy you could use the standard C function strcpy declared in the header <cstring>.
strcpy( result, s );
And do not forget to delete the allocated array.
char* res = mystrdup(test);
//…
delete [] res;
Pay attention to that the function print_array does not use the variable size. There is no need to output a C-string character by character.
The function could be defined like
std::ostream & print_array( const char *s, std::ostream &os = std::cout )
{
return os << s;
}
And at last the identifier c is usually used with single objects of the type char. If you deal with a string then it is better to use the identifier s.
You have multiple bugs in your code. You allocate wrong memory (char instead of char array). You don't delete the memory. Stop using C-string and use std::string
#include <iostream>
#include <string>
using std::cout;
void print_array(const char* c)
{
while (*c) {
cout << *c;
++c;
}
}
int main()
{
std::string = "Hello!";
std::string res = test;
print_array(test.c_str());
print_array(res.c_str());
}
In strcpy you need to create a char size.
char* mystrdup(const char* c)
{
int size = strlength(c);
char* result = new char[size];
copy(c, c + size, result);
return result;
}

I am having trouble getting 3 strings from the user using cin.getline() and using a pointer array to store them

I am getting an exception thrown at my cin.getline. I am new to c++, I am sorry if the code is really bad.
The GetInput function should do the following:
This function accepts three lines of user-input text and stores the entered lines as three individual strings. Use a pointer array to store the strings. The function should ask the user to enter in three lines of data. The function will store the information in the pointer array. (cin.getline()). Should be allocated in inside GetInput and should be exactly the correct size for each string.
int main() {
char* str[2];
GetInput(str);
}
void GetInput(char* ptr[]) {
for (int i = 0; i < 3; i++) {
cout << "Enter a string: ";
cin.getline(ptr[i], strlen(ptr[i]), '\n');
}
}
It's unfortunate that the assignment demands pointers since this would have been easy using std::vector<std::string>. If you do need to use pointers, make use of smart pointers, like std::unique_ptr. They will delete/delete[] the raw pointer they own when they are destroyed (go out of scope) and will therefor help preventing memory leaks. So, using arrays and pointers, this is one way it could be done.
#include <iostream>
#include <array> // std::array
#include <cstring> // std::memcpy
#include <memory> // std::unique_ptr
constexpr size_t max_line_length = 256;
// convenience alias
using CStrPtr = std::unique_ptr<char[]>;
void GetInput(std::array<CStrPtr, 3>& ptrs) {
char buf[max_line_length]; // temporary buffer when reading a line
// loop through the 3 pointers in the array
for(CStrPtr& cptr : ptrs) {
std::cout << "Enter a string: ";
std::cin.getline(buf, max_line_length);
size_t len = std::strlen(buf) + 1; // +1 to make place for the string terminator, '\0'
// create a new pointer to a char[] with space for the string
// and assign it to the pointer in the array
cptr = std::make_unique<char[]>(len);
// copy the string in buf into the space the raw pointer held by cptr now points to
std::memcpy(cptr.get(), buf, len);
}
}
int main() {
std::array<CStrPtr, 3> strs;
GetInput(strs);
for(const CStrPtr& cptr : strs) {
std::cout << cptr.get() << "\n";
}
}
In case you're not allowed to use the standard smart pointers, you could create your own smart pointer or a simple string class. Here's a version of the above but with a simple string class:
#include <iostream>
#include <array> // std::array
#include <cstring> // std::strlen
#include <algorithm> // std::copy
constexpr size_t max_line_length = 256;
class cstring {
char* m_mem; // the precious pointer
public:
cstring() : // default constructor
m_mem(new char[1]) // make place for the string terminator
{
m_mem[0] = '\0'; // the string terminator
}
cstring(char const* buf) : // converting constructor
m_mem{}
{
// allocate memory
size_t len = std::strlen(buf) + 1;
m_mem = new char[len];
// and copy buf to m_mem
std::copy(buf, buf+len, m_mem);
}
cstring(const cstring& o) : // copy ctor
cstring(o.m_mem) // delegate to converting ctor
{}
cstring(cstring&& o) : // move ctor
// copy the pointer from o and set o:s pointer to nullptr
m_mem(std::exchange(o.m_mem, nullptr))
{}
cstring& operator=(const cstring& o) { // copy assignment
*this = cstring(o); // using copy ctor + move assignment
return *this;
}
cstring& operator=(cstring&& o) { // move assignment
// swap pointers: let o destroy our old pointer for us
std::swap(m_mem, o.m_mem);
return *this;
}
~cstring() { delete[] m_mem; } // destructor
// user-defined conversions
operator char const* () const { return m_mem; }
operator char* () { return m_mem; }
};
void GetInput(std::array<cstring, 3>& ptrs) {
char buf[max_line_length]; // temporary buffer when reading a line
// loop through the 3 pointers in the array
for(cstring& cptr : ptrs) {
std::cout << "Enter a string: ";
std::cin.getline(buf, max_line_length);
// create a new cstring and assign it to the cstring in the array
cptr = cstring(buf);
}
}
int main() {
std::array<cstring, 3> strs;
GetInput(strs);
for(const cstring& cptr : strs) {
// cptr will here use the user-defined conversion to "char const*"
// for which there's a standard operator<< defined
std::cout << cptr << "\n";
}
}
#include <iostream>
#include <string>
using namespace std;
void getLine(string ptr[]) {
for (int index = 0; index < 3; ++index) {
string line;
getline(cin, line);
ptr[index] = line;
}
}
int main() {
string ptr[3];
getLine(ptr);
for (int index = 0; index < 3; ++index) {
cout << ptr[index] << endl;
}
}
There were several tings wrong with your code. First, you didn't allocate enough space for your array. Your length should be the length of the array, not the index of the last element. So if it's 3 strings, it's [3].
Next, I wouldn't use char * but instead use the string class. See my code.
Third, your strlen stuff -- well, you have an array of pointers to random places in memory, or maybe an array of null pointers. Either way, it's wrong.
You COULD have done something like this:
char myStrings[3][1000];
And then in your getline, use 1000 as the maximum length (maybe 999 -- I don't use getline in the form you did). This would allocate 3 character arrays of 1000 bytes each.
But the strlen thing just flat won't work because you don't start with any strings to strlen from.

pass array of strings to a function which takes const char**

I have a function which takes an array of const char** as a parameter
void Foo(const char** bar);
I can pass an array of const char * to it
const char *bar[2];
bar[0] = "test";
bar[1] = "me";
Foo(bar); // works fine
I want to do the same when 'bar' is std::string array instead of const char *
std::string bar[2];
bar[0] = "test";
bar[1] = "me";
Foo(bar); // cannot convert argument 1 from 'std::string [1]' to 'const char **'
I know the way convert std::string to const char *. Is there any way I can do it in the above case
Not sure what you're trying to achieve exactly but here
// Example program
#include <iostream>
#include <string>
void Foo(const char** bar, int num) {
while(num > 0) {
std::cout << bar[--num] << std::endl << std::flush;
}
}
const char** toCharArray(std::string* arr, int num) {
// If we ever alloc with new with have to delete
const char** buffer = new const char*[num];
for(int i = 0; i < num; i++) {
buffer[i] = arr[i].c_str();
}
return buffer;
}
int main()
{
std::string bar[2];
bar[0] = "test";
bar[1] = "me";
// Capture the result
const char** charBar = toCharArray(bar, 2);
Foo(charBar, 2);
// So we can free it later
delete[] charBar;
}
Arrays in memory do not have a length or size member like other languages so we pass in the size via function arguments. Second since we want to pass in an array of strings and get an array of chars out of it, we'll need to construct another array dynamically. Only way to do this is to use new. This stores the chars on the heap instead of the stack so when the function toCharArray finishes, the data will live on. So we store the result in charBar so that we can delete[] the array later.
you cannot do it directly
auto arr = std::vector<const char*>();
auto s1 = std::string("test");
auto s2 = std::string("me");
arr.push_back(s1.c_str());
arr.push_back(s2.c_str());
Foo(arr.data());

Storing strings

I'm trying to write a code which stores strings in an array. I'm trying to do it with char* but I couldn't achieve. I search the net but couldn't find an answer. I've tried the code below, but it didn't compile.I use string stream because at some point I need to concatenate a string with an integer.
stringstream asd;
asd<<"my name is"<<5;
string s = asd.str();
char *s1 = s;
> I'm trying to write a code which stores strings in an array.
Well, first you'll need an arary of strings. I don't like using naked arrays, so I use std::vector:
std::vector<std::string> myStrings;
But, I understand you have to use an array, so we'll use an array instead:
// I hope 20 is enough, but not too many.
std::string myStrings[20];
int j = 0;
> I use string stream because ...
Okay, we'll use stringstream:
std::stringstream s;
s << "Hello, Agent " << 99;
//myStrings.push_back(s.str()); // How *I* would have done it.
myStrings[j++] = s.str(); // How *you* have to do it.
That gets us one string, but you want an array of them:
for(int i = 3; i < 11; i+=2) {
s.str(""); // clear out old value
s << i << " is a" << (i==9?" very ":"n ") << "odd prime.";
//myStrings.push_back(s.str());
myStrings[j++] = s.str();
}
Now you have an array of strings.
Complete, tested program:
#include <sstream>
#include <iostream>
int main () {
// I hope 20 is enough, but not too many.
std::string myStrings[20];
int j = 0;
std::stringstream s;
s << "Hello, Agent " << 99;
//myStrings.push_back(s.str()); // How *I* would have done it.
myStrings[j++] = s.str(); // How *you* have to do it.
for(int i = 3; i < 11; i+=2) {
s.str(""); // clear out old value
s << i << " is a" << (i==9?" very ":"n ") << "odd prime.";
//myStrings.push_back(s.str());
myStrings[j++] = s.str();
}
// Now we have an array of strings, what to do with them?
// Let's print them.
for(j = 0; j < 5; j++) {
std::cout << myStrings[j] << "\n";
}
}
How about something like this?
vector<string> string_array;
stringstream asd;
asd<<"my name is"<<5;
string_array.push_back(asd.str());
char *s1 = s;
Is illegal. You either need:
const char *s1 = s.c_str();
if you're not set on char*, or you'll need to allocate a new char* and use strcpy to copy the contents from the string.
Just change your code to
char const* s1 = s.c_str();
because a pointer to char can't store a string object, only a pointer to char, which is what c_str() returns.
I wouldn't use the char * directly. I would wrap it in something like the template below. You can override the operators you need to do any more operations (example, I would make data a private member, and override the operators to make the data print out cleanly). I did the assignment operator just to demonstrate how clean that could make code.
#include "MainWindow.h"
#include <stdio.h>
using namespace std;
template<size_t size>
class SaferChar
{
public:
SaferChar & operator=(string const & other)
{
strncpy(data, other.c_str(), size);
return *this;
}
char data[size];
};
int main(int argc, char *argv[])
{
SaferChar<10> safeChar;
std::string String("Testing");
safeChar = String.c_str();
printf("%s\n", safeChar.data);
return 0;
}