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].

Recent Comments