Using PQexecparams with a char** array

In short, if you are creating an array of arguments to pass to PQexecparams() (i.e. char**) then you need to leave enough space to null terminate the array.

Many C programs use a while loop to iterate through an array until they find a null pointer i.e.

while (*argv) { 
    //foo 
    ++argv;
}

 

As soon as *argv = ‘\0’ the loop exits (which is why the array needs a the final element to be null terminated).

It is not documented that PQexecparams() requires a null terminated array of null terminated C-strings, but it does appear as though this is necessary as I got a particularly nasty segmentation fault which took quite a bit of tracking down prior to finding this out.

If you are constructing an array ** that will interact with a C library then it is probably prudent to terminate it thus:

#include <string>
#include <vector>
#include <cstring>
#include <iostream>

std::vector<std::string> create_array(int nRecords) {

    std::vector<std::string> myStrings;
    
    for(int i =0; i != nRecords; ++i)
        myStrings.push_back("String " + std::to_string(i +1));

    return(myStrings);
    
}

int main() {

    char 		**myCharArray 	        = nullptr;
 	const int 	nRecords 		= 10;
    
 	std::vector<std::string> vecStrings = create_array(nRecords);
    
    //+1 FOR NULL TERMINATOR
 	myCharArray = new char *[nRecords +1];
    
    //BUILD CHAR ARRAY
 	int count = 0;
    for(std::string &it : vecStrings) {
 		myCharArray[count] = new char[it.size() +1];
 		strncpy(myCharArray[count], it.c_str(), it.size() +1);
         ++count;
 	}
    
    myCharArray[nRecords] = nullptr;
    
    //OUTPUT
    for(int i =0; i != nRecords; ++i) {
        std::cout << myCharArray[i] << std::endl;
    }
    
    //CLEAN UP
    for(int i =0; i <= nRecords; ++i) {
        delete[] myCharArray[i];
        myCharArray[i] = nullptr;
    }
    
    delete[] myCharArray;
    myCharArray = nullptr;
    
}

Just remember that when iterating through it (particularly when writing) you should use “for( i != nRecords)” (i.e. not equal to array size) because you do not want to do not want to overwrite your null terminator.  As a general rule it is advisable to terminate the array procedurally after you have loaded it up with data.

What happened on my machine was that PQexecparams() worked perfectly, but I got a nasty segmentation fault on the next call.  On running gdb and doing a backtrace the final call was to pqexpbuffer which is part of libpq.

 

NOTE: This is not the first time that I have encountered an array that needs to be terminated with a null pointer, indeed it is in the Ncursses documentation for both the FIELD** and ITEM** arrays so I will be terminating all the arrays that I create in this manner in future.  Just make sure that if you do terminate an array that you create enough elements to accommodate the null terminator ( i.e. [nRecords +1] )or you will run into a whole new barrel of monkey fun.

If I am using enums I usually make the last 2 elements TERMINATOR and MAX_ELEMS or something similar

enum {
    ....,
    TERMINATOR,
    MAX_ELEMS
};

ITEM *myArray[MAX_ELEMS];

This will help to avoid the off by one error which occurs because the array starts at 0 and is a little more explanatory than declaring [MAX_ELEMS +1].