0

I have written the following code to save in an char * array and print the following content: band1.txt band2.txt ... band3.txt The code seems right but what is printed on the console is very weird.

Code:

const char ** current_band =  new const char * [103];

stringstream sstm;
string str;

for (i=0;i<103;i++){
    current_band[i] = new char[11];
}

for (i=0;i<103;i++){

    sstm.str("");
    sstm << "band" << i+1 << ".txt";
    str = sstm.str(); 

    current_band[i] = str.c_str();
    cout << current_band[i] << endl;
    cout << i << endl;
}

for (i=0;i<103;i++){
    cout << current_band[i] << endl;
    cout << i << endl;
}  

Console:

band1.txt

0

band2.txt

1

...

band103.txt

102

And then for the last loop:

band103.txt

0

band102.txt

1

band103.txt

2

band102.txt

3

...

band102.txt

101

band103.txt

102

How is this even possible?

EDIT: Actually i want the "bands" to be char* in order to call the ifstream current_band_file(current_band) constructor that wants such an argument

iiirxs
  • 4,493
  • 2
  • 20
  • 35

4 Answers4

4

You have undefined behavior by using pointers to already destroyed objects.

Simply don't use raw pointers and raw arrays and such stuff yet.

std::string is your friend for strings, std::vector is your friend for arrays.


Example:

#include <iostream>
#include <string>
#include <vector>
using namespace std;

auto main()
    -> int
{
    vector<string>  band_names;

    for( int i = 1; i <= 103; ++i )
    {
        band_names.push_back( "band" + to_string( i ) );
    }

    for( string const& name : band_names )
    {
        cout << name << endl;
    }
}
Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • i guess i can figure it out and find a solution with strings but yet cant see the reason for the undefined behavior! which objects are being destroyed? – iiirxs May 12 '14 at 21:46
  • 2
    @iiirxs: the statement `current_band[i] = str.c_str();` stores a pointer to a (internal buffer in a) local automatic variable, that is destroyed at the end of that block (the loop body). – Cheers and hth. - Alf May 12 '14 at 21:48
  • :-) using a trailing return type on main() - made me smile. – Richard Hodges May 12 '14 at 22:09
  • still didn't understand why the variable you say is destroyed at the end of the block. both current_band and str are defined before the loop... – iiirxs May 12 '14 at 22:32
  • @iiirxs The pointer returned by `str.c_str()` is (formally, "may be") invalidated when you assign a new value to `str` in the following iteration. – molbdnilo May 12 '14 at 23:06
  • @molbdnilo: you're right, i misread the code for the comment. the answer is okay though. thanks! – Cheers and hth. - Alf May 12 '14 at 23:50
1

As a minimal change to you existing code you can change:

current_band[i] = str.c_str();

to:

strcpy(current_band[i], str.c_str());

However, moving away from this mixed C and C++ to more idiomatic C++ (like Cheers and hth. - Alf's answer) will serve you better for the future.

Sticking with things like char[11] over std::string means you're stuck with:

  • The arbitrary choice of max length 11 even though probably there is no good technical reason for that limit.
  • Dealing with handling all the details of memory allocation which a proper C++ implementation hides.
  • The much less natural to read lower level code style.
PeterSW
  • 4,921
  • 1
  • 24
  • 35
0

As a band-aid you could replace:

current_band[i] = str.c_str();

with

if ( str.size() >= 11 )
    throw std::runtime_error("string too long");
std::strcpy(current_band[i], str.c_str());

However it would be a much better idea to replace this whole thing with:

std::vector<std::string> current_band(103);
int i = 0;
for (auto &s : current_band)
{
    // your sstm stuff, storing to s
}
M.M
  • 138,810
  • 21
  • 208
  • 365
0

Here's an alternative way that's a little more robust, readable and more likely to be correct.

#include <vector>
#include <iostream>
#include <string>
#include <sstream>

using namespace std;


int main()
{
    vector<string> bands;
    bands.reserve(103);
    for(size_t i = 1 ; i <= 103 ; ++i) {
        ostringstream ss;
        ss << "band" << i;
        bands.emplace_back( ss.str() );
    }

    for (size_t index = 0 ; index < bands.size() ; ++index) {
        cout << index << " : " << bands[index] << endl;
    }

    return 0;
}

output:

Compiling the source code....
$g++ -std=c++11 main.cpp -o demo -lm -pthread -lgmpxx -lgmp -lreadline 2>&1

Executing the program....
$demo 
0 : band1
1 : band2
2 : band3
...
100 : band101
101 : band102
102 : band103
Richard Hodges
  • 68,278
  • 7
  • 90
  • 142