简体   繁体   English

函数返回指针的不确定行为

[英]Undefined behaviour with function returning pointer

EDIT: The problem was not undefined behaviour, but rather "mis-use" of char-arrays 编辑:问题不是未定义的行为,而是字符数组的“滥用”

I haven't worked a lot with pointers and dynamic memory allocation, so I decided I'd try to make a really simple encrypter using these methods. 我在指针和动态内存分配方面工作不多,所以我决定尝试使用这些方法制作一个非常简单的加密器。 (this encrypter isn't supposed to be great, it uses Caesars method, only +1 instead of 3, and uses symbols in between the letters to make it harder to decrypt, not here for criticism on the algorithm) (这种加密程序本来就不理想,它使用Caesars方法,仅​​+1而不是3,并且在字母之间使用符号使解密变得更加困难,此处不批评算法)

The problem I'm facing I believe is undefined behaviour, but I don't really see how this happens. 我认为我所面临的问题是不确定的行为,但我并没有真正看到这种情况如何发生。 Say I want "Hello" to be encrypted, it only prints 'I', which comes after 'H' in the alphabet, but it stops there and the program becomes unresponsive, so I suppose the problem lies in the else part of the for loop. 假设我希望对“ Hello”进行加密,它仅打印“ I”,该字母在字母“ H”之后,但是在此停止并且程序变得无响应,因此我想问题出在for的else部分环。 The compiler also warns me about heap corruption, but after looking through this page I think I'm freeing the memory correctly. 编译器还警告我有关堆损坏的信息,但是浏览完此页面后,我认为我正在正确释放内存。

#include <iostream>
#include <string>
#include <ctime>

using namespace std;

char * enc(string);

int main()
{
    string foo = "Hello";

    char * bar = enc(foo);

    cout << *bar;

    delete[] bar;

    cin.get();

    return 0;
}

char * enc(string str)
{
    char * encrypted = new char[int(str.size())];
    srand(unsigned int(time(NULL)));

    // Disguise symbols for obscurifying text
    char * disg = new char[37]
    {
        //37 symbols, unrelevant and takes a lot of space.
    };

    for (int i = 0; i < str.size(); i++)
    {
        encrypted[i] = int(str[i]) + 1;
        if (i == str.size())
            break;
        else
            encrypted[i + 1] = disg[rand() % 37];
    }

    delete[] disg;

    return encrypted;
}

As a sidenote, I do realize that vectors might be better for this purpose, but I haven't gotten into that just yet, this is for practicing memory management. 附带一提,我确实意识到矢量可能会更好地达到此目的,但是我还没有深入到这,这是为了练习内存管理。

cout << *bar;

is printing the first character that bar points to. 正在打印栏指向的第一个字符。

You want 你要

cout << bar;

But for that you would need to null terminate the returned string. 但是为此,您需要将终止返回的字符串为null。

Side notes: please don't use raw pointers. 旁注:请不要使用原始指针。 disg should be for example a std::array . 例如, disg应该是std::array And enc should return a std::string . enc应该返回一个std::string

if (i == str.size() ) can never be true inside your loop. Also as Some programmer dude says in his answer you are writing out of bounds in the line if (i == str.size()can never be true inside your loop. Also as Some programmer dude says in his answer you are writing out of bounds in the line can never be true inside your loop. Also as Some programmer dude says in his answer you are writing out of bounds in the line encrypted[i + 1] = disg[rand() % 37] ; can never be true inside your loop. Also as Some programmer dude says in his answer you are writing out of bounds in the line加密[i + 1] = disg [rand()%37] ; . But you will better see that if you remove the wrong if. 但是您会更好地看到,如果删除错误的if。

And for (int i = 0; i < str.size(); i++) could be rewritten as for(auto c : str) . 并且for (int i = 0; i < str.size(); i++)可以重写为for(auto c : str)

Lets take a closer look at the loop in the enc function: 让我们仔细看看enc函数中的循环:

for (int i = 0; i < str.size(); i++)
{
    encrypted[i] = int(str[i]) + 1;
    if (i == str.size())
        break;
    else
        encrypted[i + 1] = disg[rand() % 37];
}

The loop will iterate with i from 0 to str.size() - 1 (inclusive). 循环将使用i0迭代到str.size() - 1 (含str.size() - 1 )。 That means the condition inside the loop, i == str.size() will never be true, and you will for the last iteration write out of bounds in the else clause. 这意味着循环内的条件i == str.size() 永远不会为真,并且对于最后一次迭代,您将在else子句中写出边界

The solution is to change the condition in the if to i == str.size() - 1 . 解决方案是将if的条件更改为i == str.size() - 1


There are some other problems as well, but none that will lead to UB like the above. 还有其他一些问题,但没有一个会像上面那样导致UB。 For example, the value you write to encrypted[i + 1] will be overwritten the very next iteration. 例如,您写入encrypted[i + 1]将在下一个迭代中被覆盖。

but I don't know how I can initialize a string with dynamic size 但我不知道如何初始化具有动态大小的字符串

I've taken your code and modified it, to use a std::string and removed the allocations as they are simply not needed here. 我已将您的代码修改为使用std::string并删除了分配,因为这里根本不需要它们。

Please read up on the code, debug it, and check the comments. 请阅读代码,对其进行调试,然后检查注释。

class Enc
{
public:
    //For arrays of fixed size, you may use this to obtain the size of the given array
    template <typename T, std::size_t N>
    constexpr static std::size_t FixedArraySize(T(&arr)[N])
    {
        return N;
    }

    //The parameter should not be modified, pass it as constant value by reference
    static std::string enc(const std::string &str)
    {
        //It looks like your symbols do not change, let's make them static to initialize only once
        static char symbols[] =
        {
            'A', 'x', '!' //...
        };

        auto originalStringSize = str.size();

        //Create the destination string and resize it to the source string's length
        std::string encrypted;
        encrypted.resize(originalStringSize);

        //Your loop
        for (int i = 0; i < originalStringSize; ++i)
        {
            //Make safe cast, which is also obvious to the reader of the code
            encrypted[i] = static_cast<int>(str[i]) + 1;

            //Fixed with - 1 to break correctly
            if (i == (originalStringSize - 1))
                break;
            else
                encrypted[i + 1] = symbols[rand() % FixedArraySize(symbols)]; //Notice the helper function
        }

        //Return the 'encrypted' string
        return encrypted;
    }
};

int wmain(int, wchar_t**)
{
    std::string testString = "Hello World";

    //Call srand only once in your program !
    srand(static_cast<unsigned int>(time(nullptr)));
    auto encrypted = Enc::enc(testString);

    std::cout << encrypted << std::endl;

    return 0;
}

As a sidenote, I do realize that vectors might be better for this purpose, but I haven't gotten into that just yet, this is for practicing memory management. 附带一提,我确实意识到矢量可能会更好地达到此目的,但是我还没有深入到这,这是为了练习内存管理。

You could use a std::vector but it is here also not neccessary. 您可以使用std::vector但这在这里也不是必需的。 You will rarely have to deal with raw pointers in modern C++. 您几乎不需要在现代C ++中处理原始指针。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM