函数返回指针的未定义行为

时间:2018-01-24 08:34:18

标签: c++ memory undefined-behavior

编辑:问题不是未定义的行为,而是“错误使用”char数组

我没有使用指针和动态内存分配,因此我决定尝试使用这些方法制作一个非常简单的加密器。 (这个加密器不应该很好,它使用Caesars方法,只有+1而不是3,并且在字母之间使用符号使其更难解密,而不是在这里批评算法)

我所面临的问题我认为是未定义的行为,但我真的不知道这是怎么发生的。假设我希望“Hello”加密,它只打印'I',它出现在字母表中的'H'之后,但它停在那里并且程序没有响应,所以我认为问题在于else for循环的一部分。编译器还警告我有关堆损坏的信息,但在查看此页面后,我认为我正在释放内存。

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

作为旁注,我确实意识到向量可能更好用于此目的,但我还没有进入那个,这是为了练习内存管理。

3 个答案:

答案 0 :(得分:1)

cout << *bar;

正在打印条指向的第一个字符。

你想要

cout << bar;

但是为此你需要null来终止返回的字符串。

附注:请不要使用原始指针。 disg应为std::arrayenc应返回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已加密[i + 1] = disg [rand()%37] ;。但是如果你删除错误的话,你会更好地看到。

for (int i = 0; i < str.size(); i++)可以改写为for(auto c : str)

答案 1 :(得分:1)

让我们仔细看看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];
}

循环将使用i0str.size() - 1(包括)进行迭代。这意味着循环中的条件,i == str.size() 永远不会为真,并且您将在最后一次迭代中在else中写出越界子句。

解决方案是将if中的条件更改为i == str.size() - 1

还有一些其他问题,但没有一个会像上面那样导致UB。例如,您写入encrypted[i + 1]的值将在下一次迭代时被覆盖。

答案 2 :(得分:1)

  

但我不知道如何初始化动态大小的字符串

我已经使用了您的代码并对其进行了修改,以使用std::string并删除了分配,因为这里根本不需要它们。

请阅读代码,调试代码并查看评论。

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

作为旁注,我确实意识到向量可能更好用于此目的,但我还没有进入那个,这是为了练习内存管理。

您可以使用std::vector但这里也不是必需的。您很少需要处理现代C ++中的原始指针。