C++ dynamic array for continuous string input - c++

I need to make a dynamic array in C++, and ask a user to input a name until the user types exit.
It should keep asking for more and more names, recording them to a dynamic string array, then randomly choosing as many names as the user wants from the list.
I should be able to figure out the random numbers part, but the continuous input is giving me issues. I'm not sure how to have the length variable continue changing values.
#include <iostream>
#include <string>
using namespace std;
int main()
{
int length;
string* x;
x = new string[length];
string newName;
for (int i = 0; i < length; i++)
{
cout << "Enter name: (or type exit to continue) " << flush;
cin >> newName;
while (newName != "exit")
{
newName = x[i];
}
}
cout << x[1] << x[2];
int qq;
cin >> qq;
return 0;
}
Any help would be greatly appreciated.

A few errors:
length is never assigned a value
You're overwriting newName with an unassigned value x[i] of the array in newName = x[i];
x is never dynamically reassigned with a new array of different length
Let's consider a solution:
#include <iostream>
#include <string>
using namespace std;
int main()
{
int length = 2; // Assign a default value
int i = 0; // Our insertion point in the array
string* x;
x = new string[length];
string newName;
cout << "Enter name: (or type exit to continue) " << flush;
cin >> newName; // Dear diary, this is my first input
while (newName != "exit")
{
if (i >= length) // If the array is bursting at the seams
{
string* xx = new string[length * 2]; // Twice the size, twice the fun
for (int ii = 0; ii < length; ii++)
{
xx[ii] = x[ii]; // Copy the names from the old array
}
delete [] x; // Delete the old array assigned with `new`
x = xx; // Point `x` to the new array
length *= 2; // Update the array length
}
x[i] = newName; // Phew, finally we can insert
i++; // Increment insertion point
cout << "Enter name: (or type exit to continue) " << flush;
cin >> newName; // Ask for new input at the end so it's always checked
}
cout << x[1] << x[2]; // Print second and third names since array is 0-indexed
int qq;
cin >> qq; // Whatever sorcery this is
return 0;
}
Addressing the errors mentioned:
length is assigned a default value at the start
Reverse the direction to get x[i] = newName;
x is dynamically assigned a new array of exponentially-increasing length
Happy learning!

Related

C++ pointer gets overwritten in for loops

Could someone explain why pointers gets overwritten when variables are declared inside a loop?
For example, given the following snippet, and the user inputs 1 and 2. I would expect that the pNums array contain 2 pointers to 2 integers holding the value 1 and 2 respectively.
But instead, the console prints out 2 and 2;
#include <iostream>
using namespace std;
//Input "1 2"
int main() {
int* pNums[2];
for(int i = 0; i < 2; i++){
int num;
cin >> num;
pNums[i] = (&num);
}
cout << (*pNums[0]) << endl;
cout << (*pNums[1]) << endl;
}
Why is this the case? And how do I get around it? What if, for example, we don't know how many numbers the user will put in, and instead of a for loop, we have a while loop? Until some conditions are met, we want to keep creating new pointers and store them into a pNums vector?
There is only one num, and you are overwriting that. (And then causing Undefined Behavior, but never mind that.)
There are two simple ways to avoid this mistake.
1) Store objects, not pointers:
int nums[2];
for(int i = 0; i < 2; i++){
cin >> nums[i];
}
2) Use dynamic allocation:
int* pNums[2];
for(int i = 0; i < 2; i++){
int *p=new int;
cin >> *p;
pNums[i] = p;
}
The pointers that you are storing in pNums are to two instances of the variable num in the for block. There is one instance of the variable in each for loop iteration and these variables live only until the end of their respective iteration of the for loop body is reached.
Therefore your pointers will be invalid when the for loop exits and so trying to dereference them with e.g. *pNums[0] causes undefined behavior.
Don't store pointer, store values:
#include <iostream>
using namespace std;
//Input "1 2"
int main() {
int pNums[2];
for(int i = 0; i < 2; i++){
int num;
cin >> num;
pNums[i] = num;
}
cout << pNums[0] << endl;
cout << pNums[1] << endl;
}
and if you need a variable number of entries in the array, use std::vector.
for(int i = 0; i < 2; i++){
int num; //< this is num. It lives here.
cin >> num;
pNums[i] = (&num); //< you have taken the address of num (twice!)
}
// here is when 'num' is destroyed (no longer in scope)
// so this is now pointing at something that doesn't exist.
cout << (*pNums[0]) << endl;

