pass array of strings to a function which takes const char** - c++

I have a function which takes an array of const char** as a parameter
void Foo(const char** bar);
I can pass an array of const char * to it
const char *bar[2];
bar[0] = "test";
bar[1] = "me";
Foo(bar); // works fine
I want to do the same when 'bar' is std::string array instead of const char *
std::string bar[2];
bar[0] = "test";
bar[1] = "me";
Foo(bar); // cannot convert argument 1 from 'std::string [1]' to 'const char **'
I know the way convert std::string to const char *. Is there any way I can do it in the above case

Not sure what you're trying to achieve exactly but here
// Example program
#include <iostream>
#include <string>
void Foo(const char** bar, int num) {
while(num > 0) {
std::cout << bar[--num] << std::endl << std::flush;
}
}
const char** toCharArray(std::string* arr, int num) {
// If we ever alloc with new with have to delete
const char** buffer = new const char*[num];
for(int i = 0; i < num; i++) {
buffer[i] = arr[i].c_str();
}
return buffer;
}
int main()
{
std::string bar[2];
bar[0] = "test";
bar[1] = "me";
// Capture the result
const char** charBar = toCharArray(bar, 2);
Foo(charBar, 2);
// So we can free it later
delete[] charBar;
}
Arrays in memory do not have a length or size member like other languages so we pass in the size via function arguments. Second since we want to pass in an array of strings and get an array of chars out of it, we'll need to construct another array dynamically. Only way to do this is to use new. This stores the chars on the heap instead of the stack so when the function toCharArray finishes, the data will live on. So we store the result in charBar so that we can delete[] the array later.

you cannot do it directly
auto arr = std::vector<const char*>();
auto s1 = std::string("test");
auto s2 = std::string("me");
arr.push_back(s1.c_str());
arr.push_back(s2.c_str());
Foo(arr.data());

Related

Dynamic array of fixed length strings

How do I create a dynamic array of fixed length strings?
I created class AString which has pointers to struct _str which has fixed-length array data.
How to assign values, and what is wrong?
#include "stdafx.h"
#include <iostream>
struct _str {
char data[20];
};
class AString {
public:
AString();
~AString();
void Add(_str string);
private:
_str *str;
int count;
};
AString::AString() {
std::cout << "Constructor" << std::endl;
str = nullptr;
count = 0;
}
AString::~AString() {
std::cout << "Destructor" << std::endl;
if (str != nullptr) delete[] str;
}
void AString::Add(_str string) {
_str *str2 = new _str[count+1];
for (int i=0;i<count;i++) {
str2[i] = str[i];
}
delete[] str;
str = str2;
count++;
str[count-1] = string;
}
int _tmain(int argc, _TCHAR* argv[])
{
AString astring;
_str str1;
str1.data="0123456789012345678"; // cannot convert from 'const char[20]' to 'char[20]'
astring.Add(str1);
std::cin.get();
return 0;
}
str1.data="0123456789012345678";: cannot convert from 'const char[20]' to 'char[20]'
Want to:
not use _str str1;, and use char str1[20];
As for me, I used this:
strcpy(str1.data, "0123456789012345678");
Here is the main:
int main(int argc, char* argv[])
{
AString astring;
_str str1;
//str1.data=const_cast<char>("0123456789012345678"); // cannot convert from 'const char[20]' to 'char[20]'
strcpy(str1.data, "0123456789012345678");
astring.Add(str1);
std::cout << str1.data;
std::cin.get();
return 0;
}
The result is as follows:
Constructor
0123456789012345678
First of all, I would recomend you yo use std::string or std::array. But if you forced to use char[], I would recomend you to use strncpy instead of operator =, so it would looks like this
strncpy(str1.data,"0123456789012345678", 20); // cannot convert from 'const char[20]' to 'char[20]'
To copy char* or block of memory, it is better to use memcpy!
void* memcpy (void* destination, const void* source, size_t length);
It copies the values of length bytes starting the location pointed to by source directly to the memory block pointed to by destination. Note that, the underlying type of the objects pointed to by both the source and destination pointers dose not matter.
You can also use strncpy.
char* strncpy(char* destination, const char* source, size_t length);
It works properly for your code but if there is/are some 0 valued bytes, the strncpy consider it as null-termination and coping continued with '0' i.e. (pad) until length is satisfied.
try
memcpy(str1.data, "0123456789012345678", 20);
You can't assign to arrays - that's just the way it is.
You could use strncpy in main, or get an assignable array with std::array<char, 20>, but if there was a need to do this by hand, I would add constructors (and hide the implementation details) in order to keep things safe:
class _str {
public:
typedef char base[20];
_str() { data[0] = 0; }
_str(const base& in) { strncpy(data, in, 20); }
const base& get() const { return data; }
private:
base data;
};
and then you can
AString astring;
_str str1 = "01234567890123456789"; // Fine
astring.Add(str1);
std::cout << str1.get();
_str str2 = "012345678901234567890"; // Compilation error
_str str3 = "0"; // Compilation error

