LinuxQuestions.org
Help answer threads with 0 replies.
Home Forums Tutorials Articles Register
Go Back   LinuxQuestions.org > Forums > Non-*NIX Forums > Programming
User Name
Password
Programming This forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.

Notices


Reply
  Search this Thread
Old 12-01-2008, 07:57 PM   #1
the_cpp
LQ Newbie
 
Registered: Nov 2008
Posts: 16

Rep: Reputation: 0
Bug with GCC 4.3.2 !


I'm currently running Ubuntu 8.10, and I use GCC 4.3.2, but when I tried to test this example :
Code:
#include <iostream>

int main()
{
	int* a = new int[ 2 ];
	a[ 0 ] = 1;
	a[ 1 ] = 2;

	delete[] a;

	std::cout << a[ 1 ] << std::endl;

	return 0;
}
It still print out : 2
I think this is a bug with gcc since I got trash-value on Visual C++. Could anyone help me to explain this situation, it confused me ! Thanks !
 
Old 12-01-2008, 08:55 PM   #2
ErV
Senior Member
 
Registered: Mar 2007
Location: Russia
Distribution: Slackware 12.2
Posts: 1,202
Blog Entries: 3

Rep: Reputation: 62
Quote:
Originally Posted by the_cpp View Post
I'm currently running Ubuntu 8.10, and I use GCC 4.3.2, but when I tried to test this example :
Code:
#include <iostream>

int main()
{
	int* a = new int[ 2 ];
	a[ 0 ] = 1;
	a[ 1 ] = 2;

	delete[] a;

	std::cout << a[ 1 ] << std::endl;

	return 0;
}
It still print out : 2
I think this is a bug with gcc since I got trash-value on Visual C++. Could anyone help me to explain this situation, it confused me ! Thanks !
It is not the bug. If you think otherwise, please, tell me where in C++ standard it is said that in such situation program shouldn't print 2.

You just were really lucky, memory address wasn't reused by anything, was still accessible and program didn't segfault as result (to my opinion, it is the worst part of the situation, because tracking bug like this might take days).

P.S. I assume, you are aware that "correct" program would be:
Code:
#include <iostream>

int main()
{
	int* a = new int[ 2 ];
	a[ 0 ] = 1;
	a[ 1 ] = 2;

	std::cout << a[ 1 ] << std::endl;

	delete[] a;

	return 0;
}
?
 
Old 12-01-2008, 10:22 PM   #3
the_cpp
LQ Newbie
 
Registered: Nov 2008
Posts: 16

Original Poster
Rep: Reputation: 0
- First thanks ErV, but I was totally confused at this point. After delete[] a called, I supposed the memory that was allocated should return to the system. Visual C++ actually performed correctly at this point, I run that code under Visual C++ and it did NOT print out '2'. If it's not the bug, then if we accidentally got to this situation debugging time would be disaster. I don't understand why g++ community weren't aware of this situation ?( even though the standard C++ did not mention it )
- The fact that, I used Visual C++ for about 1 years before I switch to Linux and it was really strict on this kind of bugs. I meant I usually got run-time crash when I did something wrong with memory. However g++ does not seem strict enough on these cases( in my opinion ). I understand as a programmer, I should not expect the compiler report bug for me but somehow g++ is less efficient comparing with Visual C++. I'm a fan of Linux of-course, that's why I tried to understand these things and hope the community got a better improvement on g++.
Quote:
You just were really lucky, memory address wasn't reused by anything, was still accessible and program didn't segfault as result (to my opinion, it is the worst part of the situation, because tracking bug like this might take days).
So like you said, it's the just the matter of OS ? Windows memory is harder to access than Linux ? Could you explain this ? Thanks in advance and I really appreciate it ErV. Sorry for my poor English !
 
Old 12-01-2008, 11:27 PM   #4
ErV
Senior Member
 
Registered: Mar 2007
Location: Russia
Distribution: Slackware 12.2
Posts: 1,202
Blog Entries: 3

