C++ char array concatenation - c++

I'm trying to overload the plus sign to concatenate two strings, but I keep getting an error.
VS 2010 gives an assertion failed message : "Expression: (L "Buffer is too small" && 0)" ; File: f:\dd\vctools\crt_bld\self_x86\crt\src\tcscat_s.inl ; Line: 42 .
What do you think is wrong with my code?
#include "stdafx.h"
class MyString{
int l; // the length of the array pointed by buf
char *buf; //pointer to a char array
public:
...
MyString(char *);
friend MyString operator+(MyString &,MyString &);
...
};
MyString::MyString(char *p)
{
buf=new char[strlen(p)+1];
strcpy_s(buf,strlen(p)+1,p);
l=strlen(p)+1;
}
MyString operator+(const MyString &a,const MyString &b)
{
MyString result("");
result.l=a.l+b.l;
delete[] result.buf;
result.buf=new char[result.l+1];
result.buf[0]='\0';
strcat_s(result.buf,result.l+1,a.buf);
strcat_s(result.buf,result.l+1,b.buf);
return result;
}
int _tmain(int argc, _TCHAR* argv[])
{
MyString a("hello"),b("world"),c("");
c=a+b;
system("pause");
return 0;
}
It work now! Thank you everyone!

strcat_s(result->buf,strlen(a.buf),a.buf);
strcat_s(result->buf,strlen(b.buf),b.buf);
The second parameter of strcat_s is the size of the destination buffer, not the size of the string that shall be appended.
So you need to change that to
strcat_s(result->buf,result->l+1,a.buf);
strcat_s(result->buf,result->l+1,b.buf);
The rest of the operator + implementation is broken as well, as was already noted by others. Newing up an Instance and then returning it by value is nonsense. Just instantiate the result on the stack and return by value.

In operator+ the variable "MyString result" was declared on the stack and it was subsequently returned by reference, which was bad.
Then the OP was edited. The variable "result" was no longer declared on the stack, but instead allocated on the heap. However, then there was a memory leak.
The right thing to do here is to return by value and also declare "MyString result" on the stack. Also make sure you have a copy constructor. And a destructor for that matter.
You should also make your constructor takes a "const char*".

It should be result.buf=new char[result.l+1]; to allow for the null character.

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.

Modifying pointer to string literal in separate function

I have what is hopefully a trivial question that someone can explain to me in simpler terms than what I have already come across. While working through
A Tour of C++ (Second Edition)
I've been trying a few examples.
I'm currently trying to modify a pointer to a string literal in a separate function (I thought it would be easy.....).
using namespace std;
void test(char *ptr)
{
ptr = "test";
}
int main()
{
char *p = "abc";
test(p);
cout << p << "\n";
return 0;
}
When using g++ to compile, I get a
Warning: ISO C++ forbids converting a string constant to char*
Apparently g++ is auto-converting *p to a const? Surely I'm missing something basic, but my previous SO and google searches have gotten me no closer to the answer. Thank you for your responses!
EDIT:
Both great examples below. Thank you everyone for your responses, very helpful!
Apparently g++ is auto-converting *p to a const?
Quite the opposite. The string "abc" will be in your binary, and that is supposed to be readonly for your program. Therefore, that string should only be read, and the value you get when assigning the string literal in this situation is of type const char*. You get the error because you're assigning it to a non-const char*. Try this instead:
const char *p = "abc";
Also, you'll have to change the function, too:
void test(const char *ptr)
{
ptr = "test";
}
It's still going to print abc, however. That's because you're only modifying a copy of the value that you're passing. But C++ lets you pass a reference instead, which you can do like this:
void test(const char *&ptr)
{
ptr = "test";
}
Now that's a reference to a pointer pointing to a const char... whew! Both the "abc" and "test" will be in the program's binary when it is compiled. When the program is run, the address of "abc" is assigned to char *p, and then the function to change it to have the address of "test" instead is called. The & tells it to work with the actual char *p and not just a copy of it that gets lost when the function finishes.
There are two things that can be const; the pointer (char * const), or the object (const char *).
The string literal is const, that's what the compiler is complaining about. You should use
const char *p = "abc";
The function would still not modify the pointer p from main() though, because it is passed by value to the function.
This should modify the pointer:
using namespace std;
const char * str2 = "test";
void test(const char *& ptr)
{
ptr = str2;
}
int main()
{
const char *p = "abc";
test(p);
cout << p << "\n";
return 0;
}
live demo

