Returning a local array corrupts array data

142 views Asked by At

I am generating random noise by creating an array of pixels, that I then use to create an image. The code works, and the compiler shows no problems, except a warning that says: 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;
}

The result I get is this:

1]

You can clearly see that the noise-generator works, but the array seems to get corrupted as wee see at the bottom of the image I am drawing. I found that the "corruption" occurs when I am returning the array. If put sf::Uint8 tmp[width * height * 4] (and I define width and height) outside the getPerlinArray function, so that tmp is global, the array does not get corrupted and the program works like it should. Is there a way to return a local variable(array, in this case), without it getting corrupted?

2

There are 2 answers

3
AudioBubble On BEST ANSWER

You should return dynamically allocated memory, ideally from a RAII'd container:

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

and then:

auto arr = getPerlinArray(width, height);

sf::Image img;
img.create(width, height, arr.data());
0
Kadir Erdem Demir On

Since you are using a language like C++ you have many options

1 - Instead returning the value you can take it like a parameter.

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 - You can extend the scope of the dangling pointer. Forexample you can make it global. Just move sf::Uint8 arr[width * height * 4]; to global scope if width*height is known in compile time.

3 - You can use C++ and use std::vector instead of C arrays. So your code will look like

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

There are many other solutions like dynamic allocation with malloc, new etc..