filling a char array with a string? c++

I need to fill this array via user prompt. I was thinking to read in the user entry to a string and then assign that string to the array but that doesn't seem to be the right way to approach this. Could someone help me out?
The error I'm receiving reads "array type array[100] is not assignable"
#include <iostream>
#include <cstdlib>
#include <ctime>
#include <string.h>
using namespace std;
int main()
{
string theString;
char array[100]; // ARRAY MAX SIZE
int length = sizeof(array)-1;
char * ptrHead = array;
char *ptrTail = array + length - 1;
//USER PROMPTS & ARRAY FILL
cout << "Please enter a string to be reverse: " << endl;
cin >> theString;
array= theString;
//WHILE LOOP SWAPPING CHARACTERS OF STRING
while (ptrHead < ptrTail)
{
char temp = *ptrHead;
*ptrHead = *ptrTail;
*ptrTail = temp;
ptrHead++;
ptrTail--;
}
cout << array << endl;
return 0;
}
arrays are not assignable. You should use strcpy here:
But for this you'll have to convert theString to C like string.
strcpy(array, theString.c_str() );
Then adjust your ptrTail pointer too , like following :
int length = theString.size();
char *ptrTail = array + length - 1;
See Here
cin >> array; should put the input directly into the array, which I'm guessing is what you want
Also, there's a problem in your string reversal logic. You are reversing the entire array, not just the part that has been filled, which will put the filled portion at the end of the array. Consider using a function like strlen() to find out how long the actual input is.
You can copy the string to the array with strcpy or input the data directly to the array with cin >> array, but the better solution would be just not to use a char array, just use the string in your algorithm. That is also a better solution because you can overflow a fixed size char array
cout << "Please enter a string to be reverse: " << endl;
cin >> theString;
for (unsigned int i = 0; i <= theString.size() / 2; ++i)
swap(theString[i], theString[theString.size() - 1 - i);
cout << theString<< endl;
Edit
The same using pointers:
std::cout << "Please enter a string to be reverse: " << std::endl;
std::cin >> theString;
char* i = &theString[0];
char* j = &theString[theString.size() - 1];
for (; i < j; ++i, --j)
std::swap(*i, *j);
std::cout << theString << std::endl;

Dynamically allocate an array of structs with C++

I am trying to dynamically allocate an array of records. When I run my program with ./a.out it appears to work fine but when I try and run the program inputing the data from a .txt file (./myProg < ) it repeatedly records the first line of text in the whole array and crashes and does not print out the chosen book in the end. My guess is I am not creating the array properly but I cannot seem to figure out what my problem is.
struct Book {
char *title; //entered with no spaces
int date; // in the form ddmmyy
};
Book *createRecord(int);
void input(Book *, int);
void display(Book *, int, int);
void destroyRecord(Book *);
int main() {
int arrN = 0;
int n = 0;
cout << "Enter size of array: ";
cin >> arrN;
Book *bookArr;
bookArr = new Book[arrN];
bookArr = createRecord(arrN);
input(bookArr, arrN);
cin.ignore();
cout << "Book: ";
cin >> n;
display(bookArr, arrN, n);
destroyRecord(bookArr);
return EXIT_SUCCESS;
}
Book *createRecord(int arrN){
struct Book *bookArr;
bookArr = new Book[arrN];
return bookArr;
}
void input(Book *bookArr, int arrN) {
for(int i = 0; i < arrN; i++){
char arrFirst[20];
cin.ignore();
cout << "Name: ";
cin.getline(arrFirst, 20);
strcpy((bookArr[i]).title = new char, arrFirst);
cout << "Score: ";
cin >> (bookArr[i]).date;
}
}
void display(Book *bookArr, int arrN, int n) {
if (0 <= n && n <= arrN){
cout << (bookArr[n-1]).title << " " << (bookArr[n-1]).date << endl;
}
}
void destroyRecord(Book *bookArr) {
delete [] (bookArr)->title;
delete bookArr;
}
Well, first up, you're allocating two arrays:
bookArr = new Book[arrN]; // <-- leaked
bookArr = createRecord(arrN);
That's a memory leak.
Secondly:
(bookArr[i]).title = new char
That is allocating one single char that you're trying to copy a whole string into. That should probably be:
// two lines please!
bookArr[i].title = new char[20];
strcpy(bookArr[i].title, arrFirst);
Thirdly:
if (0 <= n && n <= arrN){
That bounds check is incorrect. The upper bound should be n < arrN, and then just index n. By indexing n - 1, you might print the -1st index.
And last, but not least, prefer:
struct Book {
std::string title;
int date;
}
std::vector<Book> bookArr;
Problem1:
Fix the array indexing problem in the display function:
void display(Book *bookArr, int arrN, int n) {
if (0 <= n && n <= arrN){
cout << (bookArr[n - 1]).title << " " << (bookArr[n - 1]).date << endl;
}
}
to
void display(Book *bookArr, int arrN, int n) {
if (0 < n && n <= arrN){
cout << (bookArr[n - 1]).title << " " << (bookArr[n - 1]).date << endl;
}
}
when n = 0, bookArr[-1] is throwing the error in your code.
Problem 2:
Allocate character array instead of single character to title in line, change:
strcpy((bookArr[i]).title = new char, arrFirst);
to
strcpy((bookArr[i]).title = new char[20], arrFirst);
Not sure about the input problem, but you are definitely not creating the array as you should. In your code you are creating an array of books, then assigning a new array of books in the createRecord function and replacing the original array with it. Unlike what you are expecting to happen, you end up with an array of uninitialized Book pointers.
This is what you should be doing...
// Allocate an array of books
Book *bookArr;
bookArr = new Book[arrN];
// Preallocate the records
for ( int iBook = 0; iBool < arrN; iBook++ )
{
bookArr[ iBook ] = createRecord();
}
...
Book *createRecord()
{
// Allocate one new book
struct Book *pbook = new book;
return pbook;
}

When dynamically allocating memory for a list of strings that will be put in an array, is it possible to add on to that list later in the program?

I'm trying to understand how pointers and arrays work to dynamically expand memory during run time. I have this program here to illustrate what i'm talking about. Say the user makes int input = 5 and after this program runs its course sName and sID each hold 5 user specified strings. If I wanted to give the user the ability to add more elements after the initial 5, will I have to attempt to create new arrays that will be n+5 (where n is the new number of elements the user wants to add) and then copy the values of sName and sID into those arrays in addition to whatever the user puts in? Or is there a simpler way, other than just using vectors?
#include <iostream>
#include <string>
using namespace std;
int main()
{
int input;
string *sName = NULL;
string *sID = NULL;
string temp, temp2;
cout << "How many names";
cin >> input;
sName = new string[input];
sID = new string[input];
for (int i = 0; i < input; i++)
{
cout << "Enter name ";
cin >> temp;
cout << "enter id ";
cin >> temp2;
*(sName + i) = temp;
*(sID + i) = temp2;
}
return 0;
}
Small initial warning: your code is leaking memory.
That said I can't really understand why you don't want to use vectors for this since it would require much more work to achieve the same, you could allocate a new array of contiguous memory and copy the old elements back (warning: no checked iterators, the following code is highly discouraged and it is provided as an example for illustrative purposes)
// I want 2 additional elements
std::string *sNameOld = sName;
std::string *sIDOld = sID;
sName = new std::string[input + 2];
sID = new std::string[input + 2];
std::copy_n(sNameOld, input, sName);
std::copy_n(sIDOld, input, sID);
delete[] sNameOld;
delete[] sIDOld;
for (int i = input; i < input+2; i++)
{
std::cout << "Enter name ";
std::cin >> temp;
std::cout << "enter id ";
std::cin >> temp2;
*(sName + i) = temp;
*(sID + i) = temp2;
}
for (int i = 0; i < input + 2; ++i) {
std::cout << sName[i] << " " << sID[i] << std::endl;
}
delete[] sName;
delete[] sID;
I highly recommend to use std vectors since they are the primary choice for the task you're doing by providing heap-based dynamic allocation and being quite flexible at adding new elements by resizing themselves
#include <iostream>
#include <string>
#include <vector>
int main()
{
int input;
std::vector<std::string> sName;
std::vector<std::string> sID;
std::string temp, temp2;
std::cout << "How many names";
std::cin >> input;
for (int i = 0; i < input; i++)
{
std::cout << "Enter name ";
std::cin >> temp;
std::cout << "enter id ";
std::cin >> temp2;
sName.push_back(temp);
sID.push_back(temp2);
}
return 0;
}
Example
The above code is not even leaking memory.
There is no exact C++ replacement for C's realloc although you might craft something suited for the task, for instance by using placement new.
Reference: C++: What is the proper way of resizing a dynamically allocated array?
for just understanding you are doing it the right way. you have to add n when the array limit is reached. By the way vectors are best suited for such kind of problems they can increase\decrease their size dynamically and automatically.

I want to declare a pointer array to character without using string

Here is the code::
#include <iostream>
using namespace std;
const int MAX = 4;
int main ()
{
char key[20];
char *names[MAX];
for (int i=0; i<MAX; i++)
{
cout << " entr keys\n";
cin >> key;
names[i]=key;
cout<< names[i];
}
for(int i=0; i<MAX;i++)
{
cout << names[i];
}
return 0;
}
When I enter the keys and print them in the 1st for loop they show the right value, but when I print names[i] in the 2nd for loop it keeps showing the last key entered again and again.
Please tell me: where am I going wrong?
When you run names[i]=key; you don't really copy key's string value to names[i].
It just makes name[i] point to where key is (since both name[i] & key are pointers).
so all in all you're overwriting key several times, and making all of names pointers point to key.
You need to copy those strings either by working with std::string instead of char* or by using strcpy. I'd recommend on working with std::string.
Using std::string your code should look like this:
#include <iostream>
#include <string>
using namespace std;
const int MAX = 4;
int main ()
{
string names[4];
for (int i = 0; i < MAX; i++)
{
cout << "entr keys" << endl;
cin >> names[i];
cout << names[i];
}
for(int i=0; i<4;i++)
{
cout << names[i];
}
return 0;
}
Every time you execute the lines
cout << " entr keys\n";
cin >> key;
you're inserting a null-terminated string into key, e.g. "hello\0".
Afterwards you copy key's address and store it into a cell of the names pointers array:
names[i]=key; // Now I point to 'key'
cout<< names[i];
then the cycle starts again. Anyway from the second time on you're inserting null-terminated strings into key and thus overwriting the previous contents. The second time if you had entered "hi\0" the contents of the key array would become
['h', 'i', '\0', 'l', 'l', 'o', '\0']
anyway you're going to only print the first string since the null terminator will prevent the other content from being displayed.
When the program ends you're going to have four pointers to the same key array and that array will only contain the last element inserted which overwrote the previous ones.
In order to solve you can make your array a bidimensional one (or use a string array):
const int MAX = 4;
int main ()
{
char key[4][20]; // <- Now this has two indices
char *names[4];
for (int i = 0; i < MAX; i++)
{
cout << " entr keys\n";
cin >> key[i];
names[i]=key[i];
cout<< names[i];
}
for(int i=0; i<4;i++)
{
cout << names[i];
}
return 0;
}
Live Example
Corrected program:
#include <iostream>
using namespace std;
#include <cstring>
const int MAX = 4;
int main ()
{
char key[20];
char *names[MAX];
for (int i = 0; i < MAX; i++)
{
cout << " entr keys\n";
cin >> key;
names[i] = new char[strlen(key) + 1];//names[i]=key;
strcpy(names[i], key);
cout<< names[i];
}
for(int i=0; i<MAX;i++)
{
cout << names[i];
}
for(int i=0; i<MAX;i++)
{
delete [] names[i];
}
return 0;
}
You need to allocate space for each names[i] and when done, deallocate
also, changed the hardcoded 4 to MAX