libmysql: warning: address of local variable ‘rows’ returned (C++/C)

82 Views Asked by At

I'm using g++.

Implemented some wrapper for my convenience to work with libmysql. Also making C/C++ compatible code.

static MYSQL_RES *db_query(MYSQL *db, const char *query, va_list params) {
    char q[500];
    vsprintf(q, query, params);
    va_end(params);
    if (mysql_query(db, q)) {
        fprintf(stderr, "%s\n", mysql_error(db));
        return NULL;
    }

    MYSQL_RES *res = mysql_use_result(db);
    return res;
}

MYSQL_ROW* db_query_all(MYSQL *db, const char* query, ...) {
        va_list params;
    MYSQL_RES *res = db_query(db, query, params);

    int count = mysql_num_rows(res);

    MYSQL_ROW rows[count];
    for (int i = 0; i<count; i++) 
    {
        rows[i] = mysql_fetch_row(res);
    }

    mysql_free_result(res);
    return rows;
}

And getting warning during compilation:

warning: address of local variable ‘rows’ returned [-Wreturn-local-addr]
     MYSQL_ROW rows[count];

Please help me to find what's the problem there. I can't google something relevant to my particular case.

3

There are 3 best solutions below

3
kiran Biradar On BEST ANSWER

rows is local array to db_query_all function and will be destroyed once control exits the function thus it is undefined behavior to return reference of it.

You can dynamically allocate the memory and return reference to it.

MYSQL_ROW* db_query_all(MYSQL *db, const char* query, ...) {
        va_list params;
    MYSQL_RES *res = db_query(db, query, params);

    int count = mysql_num_rows(res);

    MYSQL_ROW *rows = malloc(sizeof(*rows)*count);
    for (int i = 0; i<count; i++) 
    {
        rows[i] = mysql_fetch_row(res);
    }

    mysql_free_result(res);
    return rows;
}

Note you need to free the memory once done processing.

5
dvhh On

Your error message comes from this line:

MYSQL_ROW rows[count];

which means that rows will be allocated on the stack, meaning that once your function exit the content of your variable will be undefined.

You need to either :

  • allocate the variable on heap (recommended)
MYSQL_ROW rows[] = calloc(count,sizeof(MYSQL_ROW));

don't forget to free the result once you no further need it

  • pass rows from upstream as an argument
    which would require you to get the count at another step
2
Pibben On

A more C++ way to do it is to define a movable buffer type:

struct Buf {
  Buf(size_t size) : mPtr(std::make_unique<MYSQL_ROW[]>(size)), mSize(size) {}

  std::unique_ptr<MYSQL_ROW[]> mPtr;
  size_t mSize;
};

Then you can implement the function like this:

Buf db_query_all(MYSQL *db, const char* query, ...) {
        va_list params;
    MYSQL_RES *res = db_query(db, query, params);

    int count = mysql_num_rows(res);

    Buf rows(count);
    for (int i = 0; i<count; i++) 
    {
        rows.mPtr[i] = mysql_fetch_row(res);
    }

    mysql_free_result(res);
    return std::move(rows);
}

The big upside of this approach is that you don't have to do manual memory handling.