Minh Nghĩa
Minh Nghĩa

Reputation: 1053

How to read a string of unknown size in C++

I'm a newbie, at both coding and English. This is my code:

#include<iostream>
#include<cstdio>

using namespace std;

int main()
{
    int n = 1;
    char *a = new char[n], c = getchar();
    while ((c != EOF) || (c != '\n'))
    {
        a[n-1] = c;
        c = getchar();
        n++;
    }
    cout << a;
    delete[] a;
    return 0;
}

I'm learning about dynamic memory allocation. The problem is to input a string whose length is unknown. My idea is to read the string character by character and stop when it reaches EOF or \n. Could you please point out the error?

And another question: I was told that new selects a memory block of the specified size. So what happens if there wasn't a large enough block?

Thanks for helping!

Upvotes: 2

Views: 8476

Answers (4)

Bob__
Bob__

Reputation: 12769

Considering the restrictions of your task (no std::string, no std::vector, dynamic memory allocation), I'll try to give you a modified but working version of your code.

My idea is read the string word my word and stop when it reach EOF or \n. Could you please point out the error?

As molbdnilo pointed out, (c!=EOF) || (c!='\n') is always true, so your loop will never end.

As mah noticed, your a buffer is only 1 char long and you don't check for the overflow, besides, You forgot to add the null terminator at the end of it.

Your second question is about what happens when new can't allocate enough memory. It throws an exception which your program should catch to manage the situation, but the best thing (not the only one actually, maybe the easiest) you can do is to terminate your program.

This is an example of how to accomplish your task given the above mentioned limitations:

#include <iostream>

using namespace std;

int main()
{
    const int INITIAL_SIZE = 8;

    // The following block of code could rise an exception.
    try
    {
        int n = 0;
        char c;

        // Allocate some memory to store the null terminated array of chars.
        char *a = new char[INITIAL_SIZE];
        // what happens if new fails? It throws an exception of type std::bad_alloc
        // So you better catch it.
        int allocated = INITIAL_SIZE;

        // read a charachter from stdin. If EOF exit loop
        while( cin.get(c) )
        {
            // If it's a newline or a carriage return stop.
            if( '\n' == c  ||  '\r' == c )
                //^ Note that  ^^^ putting the literals first helps avoiding common
                // error like using "=" instead of "==" in conditions.
                break;

            // If the array is full, it's time to reallocate it.
            if ( n == allocated )
            {
                // There are better alternatives, of course, but I don't know which library
                // you are allowed to use, so I have to assume none.

                // Allocate a bigger array. The growing strategy may be different.
                allocated += 2 + allocated / 2;
                char *b = new char[allocated];

                // Copy the old one in the new one (again, you could use std::copy).
                for ( int i = 0; i < n; ++i )
                {
                    b[i] = a[i];
                }

                // Release the memory handled by the old one...
                delete[] a; 

                // but keep using the same pointer. Just remember not to delete 'b'
                // so that 'a' always points to allocated memory.
                a = b;
            }
            
            a[n] = c;

            // A new character has been succesfuly added.
            ++n;
        }

        // Now, before using a, we have to add the null terminator.
        a[n] = '\0';

        // Note that a doesn't contain the '\n'.
        cout << a << '\n';

        // Clean up.
        delete[] a; 

        // Normal program termination.  
        return 0;
    }
    // If 'new' fails to allocate memory a std::bad_alloc exception is thrown.
    catch ( const exception &e )
    {
        cout << "Exception caught: " << e.what() << "\nProgram terminated.\n";
        return -1;
    }
}

Upvotes: 3

Akshay Arora
Akshay Arora

Reputation: 1945

[I know adhering to best practices and methods available is the "good" thing to do, but the OP should know why the current code doesn't work and the other answers here do not seem to be answering that]

First, you should use C++ string class for this.

Second, if you are wondering why your current code is not working, it is because:

  1. The condition inside while is wrong. It says, "Execute this block if the character is not \n or it is not EOF". So even if you press enter (c is '\n'), this block will still execute because "c is not EOF", and vice-versa.

  2. You are allocating only 1 byte worth of memory to your char*, which is clearly not enough.

This should fairly replicate what you want, but the memory allocated is static and the string has to be limited.

int main()
{
    int n=1;
    char *a = new char[100],c=getchar();
    while(true)
    {
        if(c == '\n' || c == EOF){
            break;
        }
        a[n-1]=c;
        c=getchar();
        n++;
    }
    cout << a;
    delete[] a;
    return 0;
}

Upvotes: 4

erip
erip

Reputation: 16935

#include <iostream>
#include <string>

int main() {
  std::string line;
  // first argument is the stream from whence the line comes.
  // will read to newline or EOF
  std::getline(std::cin, line);
}

Upvotes: 4

R Sahu
R Sahu

Reputation: 206637

First of all, there is no need to use char* and new char[n]. You can use std::string.

Then you have to ask yourself:

  1. Can the string contain whitespace characters?
  2. Can the string span multiple lines?
  3. If it can span multiple lines, how many lines does it span?

If the answer to the first question is "No", you can use:

std::string s;
cin >> s;

If the answer to the first question is "Yes" and the answer to the second question is "No", then you can use:

std::string s;
getline(cin, s);

If the answer to the second question is "Yes", the answer gets more complicated.

Then, you need to find answers to more questions?

  1. Is the number of lines hard coded?
  2. If it is not hard coded, how does the program get that number from the user?

Based on the answers to those questions, your code will vary.

Upvotes: 4

Related Questions