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