This is the mail archive of the gcc-help@gcc.gnu.org mailing list for the GCC project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]

Re: Why does this program break ?




Dawit Yimam wrote:

> void break (const vector<string>* doc)
> {
>     vector<string>::iterator i;
>     char* line;
>
>     for (i=doc->begin(); i != doc->end(); i++) {
>         strcpy(line, (*i).c_str());  // copy to remove const of
> (*i).c_str()
>         while (*line != '\0') {
>             cout << *line << " ";
>             ++line;
>         }
>         cout << endl;
>     }
> }

You may already know this, but bear with me.  If you want to jump right to
the solution, look for a line of asterisks.  I don't know if I can add a
hyper link so I didn't.

I have had the same problem before.  Making a char* is not the same as making
a string.  A char* only stores a location.  It "points" to a place in
memory.  To use a char* you must follow two steps:

1.)  Declare the character pointer:

char* c;        //You already did this.

2.)  Put a valid memory address into the character pointer

c = new char[5];    //c points to a set of 5 characters in sequence.

Not all memory is valid.  Applications have full access to all of the RAM
memory.  A program does not always stay in its own little space.  A slight
mistake could overwrite the RAM memory that contains DOS or Windows, and thus
freeze the computer.  (Though, when you restart the computer, DOS and Windows
are reloaded from hard disk memory so they are not permanently destroyed.)
Your program set up a pointer but did not point it to a valid location in
memory.  Luckily Windows or DOS or something detected it before your computer
crashed.

1.) Dynamic Memory

Because pointers can point to any location of the computer, they must be
aimed correctly.  The "new" operator I used above gets guaranteed "unused"
memory.  This means writing to any of the characters specified in the new
statement (the character at c and the next 5 characters) will not overwrite
any current programs.  After finishing with the memory, one should use the
delete operator:

delete[] c;

This deallocates the character at c and the other 4 past it.  If you don't
use delete, your program may accidentally fill up memory.  Delete frees
memory so your program and other programs can reuse it.

There are other methods to avoid this overwriting process.

2.) Local Memory

Every program allocates some memory when it loads.  This is called local
memory.  Local variables access local memory.  This is much faster because
the computer does not have to check this memory for other programs except at
the initial loading stage.  However, a programmer using local memory must
know how much memory needed before they compile their program.

example of local memory:

char c[5];    //Get 5 characters at the start of the program.

The number between the brackets must be a constant integer, unchangeable by
any means.  That way, the computer knows beforehand how much memory you will
need and can allocate it at the loading stage.

3.) The "Just Know" method.

The last method I know is simple.  Just know.  Address &A000:0000 in graphics
mode is the starting address for the video screen memory.  By writing to this
location and the locations beyond it, one can change the pixels of the screen
to different colors.  This is only for IBM compatible computers using DOS, so
don't try this if you have UNIX or a Macintosh.  They may have a different
system.

This is the reason using the "just know" method doesn't work very well.  If I
said char* c = 0xA0000000;
then I would already be confining myself to IBM compatible machines with DOS
installed.  The first two methods are the ones used for most programs.  They
are platform independent, which means they can be used on any computer.

One might wonder, why use new if the syntax for local memory is so much
easier.  You don't have to delete local memory.  However, the number in the
brackets for local memory must be constant.  You can't change its size.

Impossible:

char b[1000] = "A 17 letter string.";
char a[strlen(b)];    //compiler error.  strlen can change in value so it
doesn't return
                             //a  const char.
strcpy(a,b);

Possible:

char b[1000] = "A 17 letter string";
char* a = new char[strlen(b)];
strcpy(a, b);
delete[] a;

The new operator takes memory at run time so it can change how much memory it
gets.

I'm finally to the point:  the answer.

****************************************************************
**************************The Solution***************************
****************************************************************
****************************************************************


//start

void break (const vector<string>* doc)
{
    vector<string>::iterator i;
    char* line;

    for (i=doc->begin(); i != doc->end(); i++)
    {
        //strcpy(line, (*i).c_str());  // copy to remove const of
(*i).c_str()
        line = (char*) (*i).c_str();
        while (*line != '\0')
        {
            cout << *line << " ";
            ++line;
        }
        cout << endl;
    }
}

//end

There is no need here to use dynamic or local memory.  The function c_str()
already returns valid memory.  You can use a type cast "(char*)" to convert
from const char* to char*.  However, this is NOT a good idea.  I don't even
know if any compilers aside from my own support it.  What you're doing is
harmless, but in general, const char*'s are constant because people don't
want that memory to change.  It would be better to declare c to be a const
char* in this case, just to be safe.  You wouldn't have to do that funny type
cast and you would still be able to traverse the list.

//start

void break (const vector<string>* doc)
{
    vector<string>::iterator i;
    const char* line;

    for (i=doc->begin(); i != doc->end(); i++)
    {
        //strcpy(line, (*i).c_str());  // copy to remove const of
(*i).c_str()
        line = (*i).c_str();
        while (*line != '\0')
        {
            cout << *line << " ";
            ++line;
        }
        cout << endl;
    }
}

//end

You don't need a nonconstant pointer.  You may be getting const char * mixed
up with a char* const.

Example:

const char* a = "I'm constant but traversable."
char* const b = "You can change the first letter, but you can't move me."

*a = 'X';    //compiler error
a++;          //okay

*b = 'X'     //okay
b++           //compiler error

//end

With a const char*, you know it can't be changed.  With a char* const, you
know it can't be moved.  This can be extrapolated to any type.

struct Mystruct {};

const char*                        char* const
const int*                           int* const
const Mystruct*                 Mystruct* const

The following is a bit of rambling on the 'this' pointer.  Continuing is
extremely optional, though I'd appreciate it if people would point out the
problems in my reasoning.

The 'this' pointer is of the type structname* const.  You can always count on
the this pointer to point to the current object because it can't be moved.
However, one can use the 'this' pointer to change the current object's
properties.

Here's why:

struct A
{
    int bar;

    void foo(void)
    {
        this->bar = 2;  //Okay
        A* notgood = (A*) this + 1;  //Not good...
        notgood->bar = 5;
     }
};

With this structure, the member function must assume that you allocated two
members, and that the adjacent memory is a valid structure (and not
Windows).  There is no way to tell inherently if the adjacent memory is
'good' or not (that I know of).

A aobject[2];

aobject[0].foo();    //works
aobject[1].foo();    //memory overwrite!  A::foo() attempts to access
aobject[2]
                             //which is not allocated!

Since no one wants the this pointer to move around, it is the C++ standard,
and unless you get a really weird compiler, you can be sure that the this
pointer does not move.

This is still a problem:

A* a = NULL;
a->foo();    //Now foo() has to work with a NULL pointer.  'this' doesn't
point to
                  //valid memory because no memory was allocated in the first
place.

**Note, in any member function this is assumed, so if you wrote A::foo() like
this

foo()
{
    bar = 2;
}

the compiler would add the 'this' pointer for you.

foo()
{
    this->bar = 2;
}

'this' is a hidden argument that gives the member function access to the
current object.

A aobject;

aobject.foo();
/*    Becomes:

    A::foo(&aobject);

    A::foo() is just a regular function with some restrictions, and a hidden
first argument, the 'this' pointer.  That's how member functions tell which
object to do their stuff with.


      [ Send an empty e-mail to c++-help@netlab.cs.rpi.edu for info ]
      [ about comp.lang.c++.moderated. First time posters: do this! ]


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]