Returning Character Array via Assignment Operator

So this is a homework assignment, there might be come constraints that are ridiculous but please bear with me. This is just a simple function but drawn out. I need to return a character array via assignment operator but it doesn't seem to be working at all. I've tried pointers, but no luck.
#include <iostream>
using namespace std;
char* findMax(char*, char*);
int main()
{
char aArray[50] = "Hello World",
bArray[50] = "dlroW olleH",
maxArray[50];
maxArray[50] = findMax(aArray, bArray);
cout << maxArray << " is the bigger of the 2 strings" << endl;
return 0;
}
char* findMax(char* strA, char* strB){
char* maxStr;
if(strcmp(strA, strB) < 1)
maxStr = strB;
else
maxStr = strA;
return maxStr;
}
if I cout the return value of findMax() it does print out the value of bArray, but geting it into maxArray via assignment operator isn't working at all.
There are two ways to do this. As written, maxArray is an array of characters. Arrays can't be directly assigned to. Instead you need to copy each character one by one. You could do that with a loop, or by calling the strcpy standard library function.
char maxArray[50];
strcpy(findMax(aArray, bArray), maxArray);
The other way is to change the declaration of maxArray to be a char *, a pointer. Pointers can be assigned to directly without having to loop or invoke a function.
char *maxArray;
maxArray = findMax(aArray, bArray);
The difference between this and the first solution is subtle, but important. With char maxArray[50] you are actually allocating a third array of 50 characters, separate from aArray and bArray. This array has its own storage, its own 50 bytes of memory.
In the second you don't create a third array. You merely create a pointer which can point to other arrays already in existence. After the assignment, maxArray becomes an indirect reference to either aArray or bArray. It's not a copy of one of those arrays, it points to one of them.

Return string array from function in C++

