How to create a jagged string array in c++? - c++

I want to create jagged character two dimensional array in c++.
int arrsize[3] = {10, 5, 2};
char** record;
record = (char**)malloc(3);
cout << endl << sizeof(record) << endl;
for (int i = 0; i < 3; i++)
{
record[i] = (char *)malloc(arrsize[i] * sizeof(char *));
cout << endl << sizeof(record[i]) << endl;
}
I want to set record[0] for name (should have 10 letter), record[1] for marks (should have 5 digit mark )and record[3] for Id (should have 2 digit number). How can i implement this? I directly write the record array to the binary file. I don't want to use struct and class.

in C++ it would like this:
std::vector<std::string> record;

Why would you not use a struct when it is the sensible solution to your problem?
struct record {
char name[10];
char mark[5];
char id[2];
};
Then writing to a binary file becomes trivial:
record r = get_a_record();
write( fd, &r, sizeof r );
Notes:
You might want to allocate a bit of extra space for NUL terminators, but this depends on the format that you want to use in the file.
If you are writing to a binary file, why do you want to write mark and id as strings? Why not store an int (4 bytes, greater range of values) and a unsigned char (1 byte)
If you insist on not using a user defined type (really, you should), then you can just create a single block of memory and use pointer arithmetic, but beware that the binary generated by the compiler will be the same, the only difference is that your code will be less maintainable:
char record[ 10+5+2 ];
// copy name to record
// copy mark to record+10
// copy id to record+15
write( fd, record, sizeof record);

Actually the right pattern “to malloc” is:
T * p = (T *) malloc(count * sizeof(T));
where T could be any type, including char *. So the right code for allocating memory in this case is like that:
int arrsize[3] = { 10, 5, 2 };
char** record;
record = (char**) malloc(3 * sizeof(char *));
cout << sizeof(record) << endl;
for (int i = 0; i < 3; ++i) {
record[i] = (char *) malloc(arrsize[i] * sizeof(char));
}
I deleted cout'ing sizeof(record[i]) because it will always yield size of (one) pointer to char (4 on my laptop). sizeof is something that plays in compiling time and has no idea how much memory pointed by record[i] (which is really a pointer - char * type) was allocated in the execution time.

malloc(3) allocates 3 bytes. Your jagged array would be an array containing pointers to character arrays. Each pointer usually takes 4 bytes (on a 32-bit machine), but more correctly sizeof(char*), so you should allocate using malloc(3 * sizeof(char*) ).
And then record[i] = (char*)malloc((arrsize[i]+1) * sizeof(char)), because a string is a char* and a character is a char, and because each C-style string is conventionally terminated with a '\0' character to indicate its length. You could do without it, but it would be harder to use for instance:
strcpy(record[0], name);
sprintf(record[1], "%0.2f", mark);
sprintf(record[2], "%d", id);
to fill in your record, because sprintf puts in a \0 at the end. I assumed mark was a floating-point number and id was an integer.
As regards writing all this to a file, if the file is binary why put everything in as strings in the first place?
Assuming you do, you could use something like:
ofstream f("myfile",ios_base::out|ios_base::binary);
for (int i=0; i<3; i++)
f.write(record[i], arrsize[i]);
f.close();
That being said, I second Anders' idea. If you use STL vectors and strings, you won't have to deal with ugly memory allocations, and your code will probably look cleaner as well.

Related

Arduino scrolling text program freeze after some time

