返回本地数组会损坏数组数据

Returning a local array corrupts array data

我通过创建一个像素数组来生成随机噪声,然后我用它来创建图像。代码有效,编译器没有显示任何问题,除了一条警告:warning: address of local variable 'tmp' returned [-Wreturn-local-addr]

#include <SFML/Graphics.hpp>
#include <vector>
#include <stdlib.h>
#include <time.h>

sf::Uint8* getPerlinArray(unsigned int width, unsigned int height){
    sf::Uint8 tmp[width * height * 4];

    for(unsigned int i = 0; i < width * height * 4; i+=4){
        sf::Uint8 value = rand() % 256;
        tmp[i] = value;
        tmp[i + 1] = value;
        tmp[i + 2] = value;
        tmp[i + 3] = 255;
    }

    return tmp;
}

sf::Image getPerlinImage(unsigned int width, unsigned int height){

    sf::Uint8* arr = getPerlinArray(width, height);

    sf::Image img;
    img.create(width, height, arr);

    return img;
}

int main(){

    //Set random seed.
    unsigned int seed = time(NULL);
    srand(seed);

    //Create window
    sf::RenderWindow window(sf::VideoMode(200, 200), "Random Noise");

    sf::Image img = getPerlinImage(64, 64);

    sf::Texture txt;
    txt.loadFromImage(img);

    sf::Sprite sprite;
    sprite.setTexture(txt);

    while (window.isOpen()){
        sf::Event event;
        while (window.pollEvent(event)){
            if (event.type == sf::Event::Closed)
                window.close();
        }

        window.clear(sf::Color::Green);
        window.draw(sprite);
        window.display();
    }

    return 0;
}

我得到的结果是这样的:

您可以清楚地看到噪声发生器可以正常工作,但正如我们在我绘制的图像底部看到的那样,数组似乎已损坏。我发现 "corruption" 发生在我对数组进行 return 处理时。如果将 sf::Uint8 tmp[width * height * 4](我定义了 widthheight)放在 getPerlinArray 函数之外,那么 tmp 是全局的,数组就不会被破坏,并且程序正常工作。有没有办法 return 局部变量(在本例中为数组)而不损坏它?

您应该return 动态分配内存,最好来自 RAII 容器:

std::vector<sf::Uint8> getPerlinArray(unsigned int width, unsigned int height){
    std::vector<sf::Uint8> tmp(width * height * 4);
    //identical code goes here.
    return tmp;
}

然后:

auto arr = getPerlinArray(width, height);

sf::Image img;
img.create(width, height, arr.data());

由于您使用的是像 C++ 这样的语言,因此您有很多选择

1 - 您可以将其作为参数而不是返回值。

void getPerlinArray(unsigned int width, unsigned int height, sf::Uint8* ){
    for(unsigned int i = 0; i < width * height * 4; i+=4){
        sf::Uint8 value = rand() % 256;
        tmp[i] = value;
        tmp[i + 1] = value;
        tmp[i + 2] = value;
        tmp[i + 3] = 255;
    }

    return tmp;
}

sf::Image getPerlinImage(unsigned int width, unsigned int height){

    sf::Uint8 arr[width * height * 4];
    getPerlinArray(width, height, arr);

    sf::Image img;
    img.create(width, height, arr);

    return img;
}

2 - 您可以扩展悬挂指针的范围。例如,您可以使其成为全球性的。如果 width*height 在编译时已知,只需将 sf::Uint8 arr[width * height * 4]; 移动到全局范围。

3 - 您可以使用 C++ 并使用 std::vector 而不是 C 数组。所以你的代码看起来像

std::vector<sf::Uint8> getPerlinArray(unsigned int width, unsigned int height){
   std::vector<sf::Uint8> tmp(width * height * 4);

    for(unsigned int i = 0; i < width * height * 4; i+=4){
        sf::Uint8 value = rand() % 256;
        tmp[i] = value;
        tmp[i + 1] = value;
        tmp[i + 2] = value;
        tmp[i + 3] = 255;
    }

    return tmp;
}

sf::Image getPerlinImage(unsigned int width, unsigned int height){

    std::vector<sf::Uint8> arr = getPerlinArray(width, height);

    sf::Image img;
    img.create(width, height, arr.data());

    return img;
}

还有许多其他解决方案,例如使用 malloc、new 等进行动态分配。