c++ initializing char array member of class - c++

in my c++ project I have class with two members. the char array member I have problems with.
class frame_message
{
public:
explicit frame_message(const unsigned int id, const char data[]) :id_(id), data_{ *data }{};
// only the first char 'a' is copied to `data_`
char* get_data() { return data_; };
void get_data(char** data) { *data = data_; };
private:
unsigned int id_; char data_[8];
};
now from main method I want to send another char array used to initialize the class array.
main
{
char data[8]={'a','b','c'} // indexs 3 to 7 are '\0'
char data2[8];
char data3[8];
frame_message myMessage(0xF004,data); // the data is passed as "abc"
data2 = myMessage.get_data(); // analysis error
myMessage.get_data(&data3); // runtime exception
}
How should I initialize the private member of class with exactly the data array send to constructor?
also for for get_data functions what data type should be passed?
p.s. I am new in c/c++ and yet get confused in pointers, references and specially char and char*

For the constructor, it would be a good idea to pass a length parameter as well because you can accept only up to 8 bytes.
Then, if your length is <= 8 :
memcpy(data_, data, length)
Same thing in your parameterized get_data, so it would be:
memcpy(*data, data_, 8) /* Assuming that they provide long enough array. */
It is good practice when dealing with arrays to always include length, and when dealing with pointers to check if they are NULL - I'll leave this to you.
The reason you were getting errors is because you cannot assign a pointer to a statically declared array - it has a fixed address, you can only change the content.

Related

Initializing a Struct's variable const char* const* with C++

I am trying to solve a coding question that requires the results be returned using a given struct. The struct is defined as:
struct Answer
{
const char* const* lastNames;
unsigned numberOfPeople;
}
Where the lastNames is a pointer to last names that are each terminated by a non-alpha char. I can not seem to find any way to convert the vector of strings that I am using to compile all the last names into a variable that I can assign to lastNames. I have tried making a single string with all the last names and assigning it with c_str() like so:
Ans->lastName = allNames.c_str(); but this gives me an error. Due to the limitations of the question I am unable to change the struct variable to anything else. How can I assign a string to a const char* const*
The structure being used effectively uses a C-style approach to defining a variable sized array of pointers to char (with const sprinkled over it). You’ll need storage for both the array of char const* as well as the entities pointed to. Here is how you could build it from a std::vector<std::string>:
std::vector<std::string> strings = somehow_compute_the_strings();
std::vector<char const*> array;
for (std::string const& s: strings) {
array.push_back(s.c_str());
}
Answer answer = { array.data(), array.size() };
Of course, you can’t return answer without the pointer inside pointing to stale data: you’d need to keep the two std::vectors alive. Potentially these two objects could be made members of an object the function is called on. To actually return an object of type Answer without a place to hold on to the std::vectors you could allocate the relevant entities and accept that the result will yield a memory leak unless the caller can clean the result up.
You can't just cast stuff. struct Answer is expecting a char**, so you are going to have to build it and keep it valid as long as the struct Answer is in use. At least they were kind enough to let us know they don't intend to modify it or mess with cleaning up the memory, since it takes "const char * const *".
#include <iostream>
#include <vector>
#include <string>
#include <assert.h>
typedef std::vector<std::string> VectorOfStrings_type;
struct Answer
{
const char* const* lastNames;
unsigned numberOfPeople;
};
class AnswerWrapper
{
private:
// construct and maintain memory so the pointers in the Answer struct will be valid
char ** lastNames;
unsigned int numberOfPeople;
public:
AnswerWrapper(const VectorOfStrings_type &input){
numberOfPeople = input.size();
// create the array of pointers
lastNames = static_cast<char**>(
malloc(numberOfPeople * sizeof(char*))
);
// create each string
for (unsigned int i = 0; i < numberOfPeople; ++i){
const std::string &name = input[i];
// allocate space
lastNames[i] = static_cast<char*>(
malloc(name.size() + 1)
);
// copy string
strncpy(lastNames[i], name.data(), name.size());
// add null terminator
lastNames[i][name.size()] = '\0';
}
}
operator Answer (){
return Answer{ lastNames, numberOfPeople };
}
~AnswerWrapper(){
// critcally important, left as an exercise
assert(0);
}
};
void SomeFunctionWhichUsesAnswer(Answer a){
// presumably you have some legacy C code here
// but here's a quick and easy demo
for (unsigned int i = 0; i < a.numberOfPeople; ++i)
std::cout << a.lastNames[i] << std::endl;
}
int main() {
// Here is your vector of strings
VectorOfStrings_type myData { "custom formatted data goes here", "and more here", "and again" };
// You must construct a buffer for the "Answer" type, which must remain in scope
AnswerWrapper temp{ myData };
// AnswerWrapper is currently in scope, so inside this function, the pointers will be valid
SomeFunctionWhichUsesAnswer(temp);
}
Also, I noticed that the strings in Answer are not referred to as null terminated. That is a separate issue you can take care of.
A const member variable can only be assigned in the constructor.
if you can add to the struct, define a constructor, and use the : lastname(value) syntax; or use the struct Answer myVar{value,number}; initialization, right where you declare your instance.
Another - ugly, dangerous, and frowned upon - alternative is a cast: (char**) lastname = value;, or in C++ syntax reinterpret_cast<char**>(lastname) = value.
If someone is teaching you either of those approaches, change the teacher.