I use a 16x2 character LCD to display some text. What I want is first line is fixed and second line is scrolling.
I wrote a program which works fine but the problem is after some time Arduino does not respond. I suspect there might be a bug or memory leak in the code.
The relevant code is like this.
void scrollTextFromRight(int line, char text[])
{
const char space[16] = " ";
char screen[16];
char * longText;
longText = malloc(sizeof(char) * (sizeof(text) + 17));
memset(longText, '\0', sizeof(char) * (sizeof(text) + 17));
memset(screen, '\0', sizeof(screen));
for (int i = 0; i < 16; ++i)
{
longText[i] = space[i];
}
for (int j = 0; j < sizeof(text) + 17; ++j)
{
longText[16+j] = text[j];
}
for (int i = 0; i < sizeof(text) + 17; ++i)
{
lcd.setCursor(0, line);
strncpy(screen, longText + i, 17 );
lcd.print(screen);
delay(350);
}
}
I call this function from main program like this:
scrollTextFromRight(1, "Scrolling text");
Update 1 :
After reading the comments and answers I freed allocated memory space with free function. I uploaded the new code and testing whether it works as expected.
I added this part after the third for loop.
free longText;
Update 2 :
After reading the comments I decided to use Arduino's String class. The code became like this:
void scrollTextFromRight(int line, String text)
{
const String space = " ";
const String longText = space + text + ' ';
int displaySize = 16;
for (int i = 0; i <= longText.length(); ++i)
{
lcd.setCursor(0, line);
String display = longText.substring(i, i + displaySize);
lcd.print(display);
delay(350);
}
}
When you declare the argument as char text[], the compiler translates it as char* text. That is, it's a pointer.
And getting the size of a pointer (e.g. sizeof(text)) gives you the size of the pointer and not whatever it points to. If it's a null-terminated byte string, then use strlen to get the length (but note that the null-terminator not counted).
Or even better, stop using C strings and functions, because Arduino is actually programmed in C++ and have its own standard String class that should be used for all strings.
Also note that
const char space[16] = " ";
creates an array of 16 elements, and set all those elements to the space character ' '. But it's not a null-terminated string, because the terminator doesn't fit in the array.
You also know about the memset function, but seems to have forgotten the memcpy function when you copy from your arrays.
Instead of the explicit loop copying from space, you could simply do
memcpy(longText, space, sizeof space); // Using sizeof since space is not null-terminated
Lastly, be careful with the strncpy function, it might not null-terminate the destination string.

Output a list of integer values at the same time by using a pointer dynamic array?

In C++, following the code below:
char *p = new char();
*p = 'a';
*(p+1)= 'b';
*(p+2) ='\0';
cout<<p<<endl;
we can get the output result: ab
When I want to write the code like this:
int *p = new int();
*p = 1;
*(p+1)= 2;
cout<<p<<endl;
It gives the result, but not 12 or something start with 12
Question is why the result goes wrong when changing it from char to integer? How to realize the goal that output a list of value by using a pointer dynamic array?
This is because char * is a bit of a special case.
In the C days, char * was the only type we really had to deal with strings. C++ makes it easier to interoperate with legacy code by providing streaming operators that treat char * values specially, by streaming out each character until a null character ('\0') is encountered.
When you stream out p when it's an int * there is no special case -- you just get the raw pointer value displayed.
If you want a one-liner to display the elements in a standard container, you can combine std::copy() with std::ostream_iterator:
std::copy(std::begin(some_array),
std::end(some_array),
std::ostream_iterator<int>(std::cout));
Since your array is allocated on the heap and stored in a pointer, std::begin() and std::end() won't work for you; you'll have to provide the end iterator manually:
std::copy(p, p + 2, std::ostream_iterator<int>(std::cout));
(See a demo.)
But note that both code samples in your question are undefined behavior, because you allocate a single object of type char or int and then try to assign beyond it. You write into memory you haven't allocated. Don't do this.
To fix your cases, you need to allocate enough room for the objects you intend to store:
// Case one
char *p = new char[3];
// Case two
int *p = new int[2];
And, of course, don't forget to delete[] p in both cases -- or you could just use std::string in the first case and std::vector<int> in the second.
Ok, first of all, this is unsafe:
*(p+1)= 'b';
You only allocated one byte and now you're stomping on memory you don't own - which is undefined behavior. If you want to allocate 3 bytes, do it:
char* p = new char[3];
Secondly, this:
int* pi = new int;
cout << pi << endl;
Will print the address of the pointer. char* is special in that cout will actually print the C-style string that is pointed to, but for other types - you just get the address.
If you want to print 12, you have to dereference the pointer:
int* pi = new int(12);
cout << *pi << endl;
If you want to output 1 and 2, separately, you need an array:
int* pi = new int[2];
pi[0] = 1;
pi[1] = 2;
cout << pi[0] << ", " << pi[1] << endl;
Note that, again, *(p + 1) = 2 in your code is stomping on memory you don't own.

Reading data from a file; first packet is gibberish