Why does my char* copier return different things?

Writing a simple string copier and testing it in the main() fucntion. What's odd is that sometimes the program returns
"HelloHello"
like it should, but maybe every third time I run it, the program prints out:
"Hello!Hello!▌▌▌▌▌▌▌▌▌▌▒"UòB╚"
Why is the tail of garbage data only sometimes being added to the end of my second string?
#include <iostream>
using namespace std;
int strlength(const char* c)
{
int size = 0;
while (*c) {
++c;
++size;
}
return size;
}
char* mystrdup(const char* c)
{
int size = strlength(c);
char* result = new char;
copy(c, c + size, result);
return result;
}
void print_array(const char* c)
{
int size = strlength(c);
while (*c) {
cout << *c;
++c;
}
}
int main()
{
char test[] = "Hello!";
char* res = mystrdup(test);
print_array(test);
print_array(res);
}
The program has undefined behavior because you are allocating not enough memory for the result string.
char* mystrdup(const char* c)
{
int size = strlength(c);
char* result = new char;
^^^^^^^^^^^^^^^^^^^^^^^
copy(c, c + size, result);
return result;
}
Moreover you are not copying the terminating zero to the result string.
At least the two functions strlength and mystrdup can look the following way
size_t strlength( const char *s )
{
size_t size = 0;
while ( s[size] ) ++size;
return size;
}
char * mystrdup( const char *s )
{
size_t size = strlength( s ) + 1;
char *result = new char[size];
copy( s, s + size, result );
return result;
}
Of course instead of the standard algorithm std::copy you could use the standard C function strcpy declared in the header <cstring>.
strcpy( result, s );
And do not forget to delete the allocated array.
char* res = mystrdup(test);
//…
delete [] res;
Pay attention to that the function print_array does not use the variable size. There is no need to output a C-string character by character.
The function could be defined like
std::ostream & print_array( const char *s, std::ostream &os = std::cout )
{
return os << s;
}
And at last the identifier c is usually used with single objects of the type char. If you deal with a string then it is better to use the identifier s.
You have multiple bugs in your code. You allocate wrong memory (char instead of char array). You don't delete the memory. Stop using C-string and use std::string
#include <iostream>
#include <string>
using std::cout;
void print_array(const char* c)
{
while (*c) {
cout << *c;
++c;
}
}
int main()
{
std::string = "Hello!";
std::string res = test;
print_array(test.c_str());
print_array(res.c_str());
}
In strcpy you need to create a char size.
char* mystrdup(const char* c)
{
int size = strlength(c);
char* result = new char[size];
copy(c, c + size, result);
return result;
}

Passing pointer to function as an output parameter

I am using 'pBuff' as pointer and put a char array in function 'myfunc'.
So, In main function, I should receive it in aBuff.
But it is not working.. what is wrong here ??
#include <stdio.h>
void myfunc(void *pBuff, int &i);
int main()
{
int len;
char aBuff[2]={0};
printf("Hello World");
myfunc(aBuff,len);
printf("aBuff %s", aBuff);
return 0;
}
myfunc(void *pBuff, int &i){
char a[2] = {'a', 'b'};
i = 5;
pBuff = &a;
}
char a[] should come as output parameter in main function
You are passing a pointer to your function, inside your function you assign the address of the temporary variable a to the pBuff variable. The original aBuff variable is unaffected.
As we are using c++ a much simpler solution using strings would be:
#include <iostream>
void myfunc(std::string& pBuff, int &i);
int main()
{
int len;
std::string aBuff;
std::cout << "Hello World";
myfunc(aBuff,len);
std::cout << "aBuff " << aBuff;
return 0;
}
void myfunc(std::string& pBuff, int &i){
pBuff = "ab";
i = 5;
}
If you really must use raw character arrays then this would be one way to implement it (note the removal of the void*, no reason to use it here):
myfunc(char *pBuff, int &i){
strcpy(pBuff, "ab");
i = 5;
}
If you need to store 2 characters in your aBuff array it needs to be 3 characters long not 2 as you need to leave space for the null terminator otherwise printf and other string functions will not work correctly.
If you want to return an array you have several possible solutions:
Pass a pointer (type*) to the function and copy the data unsing e.g. memcpy. You should also pass the size of the destination array and make sure you don't copy too much data.
Pass a reference to the pointer (type *&) and assign to the pointer in the function
Pass the address of the pointer (type**) and assign the address to the dereferenced pointer.
You must not return the address of a local variable with automatic storage. If you want to return a (which is a pointer to the first element) from the function it must be either static or dynamically allocated using e.g. malloc.
Which solution to choose depends on your needs:
Examples for 3 variants:
#include <stdio.h>
#include <memory.h>
void myfunc(char **pBuff, int &i);
void myfunc2(char *&pBuff, int &i);
void myfunc1(char *pBuff, size_t bufsize, int &i);
int main()
{
int len;
char aBuff[3]={0}; // increased size for NUL termination
char *ptr = aBuff;
printf("Hello World");
myfunc(&ptr,len);
printf("ptr %s", ptr);
printf("\n");
myfunc2(ptr,len);
printf("ptr %s", ptr);
printf("\n");
myfunc1(aBuff, sizeof aBuff,len);
printf("aBuff %s", aBuff);
return 0;
}
void myfunc1(char *pBuff, size_t bufsize, int &i){
char a[3] = {'c', 'd', '\0'}; /* added '\0' to allow using it as a string */
i = 5;
// actually the minimum of bufsize and sizeof(a) should be used
memcpy(pBuff, a, bufsize);
}
void myfunc(char **pBuff, int &i){
static char a[3] = {'a', 'b', '\0'}; // added '\0' to allow using it as a string
i = 5;
*pBuff = a;
}
void myfunc2(char *&pBuff, int &i){
static char a[] = "xyz";
i = 5;
pBuff = a;
}