Variable length array inside a c++ sturcture

I wondering what the best solution is for a structure with variable length array for one of the fields. I've done a bunch of research and I haven't seen a clear answer yet.
I've been playing with the below code and trying to get the varField to be set to an array the size of 10 bytes.
typedef struct TestStruct{
int size;
unsigned char varField[1];
}
I have tried doing zero sized array and that gives me a compile error.
I also tried something like this and it gave me a compile error.
int size= 10;
struct TestStruct*test = malloc(sizeof(struct TestStruct) + (size- 1));
test->size= size;
Thank you so much for help.
The preferred way is to use the dynamically re-sizable std::vector. This class has the rule of five built in.
struct TestStruct {
std::vector<unsigned char> varField;
}
If you're allergic to the standard library, you could use:
unsigned char *varfield;
And supply the necessary constructors/destructors.
If you are implementing messages, a better solution is to set up a hierarchy:
struct Message_Base
{
unsigned int message_length_in_bytes;
unsigned int message_id;
virtual Checksum_Type calculate_checksum(void) = 0;
virtual bool send_message(Receiver& r) = 0;
virtual bool receive_message(Sender& s) = 0;
virtual void process_message(void) = 0;
};
Each child class would be a different message with possible different lengths. Some possible common methods to all message are listed.
This is how to implement using Object Oriented and C++.
The class C language implementation is to declare a zero length array at the end for the message's unique data.
You look like you want a std::vector<unsigned char>:
struct TestStruct{
std::vector<unsigned char> varField;
}
and you get the size with:
ts.varField.size();
You can't. In C++ dynamic size arrays are illegal. The size of an array must be a compile time constant expression.
The options you have basically are
Use an STL container like std::vector or the like. The benefits are that they also take care of memory allocation and deallocation for you.
Use a pointer in your struct and allocate the memory for it dynamically. Don't forget to use delete[] instead of just delete!
In most compilers, the following will work:
template<unsigned N>
struct TestStruct {
unsigned size = N;
unsigned char varField[N];
};
struct ITestStruct {
unsigned size;
unsigned char varField[1]; // variable
};
template<unsigned N>
ITestStruct* make_test_struct() {
return reinterpret_cast<ITestStruct*>(new TestStruct<N>());
};
ITestStruct* make_test_struct( unsigned n ) {
char* buff = new char[ sizeof(ITestStruct)+n-1 ];
ITestStruct* retval = reinterpret_cast<ITestStruct*>(buff);
retval->size = n;
return retval;
}
If you replace char with another non-POD type, things will get hairy.

how to pass a static char array to another class safely

