Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Recopying character array using pointers and trimming leading white space

I'm learning C++ on my own and was working on this problem:

Write a function named trimfrnt() that deletes all leading blanks from a string. Write the function using pointers with the return type void.

My attempt at the problem is below and I tried solving this problem 2 ways (you can see my two functions trimfrnt1() and trimfrnt2(). trimfrnt1() works fine. I kind of got this working by accident. After I coded it, I wasn't sure why it exactly it worked. My confusion is at the for loop. I drew a diagram of the msg array below:

             |<--ptrMsg--------->|
  |<------for loop---->|
   0    1    2   3     4   5     6    7
+----+----+----+----+----+----+----+----+
|    |    |  G |  R |  E |  A |  T | \0 |
+----+----+----+----+----+----+----+----+
|  G |  R |  E |  A |  T |    |    | \0 |                             
+----+----+----+----+----+----+----+----+                                

Question 1

From my diagram above, I was actually expecting the text: "GREATAT" because of the overlap. How is it that the entire string got shifted and re-initialized because I only looped through 5 letters?

Question 2

The question states to use pointers so I though trimfrnt1 was cheating because I was indexing so I tried to do another method of trimfrnt2. This function I'm stuck at the while Loop:

    // shift characters to beginning of char array
    while( *(ptrMsg + count) != '\0' )
    {        
        *ptrMsg = *(ptrMsg + count);
        ptrMsg++;
        count++;
    }

This part of the code is not working for me. When I print out *(ptrMsg + count), I get the right characters, but when I assign it to the contents of the *ptrMsg, I get garbled characters. In this case, I was also expecting "GREATAT" because I didn't reintialize the remaining characters. Is there a way to do this using a pointer method as I am trying to do?

Thank you!

#include<iostream>
#include<iomanip>

using namespace std;

void trimfrnt1(char msg[], int size)
{
    char *ptrMsg = msg;

    // Find beginning of text
    while(*ptrMsg == ' ')
        ptrMsg++;

    // Copy text to beginning of array
    for(int i=0; i < size; i++)
        msg[i] = *ptrMsg++;

    // Reset pointer to beginning of array
    ptrMsg = msg;

    // Print array
    cout << "new msg1: ";
    cout << "\"" << ptrMsg << "\"" << endl;    
    cout << endl;

    return;
}

void trimfrnt2(char msg[], int size)
{
    int count = 0;      // used to find leading non-white space
    char *ptrMsg = msg; // pointer to character array

    // find first place of non white space
    while( *(ptrMsg + count) == ' ')
        count++;

    cout << "count = " << count << endl;

    // shift characters to beginning of char array
    while( *(ptrMsg + count) != '\0' )
    {        
        *ptrMsg = *(ptrMsg + count);
        ptrMsg++;
        count++;
    }
    cout << "count = " << count << endl;

    // Reset pointer to beginning of array
    ptrMsg = msg;

    // Print array
    cout << "new msg2: ";
    cout << "\"" << ptrMsg << "\"" << endl;    
    cout << endl;
}


int main()
{
    char msg[] = "  GREAT";
    const int size = sizeof(msg)/sizeof(char);

    cout << "Orginal msg:\"" << msg << "\"" << endl;

    trimfrnt1(msg, size);

    return 0;
}
like image 980
user1527227 Avatar asked Oct 20 '22 20:10

user1527227


1 Answers

trimfrnt1() has undefined behaviour - you for loop causes ptrMsg to reach beyond the array and thus you're reading memory which doesn't belong to you and anything could happen. It seems that on your platform, such reads are OK, so the loop happily copies the "random" junk from memory - but before that, it copies the terminating NUL character, so you cannot get "GREATAT" as a result. You should change the while loop like this:

while(*ptrMsg == ' ')
{
  ptrMsg++;
  --size;
}

This will still give you just "GREAT", because the terminating NUL is part of the sizeof result.

In trimfrnt2(), your loop is wrong as you're incrementing count, which you shouldn't be. That's why it's good to give really descriptive names to variables. If it was called after its semantics (e.g. numberOfSpaces), you wouldn't even consider writing the increment in the copying while() loop. If you remove that increment, trimfrnt2() should give you the "GREATAT" result you expect.


For inspiration, here is how I would implement this using pointers without arithmetic:

void trimfrnt3(char *msg, size_t size)
{
  const char *src = msg;
  while (*src == ' ')
    ++src;
  char *dst = msg;
  while (*src)
  {
    *dst = *src;
    ++src;
    ++dst;
  }
  cout << "new msg: \"" << msg << "\"\n";  //don't use endl unless you want to flush immediately
}

As you can see, this version doesn't even use the size parameter, which could thus be removed.

like image 168
Angew is no longer proud of SO Avatar answered Nov 15 '22 07:11

Angew is no longer proud of SO