c++ Create Char Array from Variable String passed as function parameter - c++

I want to create a char array in a function from a string I have passed in.
bool foo(string s1, string s2) {
char s1_char_array[] = s1;
char s2_char_array[] = s2;
}
But I am met with
"Initialization with '{...}' expected for aggregate object."
For both declarations.
I've tried a lot of work arounds but they all have errors of their own. Learning C++ and every tutorial I find has the value hardcoded. How to make this work?

You can dynamically allocate arrays and copy data from the strings there.
#include <string>
#include <cstring> // for strcpy()
#include <algorithm> // for std::copy()
using std::string;
bool foo(string s1, string s2) {
// allocate arrays (+1 for terminating null-characters)
char* s1_char_array = new char[s1.size() + 1];
char* s2_char_array = new char[s2.size() + 1];
// copy data to arrays
#if 0
// if the strings are guaranteed not to contain '\0'
strcpy(s1_char_array, s1.c_str());
strcpy(s2_char_array, s2.c_str());
#else
// if the strings may contain '\0'
std::copy(s1.begin(), s1.end(), s1_char_array);
std::copy(s2.begin(), s2.end(), s2_char_array);
s1_char_array[s1.size()] = '\0';
s2_char_array[s2.size()] = '\0';
#endif
// do things with the arrays
// deallocate the arrays
delete[] s1_char_array;
delete[] s2_char_array;
// return something
return false;
}

The bad news is that there is no way to create an array variable from a std::string in general because std::string can represent strings of any size (very large practial limits exist but you'll run out of memory first on 64 bit systems) and that size is dynamic i.e. determined at run time. By contrast, the size of an array must be known at compile time at which time the size of the string is still unknown.
Another problem is that your arrays have automatic storage and the memory available for automatic storage is very limited - usually one to few megabytes on desktops / servers; potentially much less on embedded - while the dynamic memory owned by the string doesn't have such restriction and thus attempting to fit a large string into automatic storage could easily cause a "stack overflow".
Another issue is that arrays cannot be copy-constructed nor converted from other types so char s1_char_array[] = some_variable; can never work.
The good news is that you don't ever need to do that either. std::string already contains an array of chars internally, so there is no need to create a new array. Just keep using the array that is in the string.

this is what I come up with.
you have to declare the char array size same as strings by using string.lenght() function and
then use loops to copy the strings elements to char array index by index
bool foo(string s1, string s2) {
char s1_char_array[s1.length()] ;
char s2_char_array[s2.length()] ;
for(int i=0; i<s1.length(); i++){
s1_char_array[i]=s1[i];
}
for(int i=0; i<s2.length(); i++){
s2_char_array[i]=s1[i];
}
}

Related

How to copy a string to newly allocated memory?

In below code example, memory for an integer is dynamically allocated and the value is copied to the new memory location.
main() {
int* c;
int name = 809;
c = new int(name);
cout<<*c;
}
But, when I try to do the same with a char string it doesn't work.
Why is this?
int main() {
char* p;
char name[] = "HelloWorld";
p = new char(name);
cout << p;
}
Your second example doesn't work, because char arrays work differently than integer variables. While single variables can be constructed this way, this doesn't work with (raw) arrays of variables. (As you have observed.)
In C++ you should try to avoid handling pointers and raw arrays as much as you can. Instead, you'd rather use the standard library containers to take a copy of that string to an array of dynamically allocated memory. std::string and std::vector<char> are especially suitable in this case. (Which one should be preferred depends a bit on the semantics, but probably it's the std::string.)
Here's an example:
#include <string>
#include <vector>
#include <cstring>
#include <iostream>
int main(){
char name[] = "Hello World";
// copy to a std::string
std::string s(name);
std::cout << s << '\n';
// copy to a std::vector<char>
// using strlen(name)+1 instead of sizeof(name) because of array decay
// which doesn't occur here, but might be relevant in real world code
// for further reading: https://stackoverflow.com/q/1461432
// note that strlen complexity is linear in the length of the string while
// sizeof is constant (determined at compile time)
std::vector<char> v(name, name+strlen(name)+1);
std::cout << &v[0] << '\n';
}
The output is:
$ g++ test.cc && ./a.out
Hello World
Hello World
For reference:
http://en.cppreference.com/w/cpp/string/basic_string
http://en.cppreference.com/w/cpp/container/vector
Your second code snippet does not work because new int(name) initializes an int from an int, while new char(name) tries to initialize a char from a char[11] array.
There is no array constructor taking an array in C++. In order to make a copy of an array, you must allocate an array, and then copy data into it:
p = new char[sizeof(name)];
std::memcpy(p, name, sizeof(name));
In the first case you allocate memory for a single int object, and initialize with a single int value. Great, this works.
In the second case you allocate memory for a single char object, and initialize it with an array of characters. It does not work, an array of objects does not fit in a memory of a single object. Besides, the array has a different type, so the initialization is ill-formed.
To allocate memory for an array of characters (such as a string), you can use new[]:
char* ptr = new char[11]{"HelloWorld"};
PS. The GNU compiler (until the current version 7 at least) and clang (until version 4) have a bug which breaks the above initialization. A workaround is to copy the string after allocation.
PPS. While it is useful to learn these things, don't do manual memory management in actual programs. Use RAII containers such as std::string for strings and std::unique_ptr for single dynamic objects.
Your code doesn't work as you are trying to initialize a char instead of array of characters. In order to dynamically allocate memory, you need to allocate the memory and then copy over the content.
p = new char[strlen(name) +1];
std::strcpy(p, name);

