I have a question concerning this code from Thinking in c++ book , this is a tiny c style library for learning the process of memory allocation , what does it mean to write
int startBytes = s->next * s->size;
in this code , what does this multiplication mean ?
//: C04:CLib.h
// Header file for a C-like library
// An array-like entity created at runtime
typedef struct CStashTag {
int size;
// Size of each space
int quantity; // Number of storage spaces
int next;
// Next empty space
// Dynamically allocated array of bytes:
unsigned char* storage;
} CStash;
void initialize(CStash* s, int size);
void cleanup(CStash* s);
int add(CStash* s, const void* element);
void* fetch(CStash* s, int index);
int count(CStash* s);
void inflate(CStash* s, int increase);
///:~
//: C04:CLib.cpp {O}
// Implementation of example C-like library
// Declare structure and functions:
#include "CLib.h"
#include <iostream>
#include <cassert>
using namespace std;
/ Quantity of elements to add
// when increasing storage:
const int increment = 100;
void initialize(CStash* s, int sz) {
s->size = sz;
s->quantity = 0;
s->storage = 0;
s->next = 0;
}
int add(CStash* s, const void* element) {
if(s->next >= s->quantity) //Enough space left?
inflate(s, increment);
// Copy element into storage,
// starting at next empty space:
int startBytes = s->next * s->size;
unsigned char* e = (unsigned char*)element;
for(int i = 0; i < s->size; i++)
s->storage[startBytes + i] = e[i];
s->next++;
return(s->next - 1); // Index number
}
void* fetch(CStash* s, int index) {
// Check index boundaries:
assert(0 <= index);
if(index >= s->next)
return 0; // To indicate the end
// Produce pointer to desired element:
return &(s->storage[index * s->size]);
}
int count(CStash* s) {
return s->next; // Elements in CStash
}
void inflate(CStash* s, int increase) {
assert(increase > 0);
int newQuantity = s->quantity + increase;
int newBytes = newQuantity * s->size;
int oldBytes = s->quantity * s->size;
unsigned char* b = new unsigned char[newBytes];
for(int i = 0; i < oldBytes; i++)
b[i] = s->storage[i]; // Copy old to new
delete [](s->storage); // Old storage
s->storage = b; // Point to new memory
s->quantity = newQuantity;
}
void cleanup(CStash* s) {
if(s->storage != 0) {
cout << "freeing storage" << endl;
delete []s->storage;
}
} ///:~
//: C04:CLibTest.cpp
//{L} CLib
// Test the C-like library
#include "CLib.h"
#include <fstream>
#include <iostream>
#include <string>
#include <cassert>
using namespace std;
int main() {
// Define variables at the beginning
// of the block, as in C:
CStash intStash, stringStash;
int i;
char* cp;
ifstream in;
string line;
const int bufsize = 80;
// Now remember to initialize the variables:
initialize(&intStash, sizeof(int));
for(i = 0; i < 100; i++)
add(&intStash, &i);
for(i = 0; i < count(&intStash); i++)
cout << "fetch(&intStash, " << i << ") = "
<< *(int*)fetch(&intStash, i)
<< endl;
// Holds 80-character strings:
initialize(&stringStash, sizeof(char)*bufsize);
in.open("CLibTest.cpp");
assert(in);
while(getline(in, line))
add(&stringStash, line.c_str());
i = 0;
while((cp = (char*)fetch(&stringStash,i++))!=0)
cout << "fetch(&stringStash, " << i << ") = "
<< cp << endl;
cleanup(&intStash);
cleanup(&stringStash);
} ///:~
It looks like it's getting the next free location by multiplying the size of the object by the index number of the next available space. So if the next space is 10, and the object size is 10, it will start allocating at byte index 100.
It is a straight multiplication of the values of next and size, I'm going to guess it's calculating an offset somewhere. From looking at the code, size is set by the sz parameter of the initialize function.
Related
When I access array elements through unique_ptr, a segfault occurs,Through vs debugging, I found that the type and data of std::unique_ptr<T[]> p is strange,I think it should be an array, but it looks like a string,No matter how many elements I push, the data of p points to "to", and other elements cannot be seen.
code
#include <memory>
#include <string>
#include <assert.h>
#include<vector>
#include<iostream>
#include <stack>
#include <string>
#include <sstream>
template <typename T>
class FixedCapacityStockOfStrings {
public:
FixedCapacityStockOfStrings(const int cap) {
p = std::make_unique<T[]>(cap);
MAX = cap;
}
bool isEmpty() {
return N == 0;
}
size_t const size() { return N; }
void push(T& item){
//assert(N < MAX - 1);
if (N == MAX-1) resize(2 * MAX);
p[N++] = item;
}
T pop() {
assert(N > 0);
T item = p[--N];
p[N] = nullptr;//Segmentation fault is here
if ( N <= MAX / 4) resize(MAX / 2);
return item;
}
size_t max() const { return MAX; }
void clear() {
N = 0;
}
private:
void resize(int max) {
auto t = std::make_unique<T[]>(max);
for (int i = 0; i < N; i++) {
t[i] = p[i];
}
p.reset();
p = std::move(t);
MAX = max;
}
std::unique_ptr<T[]> p;
size_t N,MAX;
};
int main() {
FixedCapacityStockOfStrings<std::string> s(100);
std::string line,item;
while (std::getline(std::cin, line)) {
std::istringstream items(line);
while (items >> item) {
if (item != "-")
s.push(item);
else if (!s.isEmpty()) std::cout << s.pop() << " ";
}
std::cout << "(" << s.size() << " left on stack)" << " max stack : " << s.max() << std::endl;
s.clear();
}
}
Note that p[N] has type std::string& for T = std::string, so what
p[N] = nullptr;
does is call std::string::operator=(const char*) with parameter nullptr. This is not a parameter you're allowed to pass to this assignment operator; it expects a 0-terminated string.
Edit: Improved based on suggestion by #Remy Lebeau
You should go with
p[N] = T{};
instead.
You forgot to initialize N in the constructor, so it is a garbage value and reading it is undefined behavior.
p contains an array of std::string. When you assign p[N] = nullptr, you assign a C string to std::string. C string is a pointer to a null-terminated character array and nullptr is not a valid C string.
In the statement p[N] = nullptr;, you are assigning a nullptr to a std::string, which is Undefined Behavior.
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'm currently building a library In C++. I have met this problem few days ago and I'm unable to fix it. I have shorten the code so it can be seen easier.
Below is my code:
class String
{
private:
mutable char* v;
mutable int l = 0;
public:
String()
{
l++;
v = new char[1];
*v = '\0';
}
String(const char* value)
{
int length = 0;
while (value[length])
length++;
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
}
String(const String& value)
{
int length = value.len();
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
}
int len() const
{
return l - 1;
}
char* val() const
{
return v;
}
char* operator=(const char* value) const
{
delete[] v;
int length = 0;
while (value[length])
length++;
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
return v;
}
char* operator=(const String& value) const
{
delete[] v;
int length = value.len();
l = length + 1;
v = new char[l];
for (int i = 0; i < length; i++)
v[i] = value[i];
v[l - 1] = '\0';
return v;
}
char operator[](const int& index) const
{
return v[index];
}
};
class StringArray
{
private:
union ArrayDef
{
public:
mutable String stringV;
mutable int intV;
ArrayDef()
{
}
ArrayDef(const String& value)
: stringV(value)
{
}
ArrayDef(const int& value)
: intV(value)
{
}
ArrayDef(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
}
String operator=(const String& value) const
{
stringV = value;
return stringV;
}
int operator=(const int& value) const
{
intV = value;
return intV;
}
ArrayDef operator=(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
return *this;
}
};
mutable ArrayDef* arrdef;
mutable int arrLen = 0;
public:
StringArray()
{
}
void add(const ArrayDef& value) const
{
ArrayDef temp[arrLen + 1];
for (int i = 0; i < arrLen; i++)
temp[i] = arrdef[i];
temp[arrLen] = value;
arrLen++;
delete[] arrdef;
arrdef = new ArrayDef[arrLen];
for (int i = 0; i < arrLen; i++)
arrdef[i] = temp[i];
}
int len() const
{
return arrLen;
}
ArrayDef val(const int& index) const
{
return arrdef[index];
}
};
And my driver code:
#include <iostream>
int main()
{
StringArray arr;
arr.add(String("Hello"));
arr.add(String("World"));
std::cout << "Length of the array: " << arr.len() << std::endl;
int indexOfString = 1;
int indexOfCharacter = 2;
char s = arr.val(indexOfString).stringV[indexOfCharacter];
std::cout << "arr[" << indexOfString << "][" << indexOfCharacter << "]: " << s << std::endl;
}
I have created two class, that is, String and StringArray class.
For String class, I need to always add a null character after the char pointer array for safety issue.
For StringArray class, I uses union because it's actually an array for multiple types.
It can be successfully compiled but it output some random character and it is different every time I run it.
Any answers will be appreciated, and please tell me why and how it don't works. Thank you.
From,
HaiQin.
This code is just a collection of antipatters that makes it difficult to study. What is the reason of making the internal data mutable? Why do you need to play with length and l where sometimes it is the length of the string, sometimes it is the size of array? The operator operator= returns char* which is a bad practice. Using const int& index as a parameter is a strange choice. You allocate arrays multiple times but you have no destructor that frees the memory.
Here your assignment operator returns a value, not reference!
ArrayDef operator=(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
return *this;
}
Next comes even more dangerous practice:
// Recollect that this is a union
ArrayDef(const ArrayDef& value)
{
intV = value.intV;
stringV = value.stringV;
}
You are assigning both fields of the union at the same time! Did you mean struct?
Try to fix that. Start with changing union to structure.
One of the things that certainly will not work is the ArrayDef copy constructor and operator=(const ArrayDef & value). This is because you may only use the active value in the union, not both at the same time. This is usually solved by using a tagged union. Is there a reason you cannot use the Standard Template Library?
#include <iostream>
#include <string>
#include <vector>
int main() {
std::vector<std::string> arr;
arr.push_back(std::string("Hello"));
arr.push_back(std::string("World"));
std::cout << "Length of the array: " << arr.size() << std::endl;
constexpr int indexOfString = 1; // second string - starting from 0!
constexpr int indexOfCharacter = 2; // third character
char s = arr.at(indexOfString).c_str()[indexOfCharacter]; // using interfaces closest to the original
std::cout << "arr[" << indexOfString << "][" << indexOfCharacter << "]: " << s << std::endl;
}
I have a class foo like this:
class foo
{
private:
int* a;
public:
foo()
{
a = new int[4];
cout << "a" << endl;
}
};
When I create new object named foo1 and then I debug, after the allocating line, it yields the result: a 0x005a4580 {-842150451}.
But when I replace all int-s by char-s in class definition, it yields an undesired result:
a 0x005694a0 "ÍÍÍÍýýýý\x6ŒÒ•\x5Ÿ"
that the size of a is now greater than 4.
I dont know what happened. Could you please give me an explanation?
Full code:
#include <iostream>
#include <string>
using namespace std;
class String
{
public:
String(char* data)
{
setSize(0);
while (*(data + size) != '\0')
size++;
this->data = new char[size];
//need to allocate memory for 'data' pointer because 'data' pointer is now on the stack and the data must be on the heap
memcpy(this->data, data, size * sizeof(char));
}
void operator=(String rhs)
{
if (this->data != NULL)
delete[] this->data, data = NULL;
this->data = new char[rhs.getSize()]; //allocate
memcpy(this->data, data, size * sizeof(char));
}
int getSize()
{
setSize(0);
while (*(data + size))
size++;
return size;
}
void setSize(int size)
{
this->size = size;
}
void display()
{
for (int i = 0; i < size; i++)
cout << *(data + i);
}
~String()
{
if (data != NULL)
delete[] data, data = NULL;
}
private:
char* data;
int size;
};
void main()
{
String a("abcd");
String b("1");
a.display();
cout << endl;
cout << b.getSize() << endl;
a = b;
cout << a.getSize() << endl;
system("pause");
}
Whatever you're using to look at a doesn't know how much you allocated. It just knows the type.
In the first version it sees int *, so it shows a single int.
In the second version it sees char *, so it assumes it's a C string and prints whatever is in memory up to the first '\0' byte.
Header File
#pragma once
#ifndef PLAYERDATA_H
#define PLAYERDATA_H
#include <string>
using namespace std;
class PlayerData
{
private:
Private member variables
static const int SIZE = 10;
string name; //Player Name
int jnum; //Jersey Number
string team; //Player Team
string position; //Player position
int points[SIZE]; // Array of points for last 10 games
int rebounds[SIZE]; // Array of rebounds for last 10 games
int assist[SIZE]; // Array of assist for last 10 games
double ap = 0.0; // Average number of points
double ar = 0.0; // Average number of rebounds
double aa = 0.0; // Average number of assits
public:
Constructor to initialize data if no data is passed
// Constructor #1
PlayerData()
{
jnum = 0;
name = "";
team = "";
position = "";
for (int i = 0; i < SIZE; i++)
{
points[SIZE] = 0;
rebounds[SIZE] = 0;
assist[SIZE] = 0;
}
}
// Constructor #2
Constructor to accept parameter. Collects jersey number, name, team name, position, array of points for last 10 games, array of rebounds for last 10 games, array of assist for last 10 games.
PlayerData( int jn, string n, string t, string pos, int p[SIZE], int r[SIZE], int a[SIZE])
{
jnum = jn;
name = n;
team = t;
position = pos;
for (int i = 0; i < SIZE; i++)
{
points[SIZE] = p[SIZE];
rebounds[SIZE] = r[SIZE];
assist[SIZE] = a[SIZE];
}
}
// Mutator Function
void setJersery(int jn)
{
jnum = jn;
}
void setName(string n)
{
name = n;
}
void setTeam(string t)
{
team = t;
}
void setPosition(string pos)
{
position = pos;
}
void setPoints(int p[SIZE])
{
for (int z = 0; z < SIZE; z++)
{
points[SIZE] = p[SIZE];
}
}
void setRebounds(int r[SIZE])
{
for (int z = 0; z < SIZE; z++)
{
rebounds[SIZE] = r[SIZE];
}
}
void setAssist(int a[SIZE])
{
for (int z = 0; z < SIZE; z++)
{
assist[SIZE] = a[SIZE];
}
}
// Acessor methods
string getName()
{
return name;
}
int getJersey()
{
return jnum;
}
string getTeam()
{
return team;
}
string getPosition()
{
return position;
}
int getPoints()
{
return points[SIZE];
}
int getRebounds()
{
return rebounds[SIZE];
}
int getAssist()
{
return assist[SIZE];
}
/*
double averageP(int p[], const int SIZE);
double averageR(int r[], const int SIZE);
double averageA(int a[], const int SIZE);
*/
void averageP(int p[], const int SIZE);
void averageR(int r[], const int SIZE);
void averageA(int a[], const int SIZE);
double getAP()
{
return ap;
}
double getAR()
{
return ar;
}
double getAA()
{
return aa;
}
};
#endif // !PLAYERDATA_H
Calculates average points,rebounds, assist from the arrays that were passed.
PlayerData.cpp
#include "PlayerData.h"
using namespace std;
// Calculate average points
void PlayerData::averageP(int p[], const int s)
{
for (int c = 0; c < s; c++)
{
ap += p[c];
}
ap /= s;
//return ap;
}
// Calculate average rebounds
void PlayerData::averageR(int r[], const int s)
{
for (int c = 0; c < s; c++)
{
ar += r[c];
}
ar /= s;
//return ar;
}
// Calculate average assist
void PlayerData::averageA(int a[], const int s)
{
for (int c = 0; c < s; c++)
{
aa += a[c];
}
aa /= s;
//return aa;
}
Main
#include <iostream>
#include <iomanip>
#include "PlayerData.h"
using namespace std;
int main()
{
const int SIZE = 10;
int points[SIZE] = { 10,10,10,10,10,10,10,10,10,10 };
int assist[SIZE] = { 2,2,2,2,2,2,2,2,2,2, };
int rebounds[SIZE] = { 3,3,3,3,3,3,3,3,3,3 };
Here is where the problem occurs. The compiler marks under the 6 as if the int is not part of the arguments for the constructor. I'm not sure why it is doing this. I receive this message "No instance of constructor "PlayerData::PlayerData" matches the argument list."
PlayerData player1(6, "Jimmy Butler", "Chicago Bulls", "Forward", points[SIZE], rebounds[SIZE], assist[SIZE]);
getchar();
return 0;
}
Constructor requires an array of integers and in main you are passing a pointer to int. If you want to pass the whole array you should delete the [SIZE] because that is translated as (if SIZE is 5 for example) "give me the 6th element of 5 element array".
Try calling it like this.
PlayerData player1(6, "Jimmy Butler", "Chicago Bulls", "Forward", points, rebounds, assist);