I am trying to read 1244 bytes at a time from a file. Essentially, the idea is to segment the 100KB worth of data into packets. So the approach I am taking is, assigning all the data to an array and then creating an array of pointers which will contain starting positions to each of my packets. The pointer array contains values [0, 1244, 2488, and so on].
It works perfectly fine, except my first assignment is gibberish. k[0] and o[0] both come up with garbage while the remaining 79 values seem to be fine. Can anyone assist?
I realize the first argument to the fread command should be a pointer, but this worked also. Also, I need the pointers to the starting of each of my packets because I am doing other function calls (omitted from code) that format the packet properly with the appropriate headers.
It's been a while since I coded in c/c++ so any optimizations you could provide, would be much appreciated.
int main(int argc, const char * argv[])
{
FILE *data;
int size; int i;
int paySize = 1244;
//int hdrSize = 256;
data = fopen("text2.dat","r");
//get data size
fseek(data, 0, SEEK_END);
size = ftell(data);
rewind (data);
char k[size]; //initializing memory location for all the data to be read in.
fread(k, 1, size, data); //reading in data
int temp = ceil(size/paySize);
char * o[temp]; //array of pointers to beginning of each packet.
int q = 0;
for (i = 0; i < size; i = i+paySize)
{
o[q] = &k[i];
q++;
}
cout << o[0] << endl; //this outputs gibberish!
cout << o[0] << endl;
prints an address to which this pointer points. To print value at this address use:
cout << *o[0] << endl;
Here:
char k[size];
char * o[temp];
o[q] = &k[i];
you assign to o[] pointers to characters, dereferencing such a pointer result in a single char.

What's going on with my memory?

I have a function that allocated a buffer for the size of a file with
char *buffer = new char[size_of_file];
The i loop over the buffer and copy some of the pointers into a subbuffer to work with smaller units of it.
char *subbuffer = new char[size+1];
for (int i =0; i < size; i++) {
subbuffer[i] = (buffer + cursor)[i];
}
Next I call a function and pass it this subbuffer, and arbitrary cursor for a location in the subbuffer, and the size of text to be abstracted.
wchar_t* FileReader::getStringForSizeAndCursor(int32_t size, int cursor, char *buffer) {
int wlen = size/2;
#if MARKUP_SIZEOFWCHAR == 4 // sizeof(wchar_t) == 4
uint32_t *dest = new uint32_t[wlen+1];
#else
uint16_t *dest = new uint16_t[wlen+1];
#endif
char *bcpy = new char[size];
memcpy(bcpy, (buffer + cursor), size+2);
unsigned char *ptr = (unsigned char *)bcpy; //need to be careful not to read outside the buffer
for(int i=0; i<wlen; i++) {
dest[i] = (ptr[0] << 8) + ptr[1];
ptr += 2;
}
//cout << "size:: " << size << " wlen:: " << wlen << " c:: " << c << "\n";
dest[wlen] = ('\0' << 8) + '\0';
return (wchar_t *)dest;
}
I store this in a value as the property of a struct whilst looping through the file.
My issue seems to be when I free subbuffer, and start reading the title properties of my structs by looping over an array of struct pointers, my app segfaults. GDB tells me it finished normally though, but a bunch of records that I cout are missing.
I suspect this has to do with function scope of something. I thought the memcpy in getStringForSizeAndCursor would fix the segfault since it's copying bytes outside of subbuffer before I free. Right now I would expect those to then be cleaned up by my struct deconstructor, but either things are deconstructing before I expect or some memory is still pointing to the original subbuffer, if I let subbuffer leak I get back the data I expected, but this is not a solution.
The only definite error I can see in your question's code is the too small allocation of bcpy, where you allocate a buffer of size size and promptly copy size+2 bytes to the buffer. Since you're not using the extra 2 bytes in the code, just drop the +2 in the copy.
Besides that, I can only see one suspicious thing, you're doing;
char *subbuffer = new char[size+1];
and copying size bytes to the buffer. The allocation hints that you're allocating extra memory for a zero termination, but either it shouldn't be there at all (no +1) or you should allocate 2 bytes (since your function hints to a double byte character set. Either way, I can't see you zero terminating it, so use of it as a zero terminated string will probably break.
#Grizzly in the comments has a point too, allocating and handling memory for strings and wstrings is probably something you could "offload" to the STL with good results.

C++ qsort array of pointers not sorting

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.