使用指针重新复制字符数组并修剪前导空格

5
我正在自学C++,并正在解决以下问题:
编写一个名为trimfrnt()的函数,可以删除字符串中所有前导空格。使用指针编写该函数,返回类型为void。
我对此问题的尝试如下所示,并尝试了两种方法来解决此问题(您可以看到我的两个函数trimfrnt1()和trimfrnt2())。 trimfrnt1()运行良好,但是我不确定它确切地为什么有效。 我对for循环感到困惑。下面是msg数组的图示:
             |<--ptrMsg--------->|
  |<------for loop---->|
   0    1    2   3     4   5     6    7
+----+----+----+----+----+----+----+----+
|    |    |  G |  R |  E |  A |  T | \0 |
+----+----+----+----+----+----+----+----+
|  G |  R |  E |  A |  T |    |    | \0 |                             
+----+----+----+----+----+----+----+----+                                

问题1

从我的上图中,由于重叠,我实际上希望输出的文本是:"GREATAT"。但为什么整个字符串都被移动和重新初始化了呢?因为我只循环了5个字母?

问题2

题目要求使用指针,所以我认为trimfrnt1是作弊,因为我在索引。我试图使用另一种方法trimfrnt2。但我卡在了while循环中:

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

我的代码这一部分无法工作。当我输出*(ptrMsg + count)时,我得到了正确的字符,但是当我把它赋给*ptrMsg的内容时,我得到了乱码字符。在这种情况下,因为我没有重新初始化剩余的字符,所以我也期望得到"GREATAT"。是否有一种指针方法可以实现这一点呢?

谢谢!

#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;
}

问题中“使用指针写函数”的部分可能意味着输入的字符串将是一个指针。 - Harshil Sharma
trimfrnt1 函数中,你的 for 循环实际上循环次数过多。它循环了 size 次,但你从中复制的位置超出了数组的末尾。这会导致未定义行为,在你的情况下(调试器?)似乎可以工作。 - Hulk
在函数 trimfrnt1 中,请检查复制字符串数据的 for 循环。 - Harshil Sharma
3个回答

3
trimfrnt1()存在未定义的行为 - 您的for循环导致ptrMsg超出了数组范围,因此您正在读取不属于您的内存,并且任何事情都可能发生。看起来在您的平台上,这样的读取是可以的,所以循环愉快地从内存中复制“随机”的垃圾 - 但在此之前,它会复制终止的NUL字符,因此您无法得到"GREATAT"作为结果。您应该像这样更改while循环:
while(*ptrMsg == ' ')
{
  ptrMsg++;
  --size;
}

这仍然只会给你一个"GREAT",因为终止的NULsizeof结果的一部分。
trimfrnt2()中,你的循环有误,因为你正在增加count,而你不应该这样做。这就是为什么给变量起非常描述性的名称很重要。如果它按其语义命名(例如numberOfSpaces),你甚至不会考虑在复制while()循环中编写增量。如果你去掉那个增量,trimfrnt2()应该会给你期望的"GREATAT"结果。
为了启发你,这是我如何使用指针而不使用算术实现它的方法:
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
}

正如您所看到的,这个版本甚至没有使用size参数,因此可以将其删除。


非常好的观点。感谢示例代码-非常优雅。while( *src)是什么意思?这是否在不是空指针时执行? - user1527227
@user1527227 *src 的类型是 char,因此它是一个整型表达式。整型表达式会隐式转换为 bool,使得 0 变成 false,而其他所有值都变成 true。因此,这与说 while (*src != 0) 是相同的,而 while (*src != '\0') 又与之相同。 - Angew is no longer proud of SO

1
"

trimfrnt2

中的问题是:"
*ptrMsg = *(ptrMsg + count);
ptrMsg++;
count++;

它将同时推进源和目标中的 ptrMsg。您应该删除 count++。循环后,您应该在结果中放置 \0
while( *(ptrMsg + count) != '\0' )
{
    *ptrMsg = *(ptrMsg + count);
    ptrMsg++;
    //count++;
}
*ptrMsg = '\0';

它可能会对您有所帮助

void trim_left(char *msg)
{
    char *src = msg;
    char *dst = msg;

    while (*src == ' ')
        src++;

    if (dst == src)
        return; // Don't need trim

    while (*src)
        *dst++ = *src++;

    *dst = 0;
}

谢谢!数组中已经有一个\0了。当你再添加一个时会发生什么? - user1527227
\0应该在最后一个字符之后,而不是在数组的末尾,否则你会在修剪后的字符串旁边得到未修剪的字符串。此外,我添加了一个修剪函数,也许你会喜欢它。 - masoud
非常优雅 - 谢谢! while (*src) 是什么意思?换句话说,什么时候为真? - user1527227
src 等于 \0(零表示字符串的结尾)时。 - masoud

-1
关于垃圾值,这是因为在第二个 while 循环之后,您正在使用 Msg(char)分配一个 ptrMsg(char 指针)。 应该是

*ptrMsg=msg // or ptrMsg=&msg 

此外,您在cout中打印ptrMsg,这会打印地址而不是值。
至于为什么是GREAT而不是GREATAT,那是因为msg和ptrMsg具有非常不同的地址,即您没有使用ptrMsg重写msg。在定义ptrMsg时,您将msg的内容复制到了ptrMsg中,并没有分配地址...
ptrMsg=&msg  // ptrMsg contains address of string msg
*ptrMsg=msg  // address pointed by ptrMsg has same content as msg,but both have different address altogether 

最重要的是,你的问题要求你对原始消息本身进行更改。也就是说,在主函数中,在调用 trim 函数之前,你必须将 msg =' GREAT',在此之后,同样的 msg 应该具有“GREAT”作为其内容。 - Sumedh

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接