Rep: Reputation: 62
Quote:
Originally Posted by the_cpp View Post
- First thanks ErV, but I was totally confused at this point. After delete[] a called, I supposed the memory that was allocated should return to the system. Visual C++ actually performed correctly at this point, I run that code under Visual C++ and it did NOT print out '2'. If it's not the bug, then if we accidentally got to this situation debugging time would be disaster. I don't understand why g++ community weren't aware of this situation ?( even though the standard C++ did not mention it )
The problem here is not that it printed "2" instead of random garbage. There is nothing really wrong with that. The problem here is that normally when you access memory which is no longer allocated, program should instantly crash. With "segmentation fault" on linux, or with "access violation - memory can't be read" (in your case) on Windows. I'm surprised it didn't happen on either platform, but it can be explained.

Quote:
Originally Posted by the_cpp View Post
- The fact that, I used Visual C++ for about 1 years before I switch to Linux and it was really strict on this kind of bugs. I meant I usually got run-time crash when I did something wrong with memory. However g++ does not seem strict enough on these cases( in my opinion ). I understand as a programmer, I should not expect the compiler report bug for me but somehow g++ is less efficient comparing with Visual C++. I'm a fan of Linux of-course, that's why I tried to understand these things and hope the community got a better improvement on g++.
This is not a compiler problem. Program like this shouldn't print anything on both Linux and Windows - it should crash instantly at the moment when it tries to access a[1]. Try this:
Code:
#include <iostream>

int main()
{
int* a = new int[ 1000000 ];
a[ 0 ] = 1;
a[ 999999 ] = 6;

delete[] a;
std::cout << a[ 999999 ] << std::endl;

return 0;
}
It will crash on both platforms, when you'll access a[999999]. And this is the only correct way how program with such problem should work.

The reasons why this doesn't with happen with a[2] are luck and implementation details of C++.

Quote:
Originally Posted by the_cpp View Post
So like you said, it's the just the matter of OS ? Windows memory is harder to access than Linux ? Could you explain this ? Thanks in advance and I really appreciate it ErV.
explanations:
Why it prints 2 instead of garbage:
When you deleted array, memory was still accessible to the program (this is pure luck, really), and wasn't overwritten by anything else. That's why "2" was still here. This is really pure luck, and it did work this way only because of the way C++ heap was implemented.

Why it printed another number on windows:
Possibile explanation #1:
I believe the printed number was hexadecimal 0xcccccccc, and you tested program using only debug build, right? Microsoft Visual C++ has two versions of runtime library (and memory allocation routines) - debug and release. Debug version of runtime library has certain mechanisms that might warn you if you are using unintialized variable, if you corrupted another variable, and so on. Those mechanisms modify memory before you access it - as I can see, they normally fill unitialized variables and memory blocks with 0xccccccc, store additional info when you allocate memory, etc (oh, and all this takes a lot of additional CPU time). So value of "2" could have been overwritten by one of those mechanisms.
Possible explanation #2:
Memory allocation routines on MSVC might be implemented in different way, so when you call delete [], place where "2" was placed before, might become overwritten by something else.