char*/string concatenation without copying?

I would like to concatenate 2 strings in C or C++ without new memory allocation and copying. Is it possible?
Possible C code:
char* str1 = (char*)malloc(100);
char* str2 = (char*)malloc(50);
char* str3 = /* some code that concatenates these 2 strings
without copying to occupy a continuous memory region */
Then, when I don't need them any more, I just do:
free(str1);
free(str2);
Or if possible, I would like to achieve the same in C++, using std::string or maybe char*, but using new and delete (possibly void operator delete ( void* ptr, std::size_t sz ) operator (C++14) on the str3).
There are a lot of questions about strings concatenation, but I haven't found one that asks the same.
No, it is not possible
In C, malloc operations return blocks of memory that have no relationship to each other. But in C, strings must be a continuous array of bytes. So there is no way to extend str1 without copying, let alone concatenate.
For C++, perhaps ropes may be of interest: See this answer.
Ropes are allocated in chunks that do not have to be contiguous. This supports O(1) concatenation. However, the accessors make it appear as a single string of bytes. I'm certain that to convert ropes back to std::string or C style strings will take a copy however, but this is probably the closest to what you want.
Also, it is probably a premature optimization to worry about the costs of copying a few strings around. Unless you are moving lots of data, it won't matter
Text concatenation is possible by writing your own string data structure. Easier in C++ than C.
struct My_String
{
std::vector<char *> text_fragments;
};
You would have to implement all the text manipulation and searching algorithms based on this data structure. Nothing in the C library could be applied to the My_String structure. The std::string in C++ would not be compatible.
One of the issues is how to handle text modification. If one of the text fragments is a constant literal (that can't be modified), it would need to be copied before it could be modified. But copying is against the requirements. :-(
A "string" in C is a an array of chars with a null char at the end. And an array is "a data structure that lets you store one or more elements consecutively in memory". GNU C reference
You cannot concatenate two arrays that are not in consecutive memory blocks without copying one of them. You can do it however without allocating new memory. E.g.
char* str1 = malloc(100); // size 100 bytes, uninitialised
str1[0] = '\0'; // string length 0, size of str1 100
strcat(str1, "a"); // string length 1, size of str1 still 100
strcat(str1, "b"); // string length 2, size of str1 still 100
You could if you want retrieve chars of 2 strings as if they were one without copying or reallocating. Here is an example function to do that (simple example, don't use in production code)
char* str1 = (char*)malloc(100);
char* str2 = (char*)malloc(50);
char get_char(int i) {
if (i > 0 && i < 100) {
return str1[i];
}
if (i >= 100 && i < 150) {
return str2[i-100];
}
return 0;
}
But in such a case you couldn't have a char* str3 to perform pointer arithmetic with and access all 150 chars.
Tags C and C++ are contradictory. In C, I'd recommend exploring realloc. You can code something along following lines:
char* str = malloc(50);
str = realloc(ptr, 55);
If you are lucky, the realloc call will not reallocate new memory and just 'extened' the already allocated segment, but there is no guarantee for this. This way you at at least have a shot of avoiding reallocations of the string. You will still have to copy contents of the second string into neweley allocated memory.

C++ copying char to a char array (Debug assertion failed) says string is not null terminated

Just trying to assign chars to the char array and it says string in not null terminated?
I want to be able to change the teams around in the array like a scoreboard.
#include <string.h>
#include <iostream>
int main(int argc, char* argv[])
{
char Team1[7] = "Grubs";
char Team2[7] = "Giants";
char Team3[7] = "Bulls";
char Team4[7] = "Snakes";
char Team5[7] = "Echos";
char TeamList[5][7];
strcpy_s(TeamList[0], Team1);
strcat_s(TeamList[1], Team2);
strcat_s(TeamList[2], Team3);
strcat_s(TeamList[3], Team4);
strcat_s(TeamList[4], Team5);
TeamList[5][7]= '\0';
system("pause");
return 0;
}
strcat() (which is a "less-safe" version of strcat_s()) requires both strings to be null-terminated. That's because strcat() appends its second parameter (source) where first parameter (dest) ends. It replaces null-terminator of dest with first character of source, appends rest of source and then
a null-character is included at the end of the new string formed by
the concatenation of both
I would simply change
strcpy_s(TeamList[0], Team1);
strcat_s(TeamList[1], Team2);
strcat_s(TeamList[2], Team3);
strcat_s(TeamList[3], Team4);
strcat_s(TeamList[4], Team5);
to
strcpy_s(TeamList[0], Team1);
strcpy_s(TeamList[1], Team2);
strcpy_s(TeamList[2], Team3);
strcpy_s(TeamList[3], Team4);
strcpy_s(TeamList[4], Team5);
strcpy_s() does not have any requirements regarding contents of destination - only its capacity matters.
If you want to stick with strcat_s(), do this:
char TeamList[5][7];
memset(TeamList, 0, sizeof(char) * 5 * 7);
Then, this line:
TeamList[5][7]= '\0';
is not required, It is incorrect anyway, because for N-element array valid indexes are [0; N-1].
EDIT
Since in your case swapping comes into play, I would suggest you totally different approach.
First of all:
#include <string>
Then, initialize teams this way:
std::string TeamList[] =
{
"Grubs",
"Giants",
"Bulls",
"Snakes",
"Echos"
};
Now, TeamList is an array containing 5 elements and each of these elements is an object of type std::string, containing name of a particular team.
Now, if you want to swap, let's say, teams 1 and 3:
std::swap(TeamList[1], TeamList[3]);
std::swap() is a standard C++ function extensively used in standard library implementation. It is overloaded for many standard types, including std::string. This solution has one, critical benefit: if string's content is held on the heap, swapping two strings is as simple as swapping pointers (and some length/capacity variables).
Oh, and one more thing: if you are not familiar with std::string and you would need to get pointer to a buffer containing string's data, you can do it this way:
const char* team_1_raw_name = TeamList[0].c_str();
See this page for more info about std::string
strcat requires that there already be a null-terminated string in the destination to concatenate the source string onto; you're calling it with uninitialised values in the destination.
It looks like you want strcpy in every case, not just the first.
Also, remove the bogus TeamList[5][7]= '\0';. Even if you fix it to write inside the array bounds, each string has already been terminated by strcpy so there's no need to try to do that yourself.
Then stop messing around with low-level arrays and pointers. std::vector<std::string> would be much friendlier.

How to use multidimensional char or string arrays in a loop

I'm so new to C++ and I just can't figure out how to use any multidimesional arrays. I want to do something like that:
input number of product: number; //the products' name can be 7 with NULL char. (max 6)
char arr[number][7];
That works. But when I want to do that in a for loop(i):
cin>>arr[i][7];
and I don't know what the hell is compiler doing?
I just want that:
arr[0][7]=apple;
arr[1][7]=orange;
So please how can I do that?
#include <string>
#include <vector>
Since everybody is recommending it, I thought I'd sketch the options for you.
Note that you would have gotten this kind of answer in 10 milli-seconds by 3 different persons, if you had supplied a short, working sample code snippet (translating code 1:1 is more efficient than 'thinking up' examples that you might recognize)
Here you go:
std::vector<std::string> strings
strings.push_back("apple");
strings.push_back("banana");
// or
std::string s;
std::cin >> s; // a word
strings.push_back(s);
// or
std::getline(std::cin, s); // a whole line
strings.push_back(s);
// or:
// add #include <iterator>
// add #include <algorithm>
std::copy(std::istream_iterator<std::string>(std::cin),
std::istream_iterator<std::string>(),
std::back_inserter(strings));
Direct addressing is also possible:
std::vector<std::string> strings(10); // 10 empty strings
strings[7] = "seventh";
Edit in response to comments:
const char* eighth = "eighth";
if (strings[7] != eighth)
{
// not equal
}
// If you really **must** (read, no you don't) you can get a const char* for the string:
const char* sz = strings[7].c_str(); // warning:
// invalidated when `strings[7]` is modified/destructed
Unless you have a real reason (which you mustn't hide from us), make as Björn says and use a vector of strings. You can even do away with the initial request for the total size:
#include <string>
#include <vector>
#include <iostream>
std::vector<std::string> fruits;
std::string line;
while (std::getline(std::cin, line))
{
fruits.push_back(line);
}
Let's test:
std::cout << "You entered the following items:\n";
for (auto const & f : fruits) std::cout << "* " << f << "\n";
Because arr[i][7] is a char, and in fact one past the last element, which means you may get memory access error.
What you want to do maybe cin>>arr[i];.
How ever, this is not a very good idea, as you cannot control how many characters are read from input, which will easily cause memory overrun.
The easy way would be using std::vector<std::string> as others have suggested.
strcpy(&arr[0], "apple");
strcpy(&arr[1], "orange");
but for C++ is better to use std::vector<std::string> for array of strings
You have a two dimensional array of char
char arr[number][7];
And then trying to assign a string (char* or const char*) to them which will not work. What you can do here is assign a character, for example:
arr[0][1] = 'a';
If you can I would recommend using std::vector and std::string it would make things much clearer. In your case you could do
cin>>arr[i];
But I would not recommend it as you could only store up to 6 character char* strings (plus the null terminator). You can also have an array of char*
char* arr[number];
then dynamically allocate memory to store the strings.
Using std::vector and std::string will usually save you headaches once you understand them. Since you are brand new to C++, it might be useful to understand what is going on with two-dimensional arrays anyhow.
When you say
char array[N][M];
With N and M being constants, not variables, you are telling the compiler to allocate N*M items of type char. There will be a block of memory dedicated to that array of size N*M*sizeof(char). (You can declare an array of anything, not just char. Since sizeof(char) is 1, the memory will be N*M bytes long.) If you looked at raw memory, the first byte in the memory would be where array[0][0] is. The second byte would be where array[0][1] is, an so on, for M bytes. Then you would see array[1][0]. This is called row-major order.
As #jbat100 mentioned, when you say array[i][j] you are referring to a single character. When you say array[i], you are referring to the address of row i in the array. There is no pointer actually stored in memory, but when you say array[i] the compiler knows that you mean that you want the address of row i in the array:
char* row_i = array[i];
Now if i>0, then row_i points to somewhere in the middle of that block of memory dedicated to the array. This would do the same thing:
char* row_i = &array[i][0];
If you have a string, "orange" and you know that the length of it is less than M, you can store it in a given row in the array, like this:
strcpy(array[i], "orange"); // or
array[i][0] = 'o'; array[i][1] = 'a'; ... array[i][6] = 0;
Or you could have said row_i instead of array[i]. This copies 7 bytes into the array in the location of row_i. The strcpy() also copies an extra byte which is a 0, and this is the convention for terminating a character string in C and C++. So the 7 bytes are six bytes, 'o', 'r', 'a', 'n', 'g', and 'e', plus a 0 byte. Now strcmp(row_i, "orange") == 0.
Beware that if your string is longer than M, the strcpy and the simple char assignments will not (probably) produce a compile error, but you will end up copying part of your string into the next row.
Read about pointers and arrays in a good C/C++ book.

Passing 2-D array with dynamic size between functions in C/++

This is "popular" question so I already checked the similar threads but still didnt resolve my issue.
How can I declare 2-D array to hold "strings" - I need array of array of chars AFAIK - and use it as argument to 5 functions one after another where I can pass it by reference and update the content dynamically so following function can compare it. (I might get 10 "strings" or even empty array, so I want to do it correctly with dynamic array coz array content is different from system to system).
"string" => C style string aka array of chars. MAXLEN < 32;
C solution would be more disirable but if vectors can work, why not.
One possible solution in C is as follows:
char **p_strings = calloc(num_strings, sizeof(*p_strings));
for (i = 0; i < num_strings; i++)
{
// Allocate storage for the i-th string (always leave room for '\0')
p_strings[i] = calloc(len_string[i]+1, sizeof(*p_strings[i]));
}
...
// Call a function
my_function(p_strings, num_strings);
You will need to remember to free all this data when you're done with it.
If you need to alter the length of a string, or change the number of strings, you will have to do some fairly painful reallocation. So if you're working in C++, you should probably just be using a std::vector<std::string>.
std::vector<std::string> strings;
strings.push_back("Foo");
strings.push_back("Bar");
...
my_function(strings);
You can even get const pointers to C-style strings for each element, using c_str().
Assuming C++; for this I see no problem with using a vector to string (the string serves as the second dimension):
void foo(vector<string> v) {
cout << v[0]; // Assuming the elements exist!
}
int main(int argc, char *argv[])
{
vector<string> vString; // Make the vector
vString.push_back("something"); // Add a string
foo(vString); // Print out 'something'
}
In your edit you also described that the only thing that will change would be the actual string, so instead of push_backing your strings when they are needed, you can init the vector with the length:
vector<string> vString(10); // Assuming a size of 10
and then use them normally:
vString[4] = "something";
and (in response to the comment), to resize at runtime:
vString.resize(15); // Make it bigger, generates new blank strings