[英]Pointers and std::string - Strange behavior - C++
我提前道歉,因为我在之前的帖子中问了同样的问题但是,正如有人正确指出的那样,我没有发布真正的代码。 因此,我再次提出同样的问题,试图比以前更清楚。
作为练习,我正在创建一个操纵字符串的程序。 特别是,我想删除2'*'之间的部分字符串。 我要强调我已经使用库字符串的功能成功创建了相同的程序; 事实上,问题涉及使用char指针操纵给定字符串。 我将发布完整的代码并进行深入讨论。
#include <iostream>
#include <string>
using namespace std;
int main() {
string frase;
getline (cin, frase); // Takes as input the phrase
int size = frase.size();
cout << frase[0]; // <- this line is not even processed (I've used it to test the problem) However, if I put it before the first if, it will be sent in output.
char* pa1 = NULL; // The pointer which will "point" to the first *
char* pa2 = NULL; // The pointer which will "point" to the second *
bool stop = false; // When the pointers find 2 asterisk, stop = true
for(int i = 0; i < size - 1 || stop == true; i++){ // FOR LOOP n.1
if(frase[i] == '*'){
if(*pa1 == '*'){
pa2 = &frase[i];
stop = true;
}
pa1 = &frase[i];
}
}
// I've debugged the program and find that the problem is before this line, probably
// linked to the address of pointers. I will explain later what I mean.
// I've came up with this conclusion after trying to ignore part of the program and processing it in another file.
// However, I'm not fully sure with this result, since the problem regards the visualization of the content of the pointers.
if(pa2 == NULL){ // if it's a null pointer, this means that second asterisk has not been found.
if(pa1 == NULL){// if also this is a null pointer, there is no asterisk at all
cout << "Non ci sono asterischi. Non verrà eliminata nessuna parola.\n\n";
}
cout << "C'è un solo asterisco. Verrà eliminato unicamente l'asterisco.\n\n";
for(; pa1 < &frase[size - 1]; pa1++){ // FOR LOOP n.2
*pa1 = *(pa1 + 1);
}
}
else{
for(; pa1 < pa2 + 1; pa1++){ // this removes asterisk and
//the phrase between them, by overwriting the existing characters. FOR LOOP n.3
*pa1 = *(pa1 + 1);
}
}
cout << "La frase dopo l'eliminazione è:\n" << frase;
return 0;
}
在发布之前,我做了一些努力来了解问题的本质。 我看到了一个意外的行为:如果我初始化指向内存地址的指针,例如:
pa1 = &frase[i];
哪个不包含任何星号,然后,(由于for循环n.1中的'if'条件)将其地址更改为第一个星号后,我尝试通过写入来可视化它(忽略其余的代码) :
cout << *pa1;
程序不会崩溃并输出星号。 但是,对pa2执行相同操作并创建带有2个星号的短语会导致程序崩溃。 将指针pa1初始化为“NULL”并执行相同的过程会导致程序崩溃。
然后我想出了2个小说 :
1 - 也许我不能管理与字符指针一个String对象 ,虽然我只是管理给定的字符串的字符 。 但是,如果我将指针初始化为字符串的现有地址,我可以轻松地显示该短语的字符。
2 - 问题与我处理空字符的事实有关 ,例如“空格”等。 因此,问题位于for循环n.2或n.3(指代码)。
我知道我可以处理char []数组的问题 , 我知道处理字符串函数的问题会更好 ,但我想解决这个问题,以便完全理解字符串对象和字符串指针之间的相关性。 所以我只是请求帮助在这段代码中找到错误; 我不想要一个新的代码(因为这对你来说是浪费时间,而且它会以某种方式利用你,即使我们正在谈论一项独立的练习)。 我希望我能清楚地解释这个问题。 先感谢您。
编辑:我忘了指出,我也认为问题可能与大小是一个以字节为单位的int值有关 ,而我把它当作字符串中包含一个字符的插槽数。 我认为这些信息很有用,但我不确定它的可用性。
编辑2:@lilscent解决了与空指针的deference关联的问题。 我已经更改了代码并初始化了pa1指针和pa2指针
pa1 = &frase[0];
pa2 = nullptr;
编辑3:按照建议,我删除了布尔变量,我在第一个循环中使用了break。 我也正在改变最后一个for循环,因为代码现在可以工作,但是没有做它应该做的事情。 我还编辑了第二个循环添加,否则:
if(pa2 == nullptr){
if(pa1 == &frase[0]){
cout << "Non ci sono asterischi. Non verrà eliminata nessuna parola.\n\n";
}
else{
cout << "C'è un solo asterisco. Verrà eliminato unicamente l'asterisco.\n\n";
for(; pa1 < &frase[size - 1]; pa1++){
*pa1 = *(pa1 + 1);
}
*pa1 = ' ';
}
}
编辑4:现在程序完全正常。 我编辑了最后一个循环:
else{
*pa2 = ' ';
pa2+= 2;
for(; pa1 < pa2 + 1 && pa2 < &frase[size]; pa1++, pa2++){
*pa1 = *pa2;
*pa2 = ' ';
}
*pa2 = ' ';
}
感谢您的帮助和所有建议! 我已经离开了代码,因为它是帮助其他人遇到同样类型的问题。
最终编辑:请参阅NikosC。的帖子。 他修改了部分程序并提高了效率,解决了大部分问题。 再次感谢!
循环中的逻辑不起作用。 循环条件说:
for (int i = 0; i < size - 1 || stop == true; i++)
因此,只要i < size - 1
或stop == true
这将运行。 但是,你想要的是当stop == true
时循环停止,而不是继续运行。 所以你需要:
for (int i = 0; i < size && !stop; i++)
请注意,它是i < size
,而不是i < size - 1
。 std::string::size()
不包括终止\\0
字符。
在循环内,你有:
if (frase[i] == '*') {
if (*pa1 == '*') {
pa2 = &frase[i];
stop = true;
}
pa1 = &frase[i];
}
如果找到*
,则表示您正在检查pa1
指向星号。 但是,这将导致空指针取消引用,因为pa1
被初始化为null。 你应该做什么,只是测试pa1
是否仍然是一个空指针。 如果是,则表示您没有找到第一个*
。 所以这样做:
if (pa1 == nullptr) {
// Since pa1 is still null, this is the first '*' we encountered.
pa1 = &frase[i];
} else {
// pa1 was not null, so this means we just found the second '*'.
pa2 = &frase[i];
stop = true;
}
这个新逻辑允许您以不再需要stop
的方式重写循环条件。 您只需检查pa2
是否为空。 如果它仍为null,则循环可以继续运行。
整体而言:
char* pa1 = nullptr; // The pointer which will "point" to the first *
char* pa2 = nullptr; // The pointer which will "point" to the second *
for (int i = 0; i < size && pa2 == nullptr; i++) {
if (frase[i] == '*') {
if (pa1 == nullptr)
pa1 = &frase[i];
else
pa2 = &frase[i];
}
}
(另外,更喜欢使用nullptr
而不是NULL
。它将防止在使用NULL
时隐藏的某些类型的错误。)
但是,您可以使用基于范围的for循环进一步简化上述操作,这是迭代容器的所有元素的推荐方法。 您需要使用迭代引用( auto&
而不是auto
),因为我们需要获取实际元素的地址而不是元素副本的地址:
for (auto& i : frase) {
if (i == '*') {
if (pa1 == nullptr) {
pa1 = &i;
} else {
pa2 = &i;
break; // stop the loop since we found the second *
}
}
}
接下来,您有代码尝试并打印结果:
if (pa2 == NULL) {
if (pa1 == NULL) {
cout << "Non ci sono asterischi. Non verrà eliminata nessuna parola.\n\n";
}
cout << "C'è un solo asterisco. Verrà eliminato unicamente l'asterisco.\n\n";
for ( ; pa1 < &frase[size - 1]; pa1++) {
*pa1 = *(pa1 + 1);
}
}
这可不行,因为你试图反引用pa1
即使它可能为空。 在我看来,你想要做的只是发出一条错误消息,声明没有星号,或者只有一个星号,只需删除那个星号:
if (pa1 == nullptr) {
cout << "C'è un solo asterisco. Verrà eliminato unicamente l'asterisco.\n";
return 0;
}
if (pa2 == nullptr) {
cout << "Non ci sono asterischi. Non verrà eliminata nessuna parola.\n";
pa2 = pa1;
}
对于最后一部分,要删除字符串的*text*
部分,您只需要将位置pa2
的字符复制到位置pa1
并调整frase
:
while (pa2 < &frase[size]) {
++pa2;
*pa1 = *pa2;
pa1++;
}
frase.resize(size - 1 - (pa2 - pa1));
cout << "La frase dopo l'eliminazione è: " << frase << '\n';
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.