Today I gave a test there was the following question written and because I am new to C++, I became confused to the following question.
Why doesn't the following statement work?
char str[ ] = "Hello" ;
strcat ( str, '!' ) ;
char str[] = "Hello";
strcat (str, '!') ;
strcat second argument must be a pointer to a string, but you are passing a character constant.
The correct call would be strcat(str, "!"); (note the " instead of the ') but you also need to reserve enough space in str which is only large enough to hold the "Hello" string. For example, for your test, you can reserve more bytes with char str[64] = "Hello";
strcat() calls for pointer for both arguments.
'!' will converted to an (invalid for many chance) pointer by implementation-defined manner, then the program may crash for Segmentation Fault.
Note that
char str[ ] = "Hello" ;
strcat ( str, "!" ) ;
won't work well either due to lack of buffer.
char str[ ] = "Hello" ;
strcat ( str, '!' ) ;
^^^ --- this is of type char
strcat signature is :
char * strcat ( char * destination, const char * source );
^^^^^^^^^^^^^^^^^^^
so, second parameter is of type const char* and not char. You must pass either string literal or variable of type const char*. Actually string literals are of type const char[] but they decay to const char* when being assigned to.
strcat function expects two strings.'!' is a character.
in order to concatenate safely,your array must be big enough to hold the other string,so change '!' to "!",and str[] to str[8] or more.
int main(void)
{
char str[20] = "Hello" ;
strcat ( str, "!" ) ;
printf("%s\n",str);
}
Two problems:
The immediate issue is that the second parameter must be a const char*. strcat reads the memory starting at that location, until the \0 is reached. If no \0 can be reached then the function behaviour is undefined.
It's up to you to make sure that str is large enough to receive the concatenated string. If not, then the behaviour is undefined.
On the second point, you can write something like char str[100] = "Hello"; That will reserve 100 bytes of memory, populating the first 6 elements with Hello\0.
Why did you decide that these statements
char str[ ] = "Hello" ;
strcat ( str, '!' ) ;
do not work?:)
All depends on how the function strcat is defined.
For example it can be defined the following way
char * strcat( char *s, char c )
{
size_t n = std::strlen( s );
if ( n ) s[n-1] = c;
return s;
}
Or the following way
char * strcat( char *s, char c )
{
size_t n = std::strlen( s );
char *t = new char[n + 2];
std::strcpy( t, s );
t[ n - 2 ] = c;
t[ n - 1 ] = '\0';
return t;
}
If strcat in your example is the standard C function std::strcat then again it does not mean that the statements will not work. It means that the program with such statements will not compile because the second argument has a wrong type.
But if you specify a correct value for the second argument like this
strcat ( str, "!" ) ;
that is using a string literal instead of the character literal then indeed the statements will not work as you are expecting because array str does not have enough space to append string literal "!".
The array should be defined at least like
char str[7] = "Hello" ;
^^^
and the function should be called like
strcat ( str, "!" ) ;
^^^
Related
I am trying to copy a char* and char symbol into a new char* , however the desired result after copying is wrong.
char* name = "someData";
char symbol ='!';
int size1 = strlen(name);
int size2 = 1;
int newSize = size1 + size2 + 1;
char* res = new char[newSize];
strcpy(res,name);
const char* symbolPointer = &symbol;
strcat(res, symbolPointer);
cout<<*res;
I expect the result to be "someData!" , however it is only "s" , where is my mistake?
char* name = "someData";
This is an ill-formed conversion in C++ (since C++11). I recommend to not point to string literals with pointer to non-const.
const char* symbolPointer = &symbol;
strcat(res, symbolPointer);
Both arguments of std::strcat must be null terminated. symbolPointer is not a pointer to a null terminated string. Because the pre-condition of std::strcat is violated, the behaviour of the program is undefined.
cout<<*res;
res is a pointer to the first character of the string. By indirecting through the pointer to first character, you get the first character. That is why you see the first character (in case the undefined behaviour hasn't caused the program to do something completely different).
Lastly, the program leaks the allocated res.
Here is a fixed example:
std::string name = "someData";
name += '!';
std::cout << name;
The problem is the following:
cout<<*res;
This is equivalent to:
cout << res[0];
It prints just the first character of the output. Use
cout<<res;
Try it this way:
const string name = "someData";
const char symbol ='!';
string res = name + symbol;
cout << res;
You should avoid the legacy C nul-terminated string handling functions. You should avoid using new directly in your code.
The observed result you are asking about is due to you writing *res (a single character) instead of res (a pointer to the first character) in the output statement. But the code was buggy besides that, as strcat will copy until it finds the terminator, so it will overwrite some unknown amount of memory beyond what you allocated.
symbolPointer should end with null character, because strcat requires 0-terminated string.
So if you want to continue on your way,
(not a good idea but)
You can add this before strcat.
*(symbolPointer+1) = 0;
#include <iostream>
#include <cstring>
int main() {
char name[] = "someData";
char symbol ='!';
int size1 = strlen(name);
int size2 = 1;
int newSize = size1 + size2 + 1;
char* res = new char[newSize];
strcpy(res,name);
char* symbolPointer = &symbol;
*(symbolPointer + 1) = 0;
strcat(res, symbolPointer);
cout<<res;
return 0;
}
I've been reading a book for self study (http://www.amazon.com/gp/product/0321992784) and I'm on chapter 17 doing the exercises. One of them I solved, but I'm not satisfied and would like some help. Thank you in advanced.
The Exercise: Write a program that reads characters from cin into an array that you allocate on the free store. Read indvidual characters until an exclamation mark(!) is entered. Do not use std::string. Do not worry about memory exhaustion.
What I did:
char* append(const char* str, char ch); // Add a character to the string and return a duplicate
char* loadCstr(); // Read characters from cin into an array of characters
int main()
{
char* str{ loadCstr() };
std::cout << str << '\n';
return 0;
}
I made 2 functions, 1 to create a new string with a size 1 larger than the old and add a character at the end.
char* append(const char* str, char ch)
/*
Create a new string with a size 1 greater than the old
insert old string into new
add character into new string
*/
{
char* newstr{ nullptr };
int i{ 0 };
if (str)
newstr = new char [ sizeof(str) + 2 ];
else
newstr = new char [ 2 ];
if(str)
while (str [ i ] != '\0')
newstr [ i ] = str [ i++ ]; // Put character into new string, then increment the index
newstr [ i++ ] = ch; // Add character and increment the index
newstr [ i ] = '\0'; // Trailing 0
return newstr;
}
This is the function for the exercise using the append function I created, It works, but from what I understand each time I call append, there is a memory leak because I create a new character array and didn't delete the old.
char* loadCstr()
/*
get a character from cin, append it to str until !
*/
{
char* str{ nullptr };
for (char ch; std::cin >> ch && ch != '!';)
str = append(str, ch);
return str;
}
I tried adding another pointer to hold the old array and delete it after making a new one, but after about 6 calls in this loop I get a runtime error that I think tells me I'm deleting something I shouldn't? which is where I got confused.
This is the old one that doesn't work beyond 6 characters:
char* loadCstr()
/*
get a character from cin, append it to str until !
*/
{
char* str{ nullptr };
for (char ch; std::cin >> ch && ch != '!';) {
char* temp{ append(str, ch) };
if (str)
delete str;
str = temp;
}
return str;
}
So I want to know how I can fix this function so there are no memory leaks. Thank you again. (Also please note, I do know these functions already exist and using std::string handles all the free store stuff for me, I just want to understand it and this is a learning exercise.)
You have to use standard C function std::strlen instead of the sizeof operator because in case of your function the sizeof operator returns the size of pointer instead of the length of the string.
Also you need to delete already allocated array.
The function can look the following way
char* append(const char* str, char ch)
/*
Create a new string with a size 1 greater than the old
insert old string into new
add character into new string
*/
{
size_t n = 0;
if ( str ) n = std::strlen( str );
char *newstr = new char[ n + 2 ];
for ( size_t i = 0; i < n; i++ ) newstr[i] = str[i];
delete [] str;
newstr[n] = ch;
newstr[n+1] = '\0';
return newstr;
}
And in the function loadCstr it can be called like
str = append( str, ch );
Also instead of the loop to copy the string you could use standard algorithm std::copy
Is the point to learn about memory management, or about how string operations work internally?
For the second (learning about string operations), you should use std::unique_ptr<char[]> which will automatically free the attached array when the pointer dies. You'll still need to calculate string length, copy between strings, append -- all the things you are doing now. But std::unique_ptr<char[]> will handle the deallocation.
For the first case, you're better off writing an RAII class (custom version of std::unique_ptr<T>) and learning how to free memory in a destructor, than scattering delete [] statements all over your code. Writing delete [] everywhere is actually a bad habit, learning it will move your ability to program C++ backwards.
I've tried so may ways on the Internet to append a character to a char* but none of them seems to work. Here is one of my incomplete solution:
char* appendCharToCharArray(char * array, char a)
{
char* ret = "";
if (array!="")
{
char * ret = new char[strlen(array) + 1 + 1]; // + 1 char + 1 for null;
strcpy(ret,array);
}
else
{
ret = new char[2];
strcpy(ret,array);
}
ret[strlen(array)] = a; // (1)
ret[strlen(array)+1] = '\0';
return ret;
}
This only works when the passed array is "" (blank inside). Otherwise it doesn't help (and got an error at (1)). Could you guys please help me with this ? Thanks so much in advanced !
Remove those char * ret declarations inside if blocks which hide outer ret. Therefor you have memory leak and on the other hand un-allocated memory for ret.
To compare a c-style string you should use strcmp(array,"") not array!="". Your final code should looks like below:
char* appendCharToCharArray(char* array, char a)
{
size_t len = strlen(array);
char* ret = new char[len+2];
strcpy(ret, array);
ret[len] = a;
ret[len+1] = '\0';
return ret;
}
Note that, you must handle the allocated memory of returned ret somewhere by delete[] it.
Why you don't use std::string? it has .append method to append a character at the end of a string:
std::string str;
str.append('x');
// or
str += x;
The function name does not reflect the semantic of the function. In fact you do not append a character. You create a new character array that contains the original array plus the given character. So if you indeed need a function that appends a character to a character array I would write it the following way
bool AppendCharToCharArray( char *array, size_t n, char c )
{
size_t sz = std::strlen( array );
if ( sz + 1 < n )
{
array[sz] = c;
array[sz + 1] = '\0';
}
return ( sz + 1 < n );
}
If you need a function that will contain a copy of the original array plus the given character then it could look the following way
char * CharArrayPlusChar( const char *array, char c )
{
size_t sz = std::strlen( array );
char *s = new char[sz + 2];
std::strcpy( s, array );
s[sz] = c;
s[sz + 1] = '\0';
return ( s );
}
The specific problem is that you're declaring a new variable instead of assigning to an existing one:
char * ret = new char[strlen(array) + 1 + 1];
^^^^^^ Remove this
and trying to compare string values by comparing pointers:
if (array!="") // Wrong - compares pointer with address of string literal
if (array[0] == 0) // Better - checks for empty string
although there's no need to make that comparison at all; the first branch will do the right thing whether or not the string is empty.
The more general problem is that you're messing around with nasty, error-prone C-style string manipulation in C++. Use std::string and it will manage all the memory allocation for you:
std::string appendCharToString(std::string const & s, char a) {
return s + a;
}
char ch = 't';
char chArray[2];
sprintf(chArray, "%c", ch);
char chOutput[10]="tes";
strcat(chOutput, chArray);
cout<<chOutput;
OUTPUT:
test
I know the starting address of the string(e.g., char* buf) and the max length int l; of the string(i.e., total number of characters is less than or equal to l).
What is the simplest way to get the value of the string from the specified memory segment? In other words, how to implement string retrieveString(char* buf, int l);.
EDIT: The memory is reserved for writing and reading string of variable length. In other words, int l;indicates the size of the memory and not the length of the string.
std::string str(buffer, buffer + length);
Or, if the string already exists:
str.assign(buffer, buffer + length);
Edit: I'm still not completely sure I understand the question. But if it's something like what JoshG is suggesting, that you want up to length characters, or until a null terminator, whichever comes first, then you can use this:
std::string str(buffer, std::find(buffer, buffer + length, '\0'));
char *charPtr = "test string";
cout << charPtr << endl;
string str = charPtr;
cout << str << endl;
Use the string's constructor
basic_string(const charT* s,size_type n, const Allocator& a = Allocator());
EDIT:
OK, then if the C string length is not given explicitly, use the ctor:
basic_string(const charT* s, const Allocator& a = Allocator());
There seems to be a few details left out of your explanation, but I will do my best...
If these are NUL-terminated strings or the memory is pre-zeroed, you can just iterate down the length of the memory segment until you hit a NUL (0) character or the maximum length (whichever comes first). Use the string constructor, passing the buffer and the size determined in the previous step.
string retrieveString( char* buf, int max ) {
size_t len = 0;
while( (len < max) && (buf[ len ] != '\0') ) {
len++;
}
return string( buf, len );
}
If the above is not the case, I'm not sure how you determine where a string ends.
std::string str;
char* const s = "test";
str.assign(s);
string& assign (const char* s); => signature FYR
Reference/s here.
Let,
char* rw="hii"; //This string is readable and writeable
const char* r="hello"; // This string is only readable
we can convert char* or const char* to string with the help of string's constructor.
string string_name(parameter);
This parameter accepts both char* and const char* types .
Examples:
1) string st(rw);
Now string 'st', contains "hii"
2) string st(r);
Now, string 'st' contains "hello".
In both the examples, string 'st' is writable and readable.
This question already has answers here:
convert a char* to std::string
(13 answers)
Closed 6 years ago.
What is the correct/best/simplest way to convert a c-style string to a std::string.
The conversion should accept a max_length, and terminate the string at the first \0 char, if this occur before max_length charter.
This page on string::string gives two potential constructors that would do what you want:
string ( const char * s, size_t n );
string ( const string& str, size_t pos, size_t n = npos );
Example:
#include<cstdlib>
#include<cstring>
#include<string>
#include<iostream>
using namespace std;
int main(){
char* p= (char*)calloc(30, sizeof(char));
strcpy(p, "Hello world");
string s(p, 15);
cout << s.size() << ":[" << s << "]" << endl;
string t(p, 0, 15);
cout << t.size() << ":[" << t << "]" << endl;
free(p);
return 0;
}
Output:
15:[Hello world]
11:[Hello world]
The first form considers p to be a simple array, and so will create (in our case) a string of length 15, which however prints as a 11-character null-terminated string with cout << .... Probably not what you're looking for.
The second form will implicitly convert the char* to a string, and then keep the maximum between its length and the n you specify. I think this is the simplest solution, in terms of what you have to write.
std::string str(c_str, strnlen(c_str, max_length));
At Christian Rau's request:
strnlen is specified in POSIX.1-2008 and available in GNU's glibc and the Microsoft run-time library. It is not yet found in some other systems; you may fall back to Gnulib's substitute.
std::string the_string(c_string);
if(the_string.size() > max_length)
the_string.resize(max_length);
This is actually trickier than it looks, because you can't call strlen
unless the string is actually nul terminated. In fact, without some
additional constraints, the problem practically requires inventing a new
function, a version of strlen which never goes beyond the a certain
length. However:
If the buffer containing the c-style string is guaranteed to be at least
max_length char's (although perhaps with a '\0' before the end),
then you can use the address-length constructor of std::string, and
trim afterwards:
std::string result( c_string, max_length );
result.erase( std::find( result.begin(), result.end(), '\0' ), result.end() );
and if you know that c_string is a nul terminated string (but perhaps
longer than max_length, you can use strlen:
std::string result( c_string, std::min( strlen( c_string ), max_length ) );
There is a constructor accepting two pointer parameters, so the code is simply
std::string cppstr(cstr, cstr + min(max_length, strlen(cstr)));
this is also going to be as efficient as std::string cppstr(cstr) if the length is smaller than max_length.
What you want is this constructor:
std::string ( const string& str, size_t pos, size_t n = npos ), passing pos as 0. Your const char* c-style string will get implicitly cast to const string for the first parameter.
const char *c_style = "012abd";
std::string cpp_style = std::string(c_style, 0, 10);
UPDATE: removed the "new" from the cpp_style initialization