Why it doesn't crash with 2 but crashes with 1000000.
This is mostly luck. When you allocate block with size of 2, delete it and try to access last element, block was very small, and even after deletion it just happened to be within region your program could access (probably due to the way new[] was implemented). This is pure luck. But when you allocate block with size of 1000000 elements, delete it (don't allocate anything else) and try to access now deleted last element, there is very little possibility that block will be within the memory region program is allowed to access, unless you are programming on MS-DOS in real mode. As you probably know, all modern multithreaded operating systems operate in "protected" CPU mode, and program can't access any random chunk of memory it wants - it will result in crash, unless program is allowed to access it.

If you want even more details, you should read source code of new[] and delete[] operators on both Linux and Windows compilers. I can guarantee it won't be easy (especially with debug version of CRT on windows), but it should explain few things for you.

Last edited by ErV; 12-01-2008 at 11:30 PM.
 
Old 12-02-2008, 02:41 AM   #5
the_cpp
LQ Newbie
 
Registered: Nov 2008
Posts: 16

Original Poster
Rep: Reputation: 0
Wow, very clear explanation, thanks a lot ! I really appreciate it. I probably can't understand completely the source code of new and delete on both Linux and Windows right now since I've been using C++ for 2 years and a half. I will try, and probably ask if I got stuck ! One more time, thanks Erv.
 
Old 12-02-2008, 02:46 AM   #6
the_cpp
LQ Newbie
 
Registered: Nov 2008
Posts: 16

Original Poster
Rep: Reputation: 0
By the way, what is the file name which has the implementation of new and delete Erv ? I tried to search on the source of gcc but I couldn't find it ? Thanks !
 
Old 12-02-2008, 02:47 AM   #7
the_cpp
LQ Newbie
 
Registered: Nov 2008
Posts: 16

Original Poster
Rep: Reputation: 0
By the way, what is the file name which has the implementation of new and delete Erv ? I tried to search in the source of gcc but I couldn't find it ? Thanks !
 
Old 12-02-2008, 04:00 AM   #8
ErV
Senior Member
 
Registered: Mar 2007
Location: Russia
Distribution: Slackware 12.2
Posts: 1,202
Blog Entries: 3

Rep: Reputation: 62
Quote:
Originally Posted by the_cpp View Post
By the way, what is the file name which has the implementation of new and delete Erv ? I tried to search on the source of gcc but I couldn't find it ? Thanks !
It is part of gcc-g++ package, located in gcc-4.3.2/libstd++-v3/libsubc++/new_op.cc . Prepare to search for/read source of malloc.
 
Old 12-02-2008, 06:41 AM   #9
dmail
Member
 
Registered: Oct 2005
Posts: 970

Rep: Reputation: Disabled
Bug with GCC 4.3.2 !
Please just because you do not understand something do not claim that you have found a bug.
 
Old 12-02-2008, 07:39 AM   #10
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197
Quote:
Originally Posted by ErV View Post
This is not a compiler problem. Program like this shouldn't print anything on both Linux and Windows - it should crash instantly at the moment when it tries to access a[1].
It's not a compiler problem, but it is not correct that it should crash.

It is undefined behavior and undefined behavior is undefined. It could crash. It could display 2. It could display something else. When displaying 2 or something else it could (but won't) corrupt some unrelated part of your program or data causing totally confusing results. Any of those behaviors would be correct according to the C++ standard.

A C++ compiler should prioritize performance of correct code above understandable consequences for undefined behavior incorrect code. If it is expensive (as it would be in this case) to detect and abort on a particular kind of undefined behavior, then C++ should not do so.

There are various tools to run your program in various slower modes to detect many coding errors, such as this one.

Quote:
Why it doesn't crash with 2 but crashes with 1000000.
This is mostly luck.
I guess you could call it luck, but it is almost entirely predictable. The run time library inside your process gets memory from the OS and returns memory to the OS only in very large chunks. If your code asks for a very large chunk, the run time library might request it directly from the OS and when you delete it, return it directly. But when you ask for a small chunk the run time library almost always gives you memory left over from some previous OS allocation. If it had none left over, it will ask for a big chunk and give you just a small part of that AND when you delete that small part it almost always won't return the big chunk to the OS.
 
Old 12-03-2008, 12:27 PM   #11
dmail
Member
 
Registered: Oct 2005
Posts: 970

Rep: Reputation: Disabled
Quote:
This is not a compiler problem. Program like this shouldn't print anything on both Linux and Windows - it should crash instantly at the moment when it tries to access a[1].
On the less serious side for a minute this had me laughing about memory errors in windows.
Quote:
http://www.joelonsoftware.com/articles/APIWar.html
I first heard about this from one of the developers of the hit game SimCity, who told me that there was a critical bug in his application: it used memory right after freeing it, a major no-no that happened to work OK on DOS but would not work under Windows where memory that is freed is likely to be snatched up by another running application right away. The testers on the Windows team were going through various popular applications, testing them to make sure they worked OK, but SimCity kept crashing. They reported this to the Windows developers, who disassembled SimCity, stepped through it in a debugger, found the bug, and added special code that checked if SimCity was running, and if it did, ran the memory allocator in a special mode in which you could still use memory after freeing it.
 
Old 12-04-2008, 01:10 AM   #12
the_cpp
LQ Newbie
 
Registered: Nov 2008
Posts: 16

Original Poster
Rep: Reputation: 0
Thanks everybody ! My bad ! I will be more careful next time. I've just got the issue with writing std::string to binary file. I understand that the pointer char* when using read() and write() would lead to problem since std::string is different than char[].
Then I go with the solution overloading operator << and >>, but this lead me back to another problem -> this is sequential file not random-access-file, so I can't jump to another position and edit my data. Anyone could help me out ?
For example :
Code:
class SomeData {
    double d;
    string s;
};
If I use the syntax :
Code:
SomeData sd;
inf.write( reinterpret_cast< char* >( &sd ), sizeof( SomeData ) );
It won't work ?
And the last question is where could I find the 'ifstream' implementation in gcc source, I open the one "fstream.tcc" but this is about file_buf, there're nothing involving with ifstream and ofstream ? Thanks in advance !
 
Old 12-04-2008, 01:27 AM   #13
jiml8
Senior Member
 
Registered: Sep 2003
Posts: 3,171

Rep: Reputation: 116Reputation: 116
The Linux virtual memory system is going to allocate RAM in 4K page chunks, regardless of what you ask for. When you return the memory to the system, the system will only take it if all of that page is freed up. If some other variable is defined in that page as well, you get to keep the page and the freed memory won't be used by anyone else - at least, until the system swaps that page out.

So you have freed the memory, but you have not changed the pointer that you set when you defined the array, AND that pointer points to a page that you own. So you get the last value that was put there.
 
Old 12-04-2008, 09:12 AM   #14
johnsfine
LQ Guru
 
Registered: Dec 2007
Distribution: Centos
Posts: 5,286

Rep: Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197Reputation: 1197
Quote:
Originally Posted by the_cpp View Post
I've just got the issue with writing std::string to binary file.
When asking for so much help, you ought to at least put more effort into understandable descriptions of the problem.

Quote:
Then I go with the solution overloading operator << and >>, but this lead me back to another problem -> this is sequential file not random-access-file, so I can't jump to another position and edit my data.
How do those two things relate to either each other or the rest of what you're asking? In other words, what's wrong with operator<< and why do you need to jump to another position?

Quote:
inf.write( reinterpret_cast< char* >( &sd ), sizeof( SomeData ) );
That's so wrong, it isn't even a starting point for getting to correct code.

If you tried doing this with operator<<, why isn't that the version you showed us?

Quote:
And the last question is where could I find the 'ifstream' implementation in gcc source, I open the one "fstream.tcc" but this is about file_buf, there're nothing involving with ifstream and ofstream
If you don't understand the documentation on how to use those, you're not going to gain any understanding from looking at the source code.

There are certainly times where documentation is bad and/or the behavior is obscure or buggy and it is great to have open source so you can look at the source code and get the real answer. But this isn't one of those situations. Stream I/O will be MUCH easier for you to understand as a black box with well defined external behavior, than by digging into the implementation details.
 
Old 12-04-2008, 03:24 PM   #15
the_cpp
LQ Newbie
 
Registered: Nov 2008
Posts: 16

Original Poster
Rep: Reputation: 0
I apologize for being vague. Here's the version with operator << and >> :
Code:
#include <iostream>
#include <limits>
#include <string>
#include <vector>
#include <fstream>
#include <algorithm>
#include <memory>
#include <map>

using namespace std;

static const char* file_name = "t.bin";
const string user_messages   = "Show records from file";

const int BYTE_OF_INT        = sizeof( int );
const int BYTE_OF_DOUBLE     = sizeof( double );
const int BYTE_OF_FLOAT      = sizeof( float );
const int BYTE_OF_CHAR       = sizeof( char );
const unsigned BIT_PER_BYTE  = 8;



struct Record {
    int    x;
    string str;
public :
    Record( int x = 0, const string& str = "" )
        :x( x ), str( str )
    {   }

    void show( ostream& o ) const {
        o << endl << x << " " << str;
    }
};

inline
ostream& operator <<( ostream& ouf, const Record& rec ) {
    for( int cursor = 0, i = rec.x; cursor < BYTE_OF_INT; ++cursor, i >>= BIT_PER_BYTE )
        ouf.put( i & 0xFF );

    ouf << rec.str << '\0';
    return ouf;
}

inline
istream& operator >>( istream& inf, Record& rec ) {
    rec.x = 0;
    for( int i = 0; i < BYTE_OF_INT; ++i )
        rec.x += ( inf.get() << ( BIT_PER_BYTE * i ) );

    getline( inf, rec.str, '\0' );
    return inf;
}

class ClientDatabase {
private :
    vector< int > block_of_bytes;
    fstream clf;

public :
    void write_some_records( const vector< const Record* >& con );
    void show_records();
    void show_records_at( int pos );
    void edit_record_at( int pos, const Record& rec );
    void append_new_record( const Record& rec );
};


void ClientDatabase::write_some_records( const vector< const Record* >& con )  {
    clf.open( file_name, ios::binary | ios::out | ios::trunc );
    for( vector< const Record* >::const_iterator b = con.begin(), e = con.end(); b != e; ++b ) {
        block_of_bytes.push_back( clf.tellp() );
        clf << *( *b );
    }
    clf.close();
}



void ClientDatabase::show_records() {
    clf.open( file_name, ios::binary | ios::in );
    auto_ptr< Record > rec( new Record() );
    cout << endl << endl << user_messages;
    while( clf >> *rec ) {
        block_of_bytes.push_back( clf.tellg() );
        rec->show( cout );
    }
    clf.close();
}

void ClientDatabase::show_records_at( int pos )
{
    Record temp;
    clf.open( file_name, ios::binary | ios::in );
    clf.seekg( ios::beg );
    clf.seekg( block_of_bytes.at( pos ), ios::beg );
    clf >> temp;
    temp.show( cout );
}



void ClientDatabase::append_new_record( const Record& rec ) {
    clf.open( file_name, ios::binary | ios::out | ios::app );
    clf << rec;
    clf.close();
}

struct DeleteObject {
    template< typename T >
    void operator()( const T* ptr ) const {
        delete ptr;
    }
};


int main()
{
    ClientDatabase cl;
    vector< const Record* > records;
    records.push_back( new Record( 1, "Windows"   ) );
    records.push_back( new Record( 2, "Linux"     ) );
    records.push_back( new Record( 3, "Macintosh" ) );
    records.push_back( new Record( 4, "This is a long long long long a long record ever !!!!" ) );

    cl.write_some_records( records );

    Record new_rec( 5, "VVVVVVVVVVVV" );
    cl.append_new_record( new_rec );

    for_each( records.begin(), records.end(), DeleteObject() );

    cl.show_records();

    return 0;
}
As you can see, I could jump to the position that I need to print out the data of record 'th' by using another vector which holds the number of bytes of each block. However if I want to edit that record( i.e overwrite it with another record ), I can't. I thought over it the whole night. Since the std::string the I wrote is ANSI character, that block of bytes was fixed. I can't add a record with more bytes( e.g a really long string ) in the middle. The only solution that I can come up right now is overwrite the rest, but it could lead to resource extensive. So is there another way to work around this problem ? Anyways, thanks a lot for your concerning, I will particularly more specific about my question next time. Thanks !
 
  


Reply



Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

BB code is On
Smilies are On
[IMG] code is Off
HTML code is Off



Similar Threads
Thread Thread Starter Forum Replies Last Post
there is a bug in glibc's heeder ??(gcc 4.1) unixnovice Debian 2 08-24-2007 04:42 AM
Is it GCC bug? vkmgeek Programming 19 11-08-2006 12:31 AM
bug reports on glibc or gcc tvburger Linux From Scratch 1 12-06-2004 12:43 AM
Extremely strange bug in gcc/g++ Maidros Linux - Software 3 05-08-2004 06:28 AM
gcc bug? fab12 Programming 2 02-07-2004 07:44 AM

LinuxQuestions.org > Forums > Non-*NIX Forums > Programming

All times are GMT -5. The time now is 11:27 PM.

Main Menu
Advertisement
My LQ
Write for LQ
LinuxQuestions.org is looking for people interested in writing Editorials, Articles, Reviews, and more. If you'd like to contribute content, let us know.
Main Menu
Syndicate
RSS1  Latest Threads
RSS1  LQ News
Twitter: @linuxquestions
Open Source Consulting | Domain Registration