I was wondering if someone could help me understand how to securely pass a static unsigned char array from one class to another. My apologies if this is trivial but I wanted to be sure I do it right.
for example
// First Class
unsigned char data[1024];
TMainClass::TMainClass()
{
data[34] = 1;
data[45] = 65;
...
TOtherClass obj(data);
};
// Second class
static unsigned char obtainedData[1024];
TOtherClass::TOtherClass(unsigned char* _data)
{
obtainedData = _data;
};
You really should be using an stl or boost container. If, however, you have a good reason not to, then ...
You're passing it just fine, but the assignment is wrong.
obtainedData = _data;
You should really either copy the data
memcpy(obtainedData, _data, 1024);
std::copy(_data, _data+1024, obtainedData) // Thanks #Grizzly
or redefine the obtainedData variable
static unsigned char* obtainedData;
Notes:
Be sure you think about the size of the arrays you're passing and copying to. Are they guaranteed to be the same size?
What do you mean by "securely" or "safely"?
Your types are wrong ( you don't assign 1 array to another like that).
And this is c++ so really what you should probably be doing is using a std::string or std::vector or even if you need a fixed buffer std::array

Pointer to array of character arrays

Okay, this one has me stumped. I am trying to pass an array of character arrays into my class's constructor. The class has a private attribute which stores a pointer to the array of character arrays. The class may then process the array via the pointer.
Below is some code that demonstrates the desired functionality. But, it won't compile. How do I fix this code so it works?
using namespace std;
const int MAX_LINES = 10, MAX_STRING = 80;
class Alphabetizer{
public:
Alphabetizer(char * inArray[][MAX_STRING]) : input(inArray){};
private:
char * input[MAX_LINES][MAX_STRING];
};
int main(){
char charArray[MAX_LINES][MAX_STRING];
Alphabetizer theAlaphBet(charArray);
return 0;
}
If you're insisting on using C-compatible character pointers, I think you'll have the best luck using a char ** as the type for input. This is more of the usual way to do this (in C at least), and it has the added benefit of not forcing you to define a maximum string size.
As others have pointed out, you can take advantage of std::string instead, which may be a better choice overall.
I'm guessing it's that you're not passing a pointer to char[][], you're passing a char[][].
Also, you should be using std::string instead of char arrays.
std::string will be the most appropriate here! It handles strings and character arrays well enough!
There are few errors in the code. I suppose you are trying to refer to the charArray in the main function from inside the Alphabetizer object. If that is the case the declaration
char * input[MAX_LINES][MAX_STRING];
is wrong because the above declaration makes input an array of MAX_LINE of ( array of MAX_STRING of (char*)). In summary input is an array not a pointer to array of whatever. If you had intended it to be a pointer - which is what rest of your code hints to me - then you have to do the following,
const int MAX_LINES = 10, MAX_STRING = 80;
class Alphabetizer{
public:
Alphabetizer(char ((*ar)[MAX_LINES])[MAX_STRING]) : m_ar(ar){};
private:
char ((*m_ar)[10])[80];
};
int main(){
char charArray[MAX_LINES][MAX_STRING];
char ((*ar)[MAX_LINES])[MAX_STRING] = &charArray;
Alphabetizer theAlaphBet(&charArray);
return 0;
}
Moreover doing,
input(inArray)
is wrong, as it is equivalent to doing the following,
char a[1] = {'a'};
char b[1] = {'p'};
a = b;
assigning an array to another does not copy one over another. You have to do explicit memcpy. (This semantics is not meaningful in c or c++)
It's difficult to tell without seeing the compile errors, but I think the problem might be this line:
Alphabetizer theAlaphBet(charArray);
You are passing the array directly rather than it's address. It should read:
Alphabetizer theAlaphBet( &charArray );
However I think you may be overcomplicating things. You might be better off using a reference rather than a pointer:
const int MAX_LINES = 10, MAX_STRING = 80;
class Alphabetizer{
public:
Alphabetizer(char & inArray[][MAX_STRING]) : input(inArray){};
private:
char & input[MAX_LINES][MAX_STRING];
};
int main(){
char charArray[MAX_LINES][MAX_STRING];
Alphabetizer theAlaphBet(charArray);
return 0;
}
You might also want to look into using std::string instead as this may help to simplify your code.

Is it alright to use memcpy() to copy a struct that contains a pointer?

I was thinking about this the other day and I am curious if this is a bad idea...
Lets say there is a structure that contains a pointer to a string array.
Would the memcpy() copy the 'name' array pointer in the below example?
Edit: The std is inaccessible in this example.
struct charMap
{
unsigned char * name;
unsigned char id;
};
typedef struct charMap CharMapT;
class ABC
{
public:
ABC(){}
void Function();
CharMapT* structList;
}
void ABC::Function ()
{
CharMapT list[] =
{
{"NAME1", 1},
{"NAME2", 2},
{"NAME3", 3}
};
structList = new CharMapT[sizeof(list)];
memcpy(structList, &list, sizeof(list));
}
There are several errors in the code presented, which I will talk about first, followed by my stock-diatribe of pointers vs. arrays.
struct charMap
{
unsigned int * name;
unsigned int id;
};
typedef struct charMap CharMapT;
This declares a structure type that includes a pointer to unsigned int as the first member (name) and an int as the second member (id). On a 32-bit system with default byte packing this will be 8 bytes wide (32-bit pointer = 4bytes, 32-bit signed int=4bytes). If this is a 64-bit machine the pointers will be 8 bytes wide, the int still-likely 32-bits wide, making the structure size 12 bytes.
Questionable Code
void ABC::Function ()
{
CharMapT list[] =
{
{"NAME1", 1},
{"NAME2", 2},
{"NAME3", 3}
};
structList = new CharMapT[sizeof(list)];
memcpy(structList, &list, sizeof(list));
}
This allocates dynamic array of CharMapT structs. How many? More than you think. The sizeof(list) will return the byte-count of the list[] array. Since a CharMapT structure is 8 bytes wide (see above) this will 3 * 8, or 24 CharMapT items (36 items if using 64-bit pointers).
We then memcpy() 24 bytes (or 36 bytes) from list (the & in &list is unecessary) to the newly allocated memory. this will copy over 3 CharMapT structures, leaving the other 21 we allocated untouched (beyond their initial default construction).
Note: you're initializing a const char * to a field declared as unsigned int *, so if this even compiled the fundamental data type would be different. Assuming you fixed your structure and change the pointer type to const char *, the addresses of the static string constants (the addresses of the "NAME" constants) somewhere in your const data segment will be assigned to the pointer variables of the elements in structList[0].name, structList[2].name, and structList[3].name respectively.
This will NOT copy the data pointed to. it will only copy the pointer values. If you want copies of the data then you must raw-allocate them (malloc, new, whatever).
Better still, use an std::vector<CharMapT>, use std::string for CharMapT::name, and use std::copy() to replicate the source (or even direct-assignment).
I hope that explains what you were looking for.
Pointer vs. Array Diatribe
Never confuse a pointer with an array. A pointer is a variable that holds an address. Just like an int variable hold an integer value, or a char variable holds a character type, the value held in a pointer is an address
An array is different. It is also a variable (obviously), but it cannot be an l-value, and nearly every place it is typically used a conversion happens. Conceptually that conversion results in a temporary pointer that points to the data type of the array, and holds the address of the first element. There are times when that concept does not happen (such as the applying the address-of operator).
void foo(const char * p)
{
}
char ar[] = "Hello, World!";
foo(ar); // passes 'ar', converted to `char*`, into foo.
// the parameter p in foo will *hold* this address
or this:
char ar[] = "Goodbye, World!";
const char *p = ar; // ok. p now holds the address of first element in ar
++p; // ok. address in `p` changed to address (ar+1)
but not this:
char ar[] = "Goodbye, World!";
++ar; // error. nothing to increment.
It won't copy your actual data pointed by name. It will copy the pointer and you'll have 2 pointers to the same place in 2 objects (for each pair of objects in 2 arrays).
All you really need to know here is that memcpy will give you a bit for bit copy of the original. So what you'll have is two pointers with the same value (i.e., an address) which refer to the same data.
On a side note, you have declared name as a pointer to int, which is of course wrong here. It should be a const char*. Also, as this is C++ and not C, you're better served by something like std::copy which won't break your code subtly if charMap someday becomes a complex type. On the same note, prefer std::string instead of const char* in most situations.
Your use of sizeof() is wrong when calling new. You are allocating an array of CharMapT elements. You have to specify the number of elements, but you are specifying a byte count instead. So you need to fix that:
structList = new CharMapT[sizeof(list) / sizeof(CharMapT)];
With that fixed, the result of the memcpy() will be that structList will contains an exact copy of the raw data that list[] contains. That means that the structList[N].name pointers will contain the same values as the list[N].name pointers, and thus they will all be pointing at the same physical memory for the string values.
If you want to do a deep copy of the string values, you have to allocate them separately, eg:
void ABC::Function ()
{
CharMapT list[] =
{
{"NAME1", 1},
{"NAME2", 2},
{"NAME3", 3}
};
int num = sizeof(list) / sizeof(CharMapT);
structList = new CharMapT[num];
for (int i = 0; i < num; ++i)
{
int len = strlen(list[i].name);
structList[i].name = new char[len+1];
strcpy(structList[i].name, list[i].name);
structList[i].name[len] = 0;
structList[i].id = list[i].id;
}
...
for (int i = 0; i < num; ++i)
delete[] structList[i].name;
delete[] structList;
}
I'd like to add to #EdS.'s answer:
Your code is just much more c++ than c-style c++ code if you do it like this:
#include<string>
#include<vector>
struct CharMap
{
CharMap(const std::string& name, unsigned char id); // only needed if you don't use -std=c++11
std::string name;
unsigned char id;
};
CharMap::CharMap(const std::string& name, unsigned char id):
name(name),
id(id)
{}
class ABC
{
public:
ABC(); // or ABC() = default; if you use -std=c++11
void Function();
private:
std::vector<CharMap> structList;
}
ABC::ABC(){} // not needed with -std=c++11
void ABC::Function ()
{
// This works with -std=c++11:
//structList =
//{
// {"NAME1", 1},
// {"NAME2", 2},
// {"NAME3", 3}
//};
// without c++11:
structList = std::vector<CharMap>(3);
structList[0] = CharMap("NAME1",1); // don't worry about copies, we have RVO (check wikipedia or SO)
structList[1] = CharMap("NAME2",2);
structList[2] = CharMap("NAME2",3);
}
Why not using std::vector for making an array? You can do that like this:
#include<vector>
std::vector<CharMapT> structList(list.size());
It is safer, too, avoiding using pointers decreases the chance of memory leaks or bugs arising due to wrongly using the sizeof operator.
I suppose you do not really want a structList, that has as many elements as the memory size of your list. (If list is double this could be many times more than the number of elements in your list.)
Also, memcpy is really not necessary, if list is also a vector (that is a c function really). You just do a simple assign operation:
structList = list; // given that list is a vector.
This will copy the elements like memcpy.