I am having problem handling string array in C++.I tried below two methods.Still
not able to resolve the problem...Here it goes :
When I use :
string* fun(string* a,string*b)
{
string c[];
return c; /* c is string array type*/
}
it returns first string stored in string array c.I want whole string array to be returned.
When I used:
vector<string> fun(vector<string> a,vector<string> b){
vector<string> c;
return c;
}
still,i got some errors.
can you help me know where is the problem in both cases.
What modifications are required to obtain the desired result..
How can I handle string array in C++.
Thanx in advance !!
In the first version, you are returning a pointer to a local variable (your array), which will not exists any longer when you leave the scope. You need to create your array on the heap, e.g. with malloc or new. If you allocate it manually, don't forget to deallocate it.
In the second version, you are returning a copy of the vector declared in your function (if you modify the strings in the returned vector, they'll not be modified in a et b). You are creating an empty vector and not adding anything in it, so it'll not contains any string, though.
If you've got to return an object more complex than string you should pass to your function a link to vector (in your code) and fill it with values. That's a fast and right method.
vector<string> fun(vector<string> a,vector<string> b, vector<string>& result){
res.push_back("one");
res.push_back("two");
res.push_back("three");
}
In C/C++ while returning a string from function a local buffer if returned will not work.
The returned pointer should be a static buffer(like static string c[]) or pointer to a buffer passed in by the caller function (like string *fun(string *a, string *b, string *c) ) or pointer to a memory obtained using malloc/new but not local array.
In your first snippet, you could try initializing with new to allocate the space yourself.
string* func() {
string* c = new string[3];
c[0] = "Hello";
c[1] = "World";
c[2] = "<3";
return c;
}
in main:
m = func();
for(int i = 0; i < 3; i++)
{
cout << m[i] << endl;
}
That should prevent it from losing scope once the function ends. Don't forget to deallocate the space. Note you are returning a pointer to an array, not an array. Also, I had no problem running your second snippet of code. Please always share the errors you are getting.
You can't return arrays directly in C++(it inherits this from C). The way around it is to stuff it into a struct.
struct returned_array
{
string result[100];
};
returned_array fun()
{
returned_array result;
result.result[0] = "whatever";
return result;
}
I just picked an arbitrary size 100 for the example. If you want to base the return size on the size of an array that gets passed in, you can add a template parameter:
template<int N>
struct returned_array
{
string result[N];
};
template<int N>
returned_array<N> fun(string (&)[N])
{
returned_array<N> result;
result.result[0] = "whatever";
return result;
}

deprecated conversion from string constant to 'char*' [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
C++ deprecated conversion from string constant to 'char*'
I want to pass a string via char* to a function.
char *Type = new char[10];
Type = "Access"; // ERROR
However I get this error:
error: deprecated conversion from string constant to 'char*'
How can I fix that?
If you really want to modify Type:
char *Type = new char[10];
strcpy( Type, "Access" );
If you don't want to modify access:
const char *Type = "Access";
Please note, that, however, arrays of char in C and in C++ come with a lot of problems. For example, you don't really know if the call to new has been successful, or whether it is going to throw an exception. Also, strcpy() could surpass the limit of 10 chars.
So you can consider, if you want to modify type later:
std::string Type = "Access";
And if you don't want to modify it:
const std::string Type = "Access";
... the benefit of using std::string is that it is able to cope with all these issues.
There are a couple of things going on here.
char *Type = new char[10];
This create a char* pointer named Type and initializes it to point to the first element of a newly allocated 10-element array.
Type = "Access"; // ERROR
This assignment doesn't do what you think it does. It doesn't copy the 6-character string "Access" (7 characters including the terminating '\0') to the array you just created. Instead, it assigns a pointer to the first element of that array into your pointer Type. There are two problems with that.
First, it clobbers the previous value of Type. That 10-character array you just allocated now has nothing pointing to it; you can no longer access it or even deallocate it. This is a memory leak.
This isn't what the compiler is complaining about.
Second, a string literal creates a statically allocated const array ("statically allocated" meaning it exists for the entire execution of your program). Type is not declared with a const qualifier. If the compiler allowed you to point Type to the string "Access", you could use that pointer to (attempt to) modify it:
Type = "Access";
Type[0] = 'a'; // try to change the string to "access"
The purpose of const is to prevent you from modifying, or even attempting to modify, things that are read-only. That's why you're not allowed to assign a non-const pointer value to a const pointer object.
Since you're programming in C++, you're probably better off using std::string.
I want to pass a string via char* to a function.
Here is how you can pass a string via char* to a function (note the required const keyword in the function signature.)
#include <iostream>
void f(const char* p) {
std::cout << p << "\n";
}
int main() {
f("Access");
}
But, what if you are invoking an existing function, and cannot modify its signature?
If you have some external guarantee that the function will not write through its argument pointer,
#include <iostream>
void f(char* p) {
std::cout << p << "\n";
}
int main() {
f(const_cast<char*>("Access"));
}
If, on the other hand, the function might write to the string, then you'll need to allocate space for the string:
#include <iostream>
void f(char* p) {
*++p;
std::cout << p << "\n";
}
int main() {
// Allocate read-write space on the heap
char *p = new char[strlen("Access"+1)];
// Copy string to allocated space
strcpy(p, "Access");
f(p);
delete p;
}
or,
#include <iostream>
void f(char* p) {
*++p;
std::cout << p << "\n";
}
int main() {
// Allocate read-write space on the stack
char arr[] = "Access";
f(arr);
}
But, the best course by far is to avoid the whole pointer mishegas:
#include <iostream>
void f(const std::string& p) {
std::cout << p << "\n";
}
int main() {
f("Access");
}
You've got a basic operations problem here, not a coding issue.
When you want to change the contents of a C char array, you do not use the assignment operator. That will instead change the value of the underlying pointer. Ick.
Instead you are supposed to use the C string library routines. For instance, strcpy (Type, "Access"); will copy the string literal "Access" into your character array, with its all-important trailing nul character.
If you are using C++ (as your tags indicate), you should probably be using std::string instead of arrays of char. Assignment works they way you are expecting there.