So i have problem Im making class. In this class im supposed to have array of some data. I have no problem with assigning and creating new data with for example x2.NewAccount("123456" , 1000); this works fine the problem is when im trying to create data with string that is addressed to some variable. I know something about deep copying but I have no clue how to programm = operator in my case + i thought that strcpy but thats not working aswell.
PS: Its a school program so please dont judge me for not using headers and using bunch of includes that Im not using in code. Its made by my school and Im not allowed to change them + add them (I know that with string from c++ it would be much easier.).
Thanks for any help.
#ifndef __PROGTEST__
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cassert>
#include <cctype>
#include <cmath>
#include <iostream>
#include <iomanip>
#include <sstream>
using namespace std;
#endif /* __PROGTEST__ */
struct data_history{
int money = 0;
bool Income;
const char * UnStr;
const char * to_from;
};
struct client{
const char * accID;
int Balance;
int def_bal;
data_history * history;
int in_index = 0;
int in_cap = 10;
friend ostream &operator << (ostream &output , client p){
output << p.accID << ":" << endl << " " << p.def_bal << endl;
for (int i = 0 ; i < p.in_index ; i++){
if (p.history[i].Income == false)
output << " - " << abs(p.history[i].money) << ", to: " << p.history[i].to_from << ", sign: " << p.history[i].UnStr << endl;
else
output << " + " <<abs(p.history[i].money) << ", from: " << p.history[i].to_from << ", sign: " << p.history[i].UnStr << endl;
}
output << " = " << p.Balance << endl;
return output;
}
};
class CBank
{
public:
int cap = 10;
int index = 0;
client * database;
~CBank(){
for (int i = 0 ; i < index ; i++)
delete[] database[i].history;
delete[]database;
}
CBank(){
database = new client[cap];
}
bool NewAccount ( const char * accID, int initialBalance ){
for(int i = 0 ; i < index ; i ++)
if (accID == database[i].accID) {
return false;
}
//strcpy (database[index].accID , accID ); // Im getting errors while compileing (cuz I was using const char * for database.accID when i chenged it i got program crash.
database[index].accID = accID;
database[index].Balance = initialBalance;
database[index].def_bal = initialBalance;
database[index].in_cap = 10;
database[index].history = new data_history[database[index].in_cap];
index ++;
return true;
}
client Account (const char * accID ){
const char * input =accID;
for (int i = 0 ; i < index ; i++){
if (database[i].accID == input )
return database[i];
}
throw "error";
}
void print (){
for (int i = 0 ; i < index ; i ++) {
cout << endl;
cout << i << " = "<< " ID = " << database[i].accID << " | Balance = " << database[i].Balance << endl;
cout << "===Account history ===\n\n";
for (int y = 0 ; y < database[i].in_index; y++) {
cout << "Was it for him? : " << boolalpha << database[i].history[y].Income
<< "\nHow much : " << database[i].history[y].money << "\nUnique string : "
<< database[i].history[y].UnStr << "\nfrom/to: " << database[i].history[y].to_from << endl << endl;
}
}
}
private:
};
#ifndef __PROGTEST__
int main ( void )
{
char accCpy[100], debCpy[100], credCpy[100], signCpy[100];
CBank x2;
strncpy ( accCpy, "123456", sizeof ( accCpy ) );
assert ( x2 . NewAccount ( accCpy, 1000 ) );
x2 . print();
cout << "\n\n\n\n";
strncpy ( accCpy, "987654", sizeof ( accCpy ) );
assert ( x2 . NewAccount ( "987654", -500 ) );
x2 . print();
}
#endif /* __PROGTEST__ */
When using database[index].accID = accID; you are only doing a shallow copy (and relying on the caller to keep this memory valid as the pointer may be accessed).
You've correctly identified that you need to preform a deep-copy, but client::accID is just a pointer, but you may not copy into it until you initialize it to point to some memory.
One way of doing this is to dynamically allocate and manage client::accID similar to how you are dynamically allocating and managing client::history.
Instead of strcpy (database[index].accID , accID ); or database[index].accID = accID;, try:
size_t bufsize = strlen(accID) + 1;
char *buf = new char[bufsize];
memcpy(buf, accID, bufsize);
database[index].accID = buf;
and in the destructor add:
delete[] database[i].accID
As others have pointed out, this style of C++ programing is very error prone and not looked upon well by the community. This manually memory management can be easily avoided by using the standard library classes. Even after making the changes above, your program will run into undefined behavior if you start copying CBank objects.
Even if you don't have to for your assignment, you should consider trying to rewrite this as an exercise with:
std::string instead of C-strings for accID;
std::vector instead of the data_history array
Also FYI:
if (database[i].accID == input )
Will not do what expect with c-strings...
Related
I have 20byte binary char array. I want to divide into 3 parts: 4byte, 8byte, 8byte. I implemented it like the following. It works but seems I might be able to use buffer stream. I want to know how to use it.
Now
void main()
{
// _data is 20byte binary char array. 0000000000000000000000000000000000000000000001111001110001111111001110000010110000001011101101000000000000000000000000000000000000000000000000000000000000000001
// strA (4 byte)
string strA;
for (std::size_t i = 0; i < 4; ++i) {
strA += bitset<8>(_data.c_str()[i]).to_string();
}
cout << strA << endl; // 00000000000000000000000000000000
// strB (8 byte)
string strB;
for (std::size_t i = 4; i < 12; ++i) {
strB += bitset<8>(_data.c_str()[i]).to_string();
}
cout << strB << endl; // 0000000000000111100111000111111100111000001011000000101110110100
// strC (8 byte)
string strC;
for (std::size_t i = 12; i < 20; ++i) {
strC += bitset<8>(_data.c_str()[i]).to_string();
}
cout << strC << endl; // 0000000000000000000000000000000000000000000000000000000000000001
}
Expectation
I want to implement like this.
void main()
{
stringstream ss = _data;
strA = ss.pop(4);
strB = ss.pop(8);
strC = ss.pop(8);
}
Update 1
Thank you guys. I'm trying all of answers you gave me one by one. I'm newbie in c++ so it takes time to understand it. The following is Anders K's one.
struct S { char four[4]; char eight1[8]; char eight2[8]; };
struct S *p = reinterpret_cast<S*>(&_data);
cout << p->four << endl; // => Output "(" I think I can find way to output
Update 2
It works using string::substr. Thanks Zakir.
int main()
{
// I don't know how to change to string value in smart way..
string str;
for (std::size_t i = 0; i < _data.size(); ++i) {
str += bitset<8>(_data.c_str()[i]).to_string();
}
cout << str << endl; // 0000000000000000000000000000000000000000000001111001110001111111001110000010110000001011101101000000000000000000000000000000000000000000000000000000000000000001
std::string d = str; // Your binary stream goes here
int lenA = (4*8); // First 4 Bytes
int lenB = (8*8); // Second 8 Bytes
int lenC = (8*8); // Last 8 Bytes
std::string strA = d.substr(0, lenA);
std::string strB = d.substr(lenA + 1, lenB - 1);
std::string strC = d.substr(lenA + lenB + 1, lenC - 1);
cout << strA << endl; // 00000000000000000000000000000000
cout << strB << endl; // 000000000000111100111000111111100111000001011000000101110110100
cout << strC << endl; // 000000000000000000000000000000000000000000000000000000000000001
}
Update 3
I got an error when I try Scheff's way. This is my fault and I think I can solve it. And I think I should reconsider about _data's type.
int main
{
const char data = _data;
const char *iter = data;
string strA = pop(iter, 4);
string strB = pop(iter, 8);
string strC = pop(iter, 8);
cout << "strA: '" << strA << "'" << endl;
cout << "strB: '" << strB << "'" << endl;
cout << "strC: '" << strC << "'" << endl;
}
Make Error Message
error: no viable conversion from 'string' (aka 'basic_string<char, char_traits<char>, allocator<char> >') to
'const char'
const char data = _data;
It is not possible to make a new method for std::stringstream. (At least, I would not recommend this.)
Instead, I would suggest to make it a function. The usage would be similar.
#include <bitset>
#include <iostream>
#include <sstream>
#include <string>
using namespace std;
string pop(istream &in, size_t n)
{
string ret;
while (n--) {
unsigned char byte = (unsigned char)in.get();
ret += bitset<8>(byte).to_string();
}
return ret;
}
int main()
{
string data(
"\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa"
"\xbb\xcc\xdd\xee\xff\xde\xad\xbe\xef\x00", 20);
istringstream in; in.str(data);
string strA = pop(in, 4);
string strB = pop(in, 8);
string strC = pop(in, 8);
cout << "strA: '" << strA << "'" << endl;
cout << "strB: '" << strB << "'" << endl;
cout << "strC: '" << strC << "'" << endl;
return 0;
}
Output:
strA: '00010001001000100011001101000100'
strB: '0101010101100110011101111000100010011001101010101011101111001100'
strC: '1101110111101110111111111101111010101101101111101110111100000000'
Note:
Using a std::istream makes it applicable to any stream derived from std::istream.
There is no error handling in pop(). Thus, the returned result of pop() might be wrong if the passed stream isn't good() afterwards.
Btw. I agree with the comments that a std::stream might be "over-engineered". Thus, here the "light-weight" version:
#include <bitset>
#include <iostream>
#include <string>
using namespace std;
string pop(const char *&iter, size_t n)
{
string ret;
while (n--) {
ret += bitset<8>((unsigned char)*iter++).to_string();
}
return ret;
}
int main()
{
const char data[] =
"\x11\x22\x33\x44\x55\x66\x77\x88\x99\xaa"
"\xbb\xcc\xdd\xee\xff\xde\xad\xbe\xef\x00";
const char *iter = data;
string strA = pop(iter, 4);
string strB = pop(iter, 8);
string strC = pop(iter, 8);
cout << "strA: '" << strA << "'" << endl;
cout << "strB: '" << strB << "'" << endl;
cout << "strC: '" << strC << "'" << endl;
return 0;
}
The output is identical like above.
Note:
The usage of char[] and char* is much more sensitive for out-of-bound access. Thus, it has to be used carefully.
I'm not quite sure whether the (unsigned char) cast is necessary. As I have often seen "funny" effects concerning char, int and sign extension, I guess it cannot hurt. (I feel better with it.)
I can propose you a very simple alternative using string::substr
#include <iostream>
#include <string>
using namespace std;
int main ()
{
string _data="00010001001000100011001101000100\
0101010101100110011101111000100010011001101010101011101111001100\
1101110111101110111111111101111010101101101111101110111100000000";
int lenA = (4*8); //First 4 Bytes
int lenB = (8*8); //Second 8 Bytes
int lenC = (16*8); //Last 16 Bytes
string strA = _data.substr(0, lenA - 1);
string strB = _data.substr(lenA, lenB - 1);
string strC = _data.substr(lenB, lenC - 1);
std::cout << "strA: " << strA << endl;
std::cout << "strB: " << strB << endl;
std::cout << "strC: " << strC << endl;
return 0;
}
This is neat and simple but gets your job done!
Demo here
Output:-
strA: 0001000100100010001100110100010
strB: 010101010110011001110111100010001001100110101010101110111100110
strC: 100110011010101010111011110011001101110111101110111111111101111010101101101111101110111100000000
Below is the revised code from yesterday. It crashes on the line delete tempChar in my void function.
I tried using delete[] tempChar; making tempStr in my char* function as a global variable; and removing contents in tempChar first before delete tempChar. Neither worked.
#include <iostream>
using namespace std;
#include <cstring>
char* string_reverse2(const char* string){
cout << "the word to reverse: " << string << endl << endl;
if (string == NULL){
return NULL;
}
int strlength = strlen(string);
char* tempStr = new char[strlength + 1];
tempStr[strlength + 1] = 0;
int index = 0;
for (int i = 0; i <= strlength; i++){
tempStr[index] = string[strlength - 1 - i];
index++;
}
cout << endl;
return tempStr;
}
void string_reverse1(char* string){
char* tempChar = string_reverse2(string);
cout << tempChar << endl;
//delete tempChar;
}
int main(){
string_reverse1("I love Friday!");
return 0;
}
==================================================================================
I take a const char* string as my parameter from my void string_reverse1 function and try to reverse it by copying the value into a newly created char* tempStr. Inside of the for-loop, I'm able to see each value of the const char* string being successfully copied over to tempStr; however, tempStr seems to be empty outside of the for-loop. Can you help me figure out what the problem is?
Thanks a lot!
char* string_reverse2(const char* string) {
cout << "the word to reverse: " << string << endl << endl;
if (string == NULL){
return NULL;
}
int strlength = strlen(string);
char* tempStr = new char[strlength];
for (int i = strlen(string); i >= 0; i--){
tempStr[strlen(string) - i] = string[i];
cout << tempStr[strlen(string) - i];
}
cout << endl << endl;
//tempStr[strlen(string) + 1] = '\0';
cout << "reversed word: " << tempStr << endl;
return tempStr;
}
void string_reverse1(char* string){
const char* temp = string;
char* tempChar = string_reverse2(temp);
cout << tempChar;
}
Well I think you could fix your code following these suggestions (see live sample)
#include <iostream>
#include <cstring>
using namespace std;
char* string_reverse2(const char* string) {
cout << "the word to reverse: " << string << endl << endl;
if (string == NULL){
return NULL;
}
int strlength = strlen(string);
char* tempStr = new char[strlength + 1];
// ^^^
// Ensure the NUL character at end of c-string
tempStr[strlength + 1] = 0;
int index = 0; // The inital index to fill in the reversed charactrs result
for (int i = strlength - 1; i >= 0; --i) {
// ^^ Prefer the prefix increment operator
// to avoid unnecessary copies
tempStr[index] = string[i];
// ^^^^^ Use this index to fill in tempStr[index]
cout << tempStr[index];
++index;
}
cout << endl;
// tempStr[strlength] = 0;
cout << "reversed word: " << tempStr << endl;
return tempStr;
}
void string_reverse1(const char* string) {
char* tempChar = string_reverse2(string);
cout << tempChar << endl;
// Don't forget to finally release the memory allocated in string_reverse2()
delete [] tempChar;
}
int main() {
string_reverse1("Hello");
return 0;
}
Output:
the word to reverse: Hello
olleH
reversed word: olleH
I've been pointing out the necessary fixes in the comments of the code sample above.
For the life of me I can't figure out why the I can't write to a c style string inside of a struct.
College student - can't use string class, haven't learned pointers.
Help? 2 hours at trying to figure this out.
#include <iostream>
using namespace std;
void strCopy(char from[], char to[])
{
for (int i = 0; i < 255; i++)
{
to[i] = from[i];
}
}
struct card
{
char suit[20];
char rank[20];
int cvalue;
char location[20];
};
void printCard(card card)
{
cout << card.rank << " of " << card.suit << endl;
}
int main()
{
// I don't think strCopy()'s the problem, I've used it with my last project.
cout << "Test strCopy()" << endl;
char str1[14] = "abcdefghijklm";
char str2[14];
strCopy(str1, str2);
cout << " " << str2 << endl << endl;
// Now the negative.
card one;
one.cvalue = 2;
strCopy("Somewhere", one.location);
strCopy("Two", one.rank);
strCopy("Hearts", one.suit);
printCard(one);
}
// I don't think strCopy()'s the problem, I've used it with my last
project.
Wrong
for (int i = 0; i < 255; i++)
{
to[i] = from[i];
}
copies 255 characters, however that's not what you meant.
If here :
strCopy(str1, str2);
cout << " " << str2 << endl << endl;
Your're getting "correct" output, then you're just unlucky, since that invokes an undefined behavior, an you're writing off the end of the array.
I'm trying to read names and ages from user, until user inputs "stop". Then just print all these values. Please help me , I'm just the beginner in C++
// Pass.cpp
// Reading names and ages from user and outputting them
#include <iostream>
#include <iomanip>
#include <cstring>
using std::cout;
using std::cin;
using std::endl;
using std::setw;
using std::strcmp;
char** larger(char** arr);
int* larger(int* arr);
void read_data(char*** names, int** ages);
void print_data(char*** names, int** ages);
int main()
{
char** names = new char*[5];
char*** p_names = &names;
int* ages = new int[5];
int** p_ages = &ages;
read_data(p_names,p_ages);
print_data(p_names,p_ages);
}
void read_data(char*** names, int** ages)
{
const char* sent = "stop";
const int MAX = 15;
int count = 0;
char UI[MAX];
cout << "Enter names and ages."
<< endl << "Maximum length of name is " << MAX
<< endl << "When stop enter \"" << sent << "\".";
while (true)
{
cout << endl << "Name: ";
cin.getline(UI,MAX,'\n');
if (!strcmp(UI, sent))
break;
if (count + 1 > sizeof (&ages) / sizeof (&ages[0]))
{
*names = larger(*names);
*ages = larger(*ages);
}
*names[count] = UI;
cout << endl << "Age: ";
cin >> *ages[count++];
}
}
void print_data(char*** names, int** ages)
{
for (int i = 0; i < sizeof(*ages) / sizeof(*ages[0]);i++)
{
cout << endl << setw(10) << "Name: " << *names[i]
<< setw(10) << "Age: " << *ages[i];
}
}
char** larger(char** names)
{
const int size = sizeof(names) / sizeof(*names);
char** new_arr = new char*[2*size];
for (int i = 0; i < size; i++)
new_arr[i] = names[i];
return new_arr;
}
int* larger(int* ages)
{
const int size = sizeof(ages) / sizeof(*ages);
int* new_arr = new int[2 * size];
for (int i = 0; i < size; i++)
new_arr[i] = ages[i];
return new_arr;
}
You are really over complicating things.
Given the original problem:
Write a program that reads a number (an integer) and a name (less than
15 characters) from the keyboard. Design the program so that the data
is done in one function, and the output in another. Store the data in
the main() function. The program should end when zero is entered for
the number. Think about how you are going to pass the data between
functions
The problem wants you to think about passing parameters to functions. A simple solution would be:
#include "stdafx.h"
#include <iostream>
#include <iomanip>
using namespace std;
// Pass in a char array and an integer reference.
// These values will be modified in the function
void read_data(char name[], int& age)
{
cout << endl << "Age: ";
cin >> age;
cin.ignore();
cout << endl << "Name: ";
cin.getline(name, 16);
}
// Pass a const array and an int value
// These values will not be modified
void print_data(char const *name, int age)
{
cout << endl << setw(10) << "Name: " << name
<< setw(10) << "Age: " << age;
}
int main()
{
char name[16];
int age;
cout << "Enter names and ages."
<< endl << "Enter 0 age to quit.";
do {
read_data(name, age);
print_data(name, age);
} while (0 != age)
}
EDIT: Modified per user3290289's comment
EDIT2: Storing data in an array
// Simplify by storing data in a struct (so we don't have to manage 2 arrays)
struct Person {
char name[16];
int age;
};
// Returns how many People were input
int read_data(Person*& arr)
{
int block = 10; // How many persons to allocate at a time
arr = NULL;
int arr_size = 0;
int index = 0;
while (true) {
if (index == arr_size) {
arr_size += block;
arr = (Person *)realloc(arr, arr_size * sizeof(Person)); // Reallocation
// Should check for error here!
}
cout << endl << "Age: ";
cin >> arr[index].age;
cin.ignore();
if (0 == arr[index].age) {
return index;
}
cout << endl << "Name: ";
cin.getline(arr[index++].name, 16);
}
}
void print_data(Person *arr, int count)
{
for (int i = 0; i < count; i++) {
cout << endl << setw(10) << "Name: " << arr[i].name
<< setw(10) << "Age: " << arr[i].age;
}
}
int main()
{
Person *arr;
int count = read_data(arr);
print_data(arr, count);
free(arr); // Free the memory
}
try this:
#include <iostream>
#include <iomanip>
#include <vector>
#include <sstream>
using std::cout;
using std::cin;
using std::endl;
using std::setw;
using std::strcmp;
void read_data(std::vector<std::string> &names, std::vector<int> &ages);
void print_data(std::vector<std::string> &names, std::vector<int> &ages);
int main()
{
std::vector<std::string> names;
std::vector<int> ages;
read_data(names, ages);
print_data(names, ages);
}
void read_data(std::vector<std::string> &names, std::vector<int> &ages)
{
const char* sent = "stop";
cout << "Enter names and ages."
<< endl << "When stop enter \"" << sent << "\".";
while (true)
{
std::string input;
cout << endl << "Name: ";
std::getline(cin, input);
if (!strcmp(input.c_str(), sent))
break;
names.push_back(input);
cout << endl << "Age: ";
std::string age;
std::getline(cin, age);
ages.push_back(atoi(age.c_str()));
}
}
void print_data(std::vector<std::string> &names, std::vector<int> &ages)
{
for (int i = 0; i < names.capacity() ; i++)
{
cout << endl << setw(10) << "Name: " << names.at(i)
<< setw(10) << "Age: " << ages.at(i);
}
}
One problem I see is this if statement:
if (count + 1 > sizeof (&ages) / sizeof (&ages[0]))
&ages is the address of an int**, a pointer, and so it's size is 8 (usually) as that is the size of a pointer type. The function does not know the size of the array, sizeof will only return the correct answer when ages is declared in the same scope.
sizeof(&ages) / sizeof(&ages[0])
will always return 1
I believe one natural solution about this problem is as follows:
create a "std::map" instance. Here std::map would sort the elements according to the age. Here my assumption is after storing the data into the container, you would like to find about a particular student age/smallest/largest and all various manipulation with data.Just storing and printing the data does not make much sense in general.
create a "std::pair" and take the both input from the user into the std::pair "first" and "second" member respectively. Now you can insert this "std::pair" instance value into the above "std::map" object.
While printing, you can now fetch the each element of "std::map" in the form of "std::pair" and then you can display pair "first" and "second" part respectively.
I have implemented the swap via function for two numbers and this works well. However, I am now trying to swap two character strings but i receive the names in the same order. Would anybody know where I am going wrong or what I could do to have the names change positions? here is an example of the code below:
#include "stdafx.h"
#include <cstring>
#include <iostream>
#include <string>
using namespace std;
void swapages (int &age1, int &age2);
void swapname(char *person1, char *person2);
int _tmain(int argc, _TCHAR*argv[])
{
char person1[] = "Alex";
char person2[] = "Toby";
int age1 = 22;
int age2 = 27;
cout << endl << "Person1 is called " << person1;
cout << " and is " << age1 << " years old." << endl;
cout << "Person2 is called " << person2;
cout << " and is " << age2 << " years old." << endl;
swapname(person1,person2);
swapages(age1,age2);
cout << endl << "Swap names..." << endl;
cout << endl << "Person1 is now called " << person1;
cout << " and is " << age1 << " years old." << endl;
cout << "Person2 is now called " << person2;
cout << " and is " << age2 << " years old." << endl;
system("pause");
return 0;
}
void swapages(int &age1, int &age2)
{
int tmp = age2;
age2 = age1;
age1 = tmp;
}
void swapname(char *person1, char *person2)
{
char* temp = person2;
person2 = person1;
person1 = temp;
}
You have tagged this as C++, and you are including the <string> header already, so why not use std:string instead of all those pointers and arrays?
void swapname(string &person1, string &person2)
{
string temp(person2);
person2 = person1;
person1 = temp;
}
int _tmain(int argc, _TCHAR*argv[])
{
string person1 = "Alex";
string person2 = "Toby";
swapname(person1, person2);
}
The problem is that you are trying to swap pointers that are local variables of the function while you need to swap strings. So you need to copy one string into another string. Moreover the strings can have different lengths so the exact swap will be impossible. It could be done without problems if the parameters of the function would be arrays (references to arrays) of the same size. For example
void swap_name( char ( &lhs )[5], char ( &rhs )[5] )
{
char tmp[5];
std::strcpy( tmp, lhs );
std::strcpy( lhs, rhs );
std::strcpy( rhs, tmp );
}
You need to make minor changes to get it to work the way you want it to work.
void swapname(char **person1, char **person2);
.
.
char *person1 = "Alex";
char *person2 = "Toby";
.
.
swapname(&person1, &person2);
.
.
and
void swapname(char **person1, char **person2)
{
char* temp = *person2;
*person2 = *person1;
*person1 = temp;
}
In your code, person1 and person2 are defined as 2 char arrays not char pointer variables, you can not swap them, if you pass the 2 arrays into the swapname function which takes 2 pointers as parameters, it should not even compile.