A little context: I'm trying to make a very simple hashing function/hash table as described here. I'm basically on the first step, blindly adding a key to an array based on the letter it starts with (no checking if space is occupied yet). The code I'm using to do this so far:
int main(int argc, char **argv) {
char *arrayKeys[300];
std::string aName("Charles");
char *aNameCpy = new char[aName.size() + 1];
std::copy(aName.begin(), aName.end(), aNameCpy);
aNameCpy[aName.size()] = '\0';
int kPos = storeKey(arrayKeys, aNameCpy);
std::cout << "The new position in arrayKeys for 'Charles' is: " <<
kPos << "\ncontaining the text: " << arrayKeys[kPos] << std::endl;
delete[] aNameCpy;
return 0;
}
int storeKey(char **keys, char *key) {
int charLett = -1;
charLett = (int)key[0];
if(charLett != -1)
charLett = charLett - 65;
keys[charLett * 10] = key;
return charLett*10;
}
My question is, how can I add a string to the array (arrayKeys) where it is fully apart of the array, and not reliant upon the original string? If I delete the copy of the string (aNamCpy) before I print the array key out, the array key turns into garbled symbols. I'm copying the string before sending it to the function because I need a non-const string to add to the arrayKeys array (so it can be modified), and any method of string I looked at seemed to return const.
(Another version of this I attempted can be found here, but I would rather not initialize the arrayKeys like that - with a definite second dimension (string) length)
C++ is still very new to me so I can't figure out how to juggle the non-const part with copying the string into arrayKeys. Any help would be very much appreciated.
Here's how I'd change the code to use more modern C++ constructs. I think you'll find this way easier to use.
int storeKey(vector<string> &keys, const string &key) {
int charLett = -1;
if (!key.empty()) { // you weren't doing this before!
charLett = key[0];
charLett = toupper(charLett) - 'A';
keys[charLett * 10] = key;
}
return charLett*10;
}
int main() {
vector<string> arrayKeys(300);
std::string aName("Charles");
// No need to bother with the awkward copying.
// std::vector and std::string will take care of it for us.
int kPos = storeKey(arrayKeys, aName);
if (kPos >= 0) {
cout << "The new position in arrayKeys for 'Charles' is: " <<
kPos << "\ncontaining the text: " << arrayKeys[kPos] << endl;
}
// Don't have to remember to delete anything because nothing was new'ed.
return 0;
}
(#Kristo has the right idea. I'm just going to add a comment to the question as asked.)
Basically, don't delete aNameCpy. You require the copy to remind valid and therefore it shouldn't be deleted. You should only delete the strings if and when you ever delete the entire hash.
C++ is still very new to me so I can't figure out how to juggle the
non-const part
You could declare both keys and key to be const char **keys, const char *key. keys is pointer-to-pointer-to-char. More precisely, it's a pointer to nonconst pointer to const char. In other words, you can modify keys, you just cannot modify the actual characters it points (indirectly) at.
So, simply put const in your declaration of storeKey int storeKey(const char **keys, const char *key) and update arrayKeys accordingly const char *arrayKeys[300];
One final style issue: You should copy the string inside storeKey, not in main. This is better design, as it makes clear to the reader that storeKey "owns" the copy.
int storeKey(char **keys, const char *key) {
char * the_copy = new char[strlen(key)+1];
strcpy(the_copy, key);
... and so on
But, in short, use C++ string instead of all this, if you can!
Related
I have been given a task, where I need to create the string_copy function Note that the function body and prototypes have been given by the source and that needs to be maintained. The portions written by me are after the comment write your code here.
#include <iostream>
using namespace std;
int string_length(const char* string_c);
char* string_copy(const char* string_c);
int main()
{
const char* string_c = "This is a string and is a long one so that we can create memory leaks when it is copied and not deleted";
// write your code here
int length = string_length(string_c);
cout << "Copied String: " << string_copy(string_c) << endl;
return 0;
}
int string_length(const char* string) {
int length = 0;
for (const char* ptr = string; *ptr != '\0'; ++ptr) {
++length;
}
return length;
}
char* string_copy(const char* string) {
// we need to add 1 because of ’\0’
char* result = new char[string_length(string) + 1];
// write your code here (remember zero-termination !)
int i;
for (i = 0; string[i] != '\0'; ++i)
{
result[i] = string[i];
}
result[i] = '\0';
return result;
}
Now task tells me
that it is very important that any memory allocated with e=new TYPE is
released later with delete e (and a=new TYPE[size] with delete [] a)
else this will lead to an error.
It is not exactly clear if error means compile/runtime error or error as in my task did not meet the requirement error.
My question is, in this code how do I delete the intermediate dynamically created result array? If I delete result, won't it fail the purpose of the task? Then how am I to respect the quotation above or maybe simulate memory leak as given in the long string constant?
Thanks.
EDIT: Why the negative votes? Please at least explain the reason! I am not asking any solution or something, but mere suggestion if I am missing some point or not!
The caller of string_copy would be responsible for releasing the memory when it's done with it by calling delete[] on it.
This is, by the way, a terrible way to write C++ code. You should be using std::string or std::vector<char> or something like that.
Here's why:
int length = string_length(string_c);
char* copy = string_copy(string_c);
cout << "Copied String: " << copy << endl;
delete[] copy;
return 0;
Yuck.
In fact the ideal solution is to use std::string and not char *. There is no real need of using char * instead of std::string in your example.
With std::string:
You don't need to new anything
You don't need to delete anything
You can do everything with std::string, that you do with char *.
So I'm studying this book, and I came across an exercise which (briefly) wants me to remove all white spaces of a char-array by using a function: void removeSpaces(char* s)
[iostream, cstring are included and SIZE is defined]
This is main():
int main() {
char a[SIZE] = "a bb ccc d";
cout << a << endl; // a bb ccc d
removeSpaces(a);
cout << a << endl; // a bb ccc d instead of abbcccd
}
This is removeSpaces():
void removeSpace(char* s) {
int size = strlen(s);
char* cpy = s; // an alias to iterate through s without moving s
char* temp = new char[size]; // this one produces the desired string
s = temp; // s points to the beginning of the desired string
while(*cpy) {
if(*cpy == ' ')
cpy++;
else
*temp++ = *cpy++;
}
cout << s << endl; // This prints out the desired result: abbcccd
}
(My choice of names isn't ideal, but nevermind that now.) So my function basically does what I want it to do, except that the result has no effect outside of the function's scope. How can I accomplish that? What am I missing, what am I doing wrong?
Since you pass the pointer by value, you are surely changing the array in place. Obviously, you'd remove spaces like this:
void removeSpaces(char* s) {
*std::remove(s, s + strlen(s), ' ') = 0;
}
Your function shouldn't even do any string copying. It should just do an in-place replacement:
void removeSpace(char* s)
{
for (char* s2 = s; *s2; ++s2) {
if (*s2 != ' ')
*s++ = *s2;
}
*s = 0;
}
Even terser:
void removeSpace(char* s)
{
char* s2 = s;
do {
if (*s2 != ' ')
*s++ = *s2;
} while (*s2++);
}
You change the value of s but that is a local variable and thus has no affect outside the function:
void removeSpace(char* s)
You could change this to:
void removeSpace(char*& s)
This makes a reference to the passed value. Thus changing s will change the original. Unfortunately that does not work for you because of the way you call removeSpace() (as you pass an array).
char a[SIZE] = "a bb ccc d";
removeSpaces(a);
You could change your code too:
char buffer[SIZE] = "a bb ccc d";
char* a = buffer;
removeSpaces(a);
Now the modifications suggested above would work correctly. But you are leaking memory (you dynamically allocate memory in removeSpace() that is never released. The best way to resolve that is to use modern C++ techniques rather than write C and and compile it with the C++ compiler.
Couple of solutions:
Modify the array in place (no dynamic allocation)
Use proper containers (std::string or std::vector)
Use the standard algorithms.
The call to removeSpaces has no affect because you are creating a temporary buffer in the function and copying the string into that while applying the transformation. You can fix this by removing the temporary buffer and just modifying the string in place.
void removeSpaces(char* s)
{
char* cpy = s; // an alias to iterate through s without moving s
char* temp = s;
while (*cpy)
{
if (*cpy != ' ')
*temp++ = *cpy;
cpy++;
}
*temp = 0;
cout << s << endl; // This prints out the desired result: abbcccd
}
Just add one last line to your function:-
strcpy(s, temp);
and remove unnecessary ones:-
s = temp;
Your function receives a copy of the pointer a. Changing the copy within the function does not change the caller's original a pointer.
So I'm making a function that is similar to SubStr. This is an assignment so I cannot use the actual function to do this. So far I have created a function to take a string and then get the desired substring. My problem is returning the substring. In the function when I do Substring[b] = AString[b]; the substring is empty, but if I cout from inside the function I get the desired substring. So what is wrong with my code?
Here is a working demo: http://ideone.com/4f5IpA
#include <iostream>
using namespace std;
void subsec(char AString[], char Substring[], int start, int length);
int main() {
char someString[] = "abcdefg";
char someSubString[] = "";
subsec(someString, someSubString, 1, 3);
cout << someSubString << endl;
return 0;
}
void subsec(char AString[], char Substring[], int start, int length) {
for (int b = start; b <= length; b++) {
Substring[b] = AString[b];
}
}
Maybe this does what you're looking for? It's hard to say as your initial implementation used the length parameter as more of an end position.
#include <iostream>
using namespace std;
void subsec(char AString[], char Substring[], int start, int length)
{
const int end = start + length;
int pos = 0;
for(int b = start; b < end; ++b)
{
Substring[pos++] = AString[b];
}
Substring[pos] = 0;
}
int main()
{
char someString[50] = "abcdefghijklmnopqrstuvwxyz";
char someSubString[50];
subsec(someString, someSubString, 13, 10);
cout << someSubString << endl;
return 0;
}
There are several problems with the code:
1) The char arraysomeSubString has size 1 which cannot hold the substring.
2) The subsec is not correctly implemented, you should copy to the Substring from index 0.
Also remember to add \0 at the end of the substring.
void subsec(char AString[], char *Substring, int start, int length) {
int ii = 0;
for (int jj = start; jj <= length; jj++, ii++) {
Substring[ii] = AString[jj];
}
Substring[ii] = '\0';
}
You need to allocate more than 1 byte for someSubString i.e.
char someSubString[] = "xxxxxxxxxxxxxxxxxx";
or just
char someSubString[100];
if you know the max size you'll ever need.
Either would allocate enough space for the string you're copying to it. Then, you're not doing anything about the terminating 0 either. At the end of a C-style string there needs to be a terminating null to signify end of string. Otherwise cout will print something like;
abcdefgxxxxxxx
if you initialized with x's as I indicated.
There are a few problems with your code as it stands. Firstly, as your compiler is no doubt warning you, in C++ a string literal has type const char[], not just char[].
Secondly, you need to have enough space to store your substring. A good way to do this is for your function to allocate the space it needs, and then pass back a pointer to this memory. This is the way things are typically done in C code. The only thing is that you have to remember to delete the allocated array when you're done with it. (There are other, better ways to do this in C++, with things like smart pointers and wrapper objects, but those come later :-) ).
Thirdly, you'll have a problem if you request a length which is actually longer than the passed-in string -- you'll run off the end and start copying random memory (or just crash), which is definitely not what you want. C strings are terminated with a "nul byte" -- so you need to check whether you've come across this.
Speaking of the nul, you need to make sure that your substring ends with one.
Lastly, it's not really a problem but there's no need for the start parameter, you can just pass a pointer to the middle of the array if you want to.
char* substring(const char* str, int length)
{
// Allocate memory for substring;
char* subs = new char[length+1];
// Copy characters from given string
int i = 0;
while (i < length && str[i] != '\0') {
subs[i] = str[i];
i++;
}
// Append the nul byte
subs[i] = '\0';
return subs;
}
int main()
{
const char someString[] = "foobarbaz"; // Note -- must be const in C++
char* subs = substring(someString + 3, 3);
assert(strcmp(subs, "bar") == 0);
delete subs;
}
I'm trying to return an array of char arrays. While I am able to create the array succesfully, I am apparently returning it incorrectly.
Is my syntax off or am I committing some other error that I am overlooking?
Here are the most relevant lines, the full functions follow
// prototype
char * testFunc();
// Function
char * testFunc() {
char* ptrArray[2];
return(*ptrArray);
}
// Assignment in main()
int main {
char * res = testFunc();
}
Here is a simplified version of the full code
#include <iostream>
using std::cout;
// prototype
char * testFunc();
int main() {
short i, j;
char * res = testFunc();
for (i=0; i < 2; i++)
cout <<"This is res[" << i << "] : " << res[i] <<"\n";
return(0);
}
char * testFunc() {
char word1[] = "one";
char word2[] = "two";
// create an array of char*
char* ptrArray[2];
ptrArray[0] = word1;
ptrArray[1] = word2;
for (int i=0; i<2; i++)
cout <<"This is ptrArray[" << i << "] : " << ptrArray[i] <<"\n";
return(*ptrArray);
}
Returning objects allocated in the automatic storage (also known as "stack objects") from a function is undefined behavior. When you need to return an array in C, you have three options:
Return an object allocated in the static storage area,
Return an object allocated in the dynamic storage area, or
Take a buffer and max size, and return the actual size of the array.
The first option is rarely applicable, because it makes your function non-reentrant. The third option is widespread, but it has limitations: when you must return more data than fits into the buffer, the API needs to be called multiple times.
This leaves us with option number two: use new to allocate the memory that you are returning, copy the data into it, and return the result to the caller. It is now caller's responsibility to free the dynamic memory:
// You need two asterisks: a string needs one asterisk, you return
// a 1-D array of strings, so you need another level of indirection.
char ** testFunc() {
char word1[] = "one"; // Automatic memory - needs a copy
char word2[] = "two"; // Automatic memory - needs a copy
// create an array of char*
char** ptrArray = new char*[2];
ptrArray[0] = new char[strlen(word1)+1]; // Plus one for the null terminator
strcpy(ptrArray[0], word1);
ptrArray[1] = new char[strlen(word2)+1]; // Plus one for the null terminator
strcpy(ptrArray[1], word2);
for (int i=0; i<2; i++)
cout <<"This is ptrArray[" << i << "] : " << ptrArray[i] <<"\n";
return ptrArray;
}
Note: you may not have reached the standard library yet, so the solution below may not apply. However, you should know that the above solution is not the best C++ can do: you can rewrite this wit dynamic containers, and make the code much easier to read:
vector<strig> testFunc() {
vector<string> res;
res.push_back("one");
res.push_back("two");
return res;
}
In C++11 you can do even better:
vector<string> test() {
return vector<string> {"one", "two"};
}
A single "character array" is roughly equivalent to char *. To return an array of arrays, you need char ** or perhaps char[]*.
As the other answer says, if you're returning pointers from inside a function these need to be "global" memory -- not local variables which are only ever valid within the function. Returned pointers to "stack-based" local variables are no longer valid after the function returns, since that stack-space will be overwritten by the next function-call (or sooner).
[Since the original posting, it has been suggested that const char* and (presumably) const char** would be preferred, for "const correctness"].
My C++ is rusty.. but:
const char** testFunc() {
const char word1[] = "one";
const char word2[] = "two";
// create an array of char*
const char** ptrArray = (const char **) malloc( 2 * sizeof(char *));
ptrArray[0] = word1;
ptrArray[1] = word2;
for (int i=0; i<2; i++)
cout <<"This is ptrArray[" << i << "] : " << ptrArray[i] <<"\n";
return ptrArray;
}
And in main:
int main() {
short i;
// get the array -- will now be our responsibility to free
const char** ptrArray = testFunc();
for (i=0; i < 2; i++) {
// read single pointer (char*), from our array of pointers (char**)
const char* word = ptrArray[i];
cout <<"This is res[" << i << "] : " << word <<"\n";
}
// free it.
free( ptrArray);
return(0);
}
As the original question is posed, the inputs for the array are string constants. Returning a mutable array of constant strings is thus preferred, to returning a mutable array of mutable strings.
Even if the full system (not shown in the question) built up the strings programmatically, as per other the answer, it is more than likely they would best be returned as const char * -- not treated as buffers for further modification.
Below the map 'widgets' is always size of 1 for some reason. There should be 4 when it's done.
Output:
Widget: widget_ram_label:layout_bar:0 1
Widget: widget_ram_active:layout_bar:0 1
Widget: widget_ram_total:layout_bar:0 1
Widget: widget_wlan0_label:layout_bar:0 1
Here's widgets:
std::map<const char *, Widget *> widgets;
And here's the code:
void Generic::BuildLayouts() {
for(std::map<const char*, std::vector<widget_template> >::iterator l =
widget_templates.begin(); l != widget_templates.end(); l++) {
std::vector<widget_template> layout = l->second;
for(unsigned int i = 0; i < layout.size(); i++ ) {
Json::Value *widget_v = CFG_Fetch_Raw(root, layout[i].key);
if(!widget_v) {
error("No widget named <%s>", layout[i].key);
continue;
}
Json::Value *type = CFG_Fetch_Raw(widget_v, "type");
if(!type) {
error("Widget <%s> has no type!", layout[i].key);
delete widget_v;
continue;
}
Widget *widget;
char name[256];
sprintf(name, "%s:%s", layout[i].key, l->first);
char tmp[256];
int i = 0;
sprintf(tmp, "%s:%d", name, i);
while(widgets.find(tmp) != widgets.end()) {
i++;
sprintf(tmp, "%s:%d", name, i);
}
memcpy(name, tmp, 256);
if(strcmp(type->asCString(), "text") == 0) {
widget = (Widget *) new WidgetText(this, name, widget_v,
layout[i].row, layout[i].col);
std::cout << "Widget: " << name << " " << widgets.size() << std::endl;
} else {
error("Unknown widget type: %s", type->asCString());
}
if(widget) {
widgets[name] = widget;
}
delete type;
}
}
}
std::map<const char *, Widget *> widgets;
Don't do this. Never use char* as a map key. (The map uses the std comparison std::less for its keys and that compares the addresses for pointers.)
Do yourself a favour and use std::string. Once you mastered this, you might try to go back dealing with C strings again.
Maybe because all your name pointers poitns to the same buffer? Because the content of name changes, but not the value of the pointer to name in the map.
Try to use std::string instead.
Replace you name buffer by
#include <string >
//...
std::string name = "the name";
and replace your map by
std::map< const std::string , Widget* > widgets;
That will make your life easier, safer and more readable.
To help formatting, use std::stringstream or boost string algorithms.
A start of example, this code :
char name[256];
sprintf(name, "%s:%s", layout[i].key, l->first);
char tmp[256];
int i = 0;
sprintf(tmp, "%s:%d", name, i);
while(widgets.find(tmp) != widgets.end()) {
i++;
sprintf(tmp, "%s:%d", name, i);
}
memcpy(name, tmp, 256);
would be written like this:
Widget *widget = NULL; // always initialize your variables!!!
std::stringstream name_stream; // this will let us format our string
name_stream << layout[i].key << ":" << l->first;
std::string name = name_stream.str(); // now we got the string formated.
std::stringstream tmp_stream; // same for tmp
tmp_stream << name << ":" << i; // will automatically convert basic types, see the doc if you want specific formatting
std::string tmp = tmp_stream.str(); // now we got the string formated.
// the while loop have no sense : a map have only one value by key
// if you want to have several values by key, use std::multi_map instead -- it don't work exactly the same though
// for now i'll just assume you just need to find the value associated to the name:
typedef std::map< const std::string, Widget* > WidgetMap; // for ease of writing, make a shortcut! ease your life!
WidgetMap::iterator tmp_it = widgets.find( tmp );
if( tmp_it != widgets.end() )
{
// starting here I don't understand you code, so I'll let you find the rest :)
}
Your map doesn't know how to compare C-style strings properly. If you insist on using C-style strings as keys for your map, you need to supply the proper comparator. For example, it could be done as follows
inline bool str_less(const char *s1, const char *s2) {
return strcmp(s1, s2) < 0;
}
...
std::map<const char *, Widget *, bool (*)(const char *, const char *)> widgets(str_less);
Or, if you wish, you can implement the comparator as a class-based functor.
But that's not all. Your map does not know how to (and will not) manage memory for your const char * keys. You are passing a pointer to a local short-lived buffer to the map as a key. This is totally useless. In case of const char * keys you are supposed to manage the key memory by yourself. For example, you can allocate each key dynamically and pass a pointer to such a dynamically allocated and properly initialized key buffer to the map. And later, when the time comes to destroy your map, it is your responsibility to deallocate all these key buffers manually.
This all is a major pain in the neck. For this reason you should be much better off using 'std::string' as the key type, not a C-style string.
As an alternative to using std::string, you can just strdup the buffer before using it. Just remember to free it when you free your std::map.
It's probably hashing on the address of name. Use std:string.