Is there a way to avoid array-to-pointer decay?

The parameter to main char* argv[] decays to char**, which is unfortunate, because it cannot be used with std::begin which only accepts arrays. The only workaround I see is to use a variable length array which is undesirable.
#include <iostream>
int main(int argc, char* argv[])
{
char* _argv[argc];
for (int i = 0; i < argc; ++i)
_argv[i] = argv[i];
for (arg : _argv)
{
std::cout << arg << " ";
}
return 0;
}
Desirably I want something like: char* _argv[] = { ... };
That is not possible, since the signature of main is fixed. You can copy the elements to a vector using:
std::vector<std::string> args {argv, argv + argc};
Then you can use std::begin on args
You can define a simple class wrapper to give you begin() and end().
struct Args {
int argc_;
char **argv_;
Args (int argc, char *argv[]) : argc_(argc), argv_(argv) {}
char ** begin () { return argv_; }
char ** end () { return argv_ + argc_; }
};
int main(int argc, char *argv[]) {
for (auto s : Args(argc, argv)) {
std::cout << s << '\n';
}
return 0;
}
The only workaround I see is to use a variable length array which is
undesirable.
The only workaround for what exactly? There is no problem. If you actually need an array of strings, use a vector as shown in TNAs answer; I doubt you need to modify the parameters though.
If you just need to iterate through it, there is no need to copy all the pointers.
for (auto str : boost::make_iterator_range(argv, argv + argc))
std::cout << str;
Or
for (auto ptr = argv; ptr != argv + argc; ++ptr)
std::cout << *ptr << '\n';
The parameter to main char* argv[] decays to char**, which is
unfortunate, because it cannot be used with std::begin which only
accepts arrays.
There is no other way. The amount of command line parameters is not known at compile time, and this method of passing them (instead of using two pointers-to-pointers) is historically motivated.

Getting the Content of String into a Char Array

#include <stdio.h>
#include <string.h>
int main()
{
std::string data;
data = "hello world";
char string1[] = data;
}
If I must use char string1[] and not char *string1, is there a way I can copy content of string data into this char string1[]?
file.cpp: In function ‘int main()’:
file.cpp:13:22: error: initializer fails to determine size of ‘string1’
You can call method c_str on std::string object and copy result to your array.
Since the size of data may be variable, it cannot be copied via initialization of a static array.
One way you can do it is with dynamic allocation (which C++ string handles automatically):
char *string1 = strdup(data.c_str());
// do stuff with string1
free(string1);
If you are familiar with loops, then the easiest way is to copy the contents of string to char array one by one. As you are not going to use char pointer so you have to fix the size of char array, say 20.
int main()
{
string data;
data = "hello world";
int size = data.size(); // size of data
char string1[20]; //you can change the size of char array but it must be greater than size of data string
for (int i = 0; i < size; i++) {
string1[i] = data[i];
}
return 0;
}
Using stack memory (only if you are sure that the data.size() is less than 1000.):
char result[1000];
strcpy(result, data.c_str());
Or using heap memory:
char* result = new char[data.size() + 1];
strcpy(result, data.c_str());
// ...
delete[] result;