I am trying to create a multimap indexed on a C-style string, as shown in the following code segment:
#include <cstring>
#include <map>
#include <iostream>
using namespace std;
int main(void)
{
int i, j;
int (*fn_pt)(const char *, const char *) = strcmp;
multimap<char *, char *, int (*)(const char *, const char *)>a(fn_pt);
for (i = 0; i < 2; i++)
{
char key[2];
sprintf(key, "%d", i);
for (j = 0; j< 5; j++)
{
char value[2];
sprintf(value, "%d", j);
a.insert(pair<char *, char *>(key, value));
}
}
for (i = 0; i < 2; i++)
{
char key[2];
sprintf(key, "%d", i);
multimap<char *, char *>::iterator it = a.find(key);
while (it != a.end())
{
cout << it->first << "\t" << it->second <<endl;
it++;
}
}
}
Simply changing the key in the above program to integer gives me the expected result. But, indexing the multimap on a string is giving me something unexpected (only rows of 1's and 4's separated by space), instead of showing me every value for every key value used.
Where am I going wrong in this?
Thanks
strcmp is a wrong predicate to use in multimap.
The predicate shall satisfy the following:
The expression comp(a,b), where comp is an object of this comparison class and a and b are key values, shall return true if a is to be placed at an earlier position than b in a strict weak ordering operation.
strcmp violates that because it returns a nonzero value if the strings are unequal, either a < b or a > b.
You should define your own predicate which returns true if and only if the first string is less than the second.
multimap<char *, char *, int (*)(const char *, const char *)>a(fn_pt);
for (i = 0; i < 2; i++)
{
char key[2];
sprintf(key, "%d", i);
for (j = 0; j< 5; j++)
{
char value[2];
sprintf(value, "%d", j);
a.insert(pair<char *, char *>(key, value));
}
}
You're storing two pointers in a container, and then you're destroying the objects (key and value) those pointers point to when they go out of scope. This leaves the container holding information that is now meaningless.
You're using the memory of key and value long after they go out of scope. In fact, all your char* pointers point at the same piece of stack memory, and that piece is ready to be reused by the time you actually look at it.
To do what you want you need to use strdup() to create a permanent copy of your char * data. Of course, then you need to worry about deallocating it later.
Related
I`m trying to found fastest way to generate random digit/char array.
char *randomGet(int num) {
srand(time(NULL));
const char ab[37] = { "0123456789ABCDEFGHIGKLMNOPQRSTUVWXYZ" };//Alphabet&Digit
char *targ = new char[num];
for (int i = 0; i < num; i++) {
strcat(targ, ab[rand() % 38]);
}
return targ;
}
So far I've come up with this, but it does not work (argument of type char is incompatible with parameter of type const char *).
Help me find the best solution to my problem. Ty.
strcat() takes a char* as input, but you are giving it a single char instead, thus the compiler error.
Also, the buffer that strcat() writes to must be null terminated, but your targ buffer is not null terminated initially, and you are not allocating enough space for a final null terminator anyway.
You don't need to use strcat() at all. Since you are looping anyway, just use the loop counter as the index where to write in the buffer:
Also, you are using the wrong integer value when modulo the return value of rand(). You are producing a random index that may go out of bounds of your ab[] array.
Try this instead:
char *randomGet(int num)
{
srand(time(NULL));
static const char ab[] = "0123456789ABCDEFGHIGKLMNOPQRSTUVWXYZ"; //Alphabet&Digit
char *targ = new char[num+1];
for (int i = 0; i < num; ++i) {
targ[i] = ab[rand() % 36];
}
targ[num] = '\0';
return targ;
}
I'd make two changes. First, make the internal source array static:
static const char ab[] = "0123456789ABCDEFGHIGKLMNOPQRSTUVWXYZ";
Note that this version does not specify the array size; the compiler will figure it out from the initializer.
Second, pass in a pointer to the target array:
void randomGet(char* targ, int num) {
static const char ab[] = "0123456789ABCDEFGHIGKLMNOPQRSTUVWXYZ";
for (int i = 0; i < num - 1; ++i)
targ[i] = ab[rand() % (sizeof ab - 1)];
targ[num - 1] = '\0';
}
This way, the caller decides how to allocate memory for the string.
I'm eradicating std::string in favor of C-strings, which I'm new to. How do I get the following to compile? g++ complains: cannot convert char(*)[16] to char**
#include <iostream>
void print(char** s, int n)
{
for (int i = 0; i < n; ++i)
{
std::cout << s[i] << '\n';
}
}
int main()
{
constexpr int n = 3;
char s[n][16]{ "Hello", "Bye", "Sky"};
print(s, n);
}
You created a multidimensional array, not an array of pointers. Usually an array can be said to be equivalent to a pointer, however in this case c++ needs to know the size of the second dimension of your array. The function would be as follows
void print(char s[][16], int n)`{
for (int i = 0; i < n; ++i)
{
std::cout << s[i] << std::endl;
}
}
Understandably you may want to pass the function using pointers as to not make an entire copy of the 2-d array. I saw you mentioned you were okay with variable length strings. That functionality is supported in the string library. You are dealing with c-strings which are not strings at all but static arrays of type character. Defining these c-strings using dynamic memory happens to give you the desired behavior as you create in the simplest terms an array of pointers.
void print(char** s, int n)
{
for (int i = 0; i < n; ++i)
{
std::cout << s[i] << std::endl;
}
}
int main()
{
int n = 3, i;
char** s = new char*[n];
for (i = 0; i < 3; i++) {
s[i] = new char[16];
}
s[0] = "Hello";
s[1] = "Bye";
s[2] = "Sky";
print(s, n);
for (i = 0; i < 3; i++) {
delete [] s[i];
}
delete [] s;
s = NULL;
return 0;
}
Since you are using dynamic memory now you need to free your memory which is what the last loop serves to do. As you can see using all this dynamic memory is quite taxing and it would be easier to use the string library that has been optimized to do a much better job then you can. If you're still not convinced you should at least make your own string class to handle the dynamic memory that contains a char * as its private member. In either case you avoid this mess and just make an array of zed class objects and not deal at all with multidimensional nonsense. No one likes seg faults and memory leaks.
Given any type T, T arr[N]; declares a variable arr of type T[N], which is an array and not a pointer. When you use arr in almost all contexts, array to pointer conversions happen, giving the incorrect illusion that arr is a pointer of type T*.
char s[n][16] = { "Hello", "Bye", "Sky" };
declares s as an array of n elements of type char[16]. Now, when array to pointer conversion happens, s decays into a pointer of type char (*)[16]. Hence, your function needs to have the signature
void print(char (*s)[16], int n);
Which is equivalent to
void print(char s[][16], int n);
the [] is interpreted as a pointer by the compiler.
To make these complex types more readable, a type alias may be used.
using T = char[16];
void print(T s[], int n);
Addressing some concerns
As pointed out in the comments, std::string should almost always be preferred over a char array. If you have performance concerns, benchmark before doing this. I really doubt much performance gains can be observed in most cases.
Declaring an array with length n which is an int is not standard C++. It is an extension provided by your compiler, it is not portable and in most cases not necessary.
int n = 3;
char vla[n]; // this is a variable length array
char arr[3]; // this is just an array
char* darr = new char[3]; // this is a pointer pointing to dynamically allocated memory
std::string str; // but instead, this is just better
The compiler cannot extract from char ** the infomation about char[16]. You need to define a type char[16] and pass the pointer to this type to your print function.
#include <iostream>
typedef char str_t[16];
void print(str_t* s, int n)
{
for (int i = 0; i < n; ++i)
{
std::cout << s[i] << std::endl;
}
}
int main()
{
int n = 3;
char s[n][16]{ "Hello", "Bye", "Sky"};
print(s, 3);
}
I have a simple table called mytable2 with only one column, name as varchar2(20).
I now have a list of names stored as vector of std::string to be inserted into the table.
I want to use executeArrayUpdate, so I must do the setDataBuffer first.
However, as I could see, people always use char[][20] to set databuffer.
This leaves me a big headache, since I have two issues here, first is to convert from vector to array, second is to convert the string to char.
1st, I tired to use vector of char[20], and this doesn't compile. Googled and they say that vector can't take char[], so I changed my vector of std::string to vector of char*.
2nd, I tried to turn the vector to arrray by using "void* p=&names[0]", as some people say this way we can use vectors just as array.
I used stmt->setDataBuffer(1,mystring,OCCI_SQLT_STR,20,NULL), and the program compiled and executed alright, but when I "select name from mytable2", it showed only some strange charaters.
Anyone has had a similiar issue before? what should I do?
My code is simple as below:
count=2;
vector<char*> mystring;
for(int i=0;i<count;i++)
{
char my[20];
strcpy_s(my,"Michael");
mystring.insert(mystring.end(),my);
}
stmt->setDataBuffer(1,&mystring[0],OCCI_SQLT_STR,20,NULL);
stmt->setBatchErrorMode (true);
stmt->executeArrayUpdate(count);
You'd need to dynamically create the char array you're putting into the vector for it to have a chance of working correctly.
I have not used OCCI, but if I had to use API that asked for char[][20], I would give it char[][20]
If you have your existing data in vector, why not just copy it across into the 2D char array? Eg.
// intialise vector with nonsense
unsigned int const VEC_SIZE = 2 ;
vector<string> v;
for (unsigned int i = 0; i < VEC_SIZE; ++i) {
stringstream s;
s << "Jon Paul " << i << endl;
v.push_back ( s.str() ) ;
}
// create the required 2D char array
unsigned int const STR_LEN = 20 ;
char c [VEC_SIZE][STR_LEN];
// copy the data from the vector of strings to the 2D char array
for (unsigned int i = 0; i < VEC_SIZE; ++i) {
string s = v[i].substr(0,STR_LEN);
char const * const cc = s.c_str();
unsigned int j = 0;
for (; j < STR_LEN && j < s.size(); ++j) {
c[i][j] = cc[j];
}
c[i][ j==STR_LEN ? 20 : j ] = 0; // write NULL character
}
I take it you've simplified your example to be a fixed size vector, so my response is going to be simplified to, with the thorny issue of dynamic allocation of 2D arrays left as an exercise for the reader...
I am trying to sort a buffer full of variable-length records alphabetically in C++. I previously asked how to implement this, and was told to sort an array of pointers to the records. I set up an array of pointers, but realized that each pointer points to the beginning of a record, but there is no way of it knowing when the record stops. When I try to print out the record pointed to by each pointer in the array, therefore, for each pointer, I get the entire buffer of all records, starting from the one pointed to. (For example, if the buffer holds "Helloworld", and there is a pointer at each letter, printing the array of pointers would produce "Helloworldelloworldlloworldloworldoworldworldorldrldldd".) Obviously, this is not what I want; also, the qsort does not seem to be working on the array of pointers either. When I debug, the memory spaces pointed to by the pointers seem to hold very odd characters that are definitely not part of the ascii character set and were not included in my input file. I am very confused. Below is my code; how can I do this without getting the odd results I get now? Thank you so much, bsg.
int _tmain(int argc, _TCHAR* argv[])
{
//allocate memory for the buffer
buff = (unsigned char *) malloc(2048);
realbuff = (unsigned char *) malloc(NUM_RECORDS * RECORD_SIZE);
fp = fopen("postings0.txt", "r");
if(fp)
{
fread(buff, 1, 2048, fp);
/*for(int i=0; i <30; i++)
cout << buff[i] <<endl;*/
int y=0;
//create a pointer to an array of unsigned char pointers
unsigned char *pointerarray[NUM_RECORDS];
//point the first pointer in the pointer array to the first record in the buffer
pointerarray[0] = &buff[0];
int recordcounter = 1;
//iterate through each character in the buffer;
//if the character is a line feed (denoting a new record),
// point the next pointer in the pointer array to the next
//character in the buffer (that is, the start of the next record)
for(int i=0;i <2048; i++)
{
if(buff[i] == char(10))
{
pointerarray[recordcounter] = &buff[i+1];
recordcounter++;
}
}
//the actual qsort (NUM_RECORDS is a constant declared above; omitted here)
qsort(pointerarray, NUM_RECORDS, sizeof(char*), comparator);
}
else
cout << "sorry";
cout << sizeof(pointerarray)/sizeof(char*);
for(int k=0; k < sizeof(pointerarray)/sizeof(char*);k++)
{
cout << pointerarray[k];
}
int comparator(const void * elem1, const void * elem2)
{
//iterate through the length of the first string
while(*firstString != char(10))
{
return(strcmp(firstString, secondString));
firstString++;
secondString++;
/
}
return 0;
}
I'm guessing the problem is in your comparator function (which doesn't compile as posted).
qsort gives a pointer to the array element to the comparator function. In your case that would be a pointer to the char* stored in the array.
The man page for qsort gives this example:
static int
cmpstringp(const void *p1, const void *p2)
{
/* The actual arguments to this function are "pointers to
pointers to char", but strcmp(3) arguments are "pointers
to char", hence the following cast plus dereference */
return strcmp(* (char * const *) p1, * (char * const *) p2);
}
int
main(int argc, char *argv[])
{
int j;
assert(argc > 1);
qsort(&argv[1], argc - 1, sizeof(char *), cmpstringp);
for (j = 1; j < argc; j++)
puts(argv[j]);
exit(EXIT_SUCCESS);
}
This question basically comes down to 'how do you know the length of your variable-length record.' There needs to be some way to tell, either from the record itself, or from some other data.
One way is to use pointer/length pairs to refer to records -- a pointer to the beginning of the record and a length (int or size_t), which you store together in a struct. With C++ you can use std::pair, or with C define a litte struct. You can then use qsort on an array of these.
In your case, you can tell the length by looking for a char(10), as you always use them to terminate your strings. You need a custom comparison (strcmp won't work -- it expects NUL terminators) that is aware of this.
Can you tell me what's wrong with my method? I ends up putting the same thing everywhre and it's actually not sorting.
void sortArrays(){
int i, j;
for(i=0; i<counter; i++){
for( j=0; j<i; j++){
if( strcmp(title_arr[i], title_arr[j]) < 0){
char* title_temp = title_arr[i];
title_arr[j] = title_temp;
}
}
}
This:
char* title_temp = title_arr[i];
title_arr[j] = title_temp;
Is equivalent to:
title_arr[j] = title_arr[i];
You never swap them, you just copy one to the other. You should add this line:
title_arr[i] = title_arr[j];
In between the two. That way, you'll overwrite [i] with [j], but _temp still holds the old value of [i], so you can copy that value into [j], thus swapping them.
I suppose it's also a time for a lesson on algorithms. Your algorithm is known as a "bubble sort" algorithm. It is known for it's simplicity, but in a realistic setting it is known for it's inefficiency (the technical term is "teh sux", and the real technical term is O(n^2) ("N squared") performance). Some more common (and more efficient) algorithms include Quicksort, merge sort, and Heapsort, among many others. For more about measuring algorithmic scalability, see the article on Big Oh notation.*
But, as vava pointed out in a comment, unless your assignment is to write your own sorting function, you're going to get better performance with qsort (in C) or std::sort (in C++).
int mystrsort(const void *a, const void *b)
{
return strcmp(*(const char **)a, *(const char **)b);
}
// later:
qsort(title_arr, sizeof title_arr / sizeof(char *), sizeof(char *), mystrsort);
I'm not going to stab at std::sort, but it's going to work about the same (perhaps easier).**
*Note that anyone who likes is free to change these Wikipedia links to Stack Overflow links. It would be better to link to SO, I just linked to Wikipedia because I knew how to find the info I needed faster.
**Note that anyone who likes is free to add a std::sort example. I'm just not sufficiently familiar with C++.
You didn't swap properly, that's why it didn't work.
#include <iostream>
#include <algorithm>
int const counter = 4;
char * title_arr[counter] = {
"d", "c", "b", "a"
};
void sortArrays(){
for(int i = 0; i < counter; i++){
for(int j = 0; j < i; j++){
if(strcmp(title_arr[i], title_arr[j]) < 0){
char* title_temp = title_arr[i];
title_arr[i] = title_arr[j];
title_arr[j] = title_temp;
//you wouldn't have made that stupid mistake this way.
//std::swap(title_arr[i], title_arr[j]);
}
}
}
}
int compare(void const * a, void const * b) {
return strcmp(static_cast<char const *>(a), static_cast<char const *>(b));
}
struct StringLess : public std::binary_function<char const *, char const *, bool> {
bool operator() (char const * a, char const * b) const {
return strcmp(a, b) < 0;
}
};
int main(int argc, char * argv[])
{
sortArrays();
//those ones better
// qsort(title_arr, counter, sizeof(char *), compare);
// std::sort(title_arr, title_arr + counter, StringLess());
for (int i = 0; i < counter; i++) {
std::cout << title_arr[i] << ", ";
}
return 0;
}
Bad coding style:
1. Don't use global variables. It's better to pass your array and length as arguments into sort function. Why? Your function is not reusable. What if you will need to sort another array? Yes, you will need to write another sort function...
2. More advanced tip: use emulation of higher-order function. What if you will need to sort not only characters? Integer, floats, strings or your own types. In this case you can also pass compare() function into your sort function which can compare objects of your array.