std::strcpy and std::strcat with a std::string argument - c++

This is from C++ Primer 5th edition. What do they mean by "the size of largeStr". largeStr is an instance of std::string so they have dynamic sizes?
Also I don't think the code compiles:
#include <string>
#include <cstring>
int main()
{
std::string s("test");
const char ca1[] = "apple";
std::strcpy(s, ca1);
}
Am I missing something?

strcpy and strcat only operate on C strings. The passage is confusing because it describes but does not explicitly show a manually-sized C string. In order for the second snippet to compile, largeStr must be a different variable from the one in the first snippet:
char largeStr[100];
// disastrous if we miscalculated the size of largeStr
strcpy(largeStr, ca1); // copies ca1 into largeStr
strcat(largeStr, " "); // adds a space at the end of largeStr
strcat(largeStr, ca2); // concatenates ca2 onto largeStr
As described in the second paragraph, largeStr here is an array. Arrays have fixed sizes decided at compile time, so we're forced to pick some arbitrary size like 100 that we expect to be large enough to hold the result. However, this approach is "fraught with potential for serious error" because strcpy and strcat don't enforce the size limit of 100.
Also I don't think the code compiles...
As above, change s to an array and it will compile.
#include <cstring>
int main()
{
char s[100] = "test";
const char ca1[] = "apple";
std::strcpy(s, ca1);
}
Notice that I didn't write char s[] = "test";. It's important to reserve extra space for "apple" since it's longer than "test".

You are missing the
char largeStr[100];
or similar the book doesn't mention.
What you should do is forget about strcpy and strcat and C-style strings real quick. Just remember how to make c++ std::string out of them and never look back.

Related

Null character behavior c++

#include <iostream>
#include <string>
using namespace std;
int main() {
string s = "hello";
cout << s[5] << endl;
return 0;
}
In the above code, if I print s[5], it correctly prints a NULL character. But if I change the code to this:
#include <iostream>
#include <string>
using namespace std;
int main() {
char[] s = {'a','b','c','d','e'};
cout << s[5] << endl;
return 0;
}
It doesn't print a NULL character but something random. If I store the string as a string or as a char*, then the behavior is in tune with what I expect.
But if I explicitly declare the character array, how does the compiler know when the array ends? Does the size of the array gets stored at compile time?
String literals and std::strings store null terminated strings.
But an array of 5 char declared like:
char s[] = {'a','b','c','d','e'};
contains only 5 char, no null terminator.
But the compiler does know the size of s. It is part of the type of s. It has no convenient .size() function like std::string, std::vector or std::array does but you can get it by doing:
sizeof(s) / sizeof(s[0])
Or more safely in C++11:
std::extent<decltype(s)>::value
Or in C++17:
std::size(s)
(demo)
Arrays have a habit of decaying to pointers though and then there is no way of getting the size, you have to keep track of it yourself. Which is why std::string, std::vector or std::array is preferred in C++.
Strings are null-terminated, and const char* are treated the same way as Strings are. When you declare a array with a size it's put on the stack and the compiler doesn't know the size. Array out-of-bounds exceptions aren't determined during compile time.
the string class in c++ has the constructor which by itself adds the null character to the string passed to it if not explicitly added. But while using char it only stores the content passed to it (i.e) if you want to have a null character you have to explicitly add in the declaration or the definition of that char.
When you do char[] s = {'a','b','c','d','e'};, it will store characters mentioned and nothing else.
if I explicitly declare the character array, how does the compiler know when the array ends?
size is determined by number of characters provided by you.
Does the size of the array gets stored at compile time?
no, the size of array is determined by memory blocks allocated to it. (It is not stored separately in memory, if that's what you meant)
And when you use this string s = "hello";, strings are always null terminated.
Your code is char s[] = {'a','b','c','d','e'};, so it will not put the \0 at the end of your char array. It will put the \0 with three methods below:
1. char s[] = {'a','b','c','d','e', '\0'};
2. char s[] = "abcde";
3. string s = "abcde";
So if you use any of the three above, you will get a NULL character.
"how does the compiler know when the array ends ?": the compiler knows how many elements the array has, from its declaration, and this information is available through the sizeof operator.
Anyway C-style arrays have virtually no size, as they are implicitly turned to pointers when passed as arguments, and their length is dropped (IMO a major flaw in the design of the C language). Overflow avoidance is your responsibility.
For this reason, you mustn't use a cout << statement if your string isn't null-terminated.

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.

Assigning value to char array [duplicate]

This question already has answers here:
Assigning char array a value in C
(2 answers)
Closed 8 years ago.
I get no error when I type
char a[10] = "Hi";
But when I change it to the following, I get an error: Array type char is not assignable.
char a[10];
a = "Hi";
Why is array type char not assignable? Is it because the language is written that way on purpose or am I missing a point?
An array is not a modifiable lvalue
use
char a[10];
strcpy(a, "Hi");
The C++ way of doing this, as I commented above, would be to use std::string instead of char[]. That will give you the assignment behavior you're expecting.
That said, the reason you're only getting an error for the second case is that the = in these two lines mean different things:
char a[10] = "Hi";
a = "Hi";
The first is an initialization, the second is an assignment.
The first line allocates enough space on the stack to hold 10 characters, and initializes the first three of those characters to be 'H', 'i', and '\0'. From this point on, all a does is refer to the position of the the array on the stack. Because the array is just a place on the stack, a is never allowed to change. If you want a different location on the stack to hold a different value, you need a different variable.
The second (invalid) line, on the other hand, tries to change a to refer to a (technically different) incantation of "Hi". That's not allowed for the reasons stated above. Once you have an initialized array, the only thing you can do with it is read values from it and write values to it. You can't change its location or size. That's what an assignment would try to do in this case.
The language does not allow assigning string literals to character arrays. You should use strcpy() instead:
strcpy(a, "Hi");
a is a pointer to the array, not the array itself. It cannot be reassigned.
You tagged with C++ BTW. For that case better use std::string. It's probably more what you're expecting.
Simple, the
char a[10] = "Hi";
is a little "extra feature", as it cannot be done like that on run-time.
But that's the reason for C/C++ standard libraries.
#include <string.h>
// ...
strcpy(a,"Test"); // STR-ing C-o-PY.
This comes from the C's standard library. If using C++ you should use std::string, unless you really want to suck all the possible performance from your destination PC.
this is because initialization is not an assignment. the first thing which works is an initialization, and the second one, which does not work, as expected, is assignment. you simply cant assign values to arrays you should use sth like strcpy or memcpy. or you can alternatively use std::copy from <algorithm>
It is so simple,(=) have two different mean assignment and initialization. You can also write your code like that
#include <iostream>
using namespace std;
int main ()
{
char message[3] = {'H', 'i', '\0'};
cout << message<< endl;
return 0;
}
in this code you have no need to write a difficult code or function and even no need of string.h

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.

Segmentation fault when appending two chars - C++

I am trying to append two chars but for some reason I am getting a segmentation fault.
My code is like;
#include <string.h>
char *one = (char*)("one");
char *two = (char*)("two");
strcat(one, two);
and I seem to be getting a segmentation fault at strcat(one, two), why is that?
http://www.cplusplus.com/reference/clibrary/cstring/strcat/
the first parameter to strcat, must be big enough to hold the resulting string
try:
//assuming a,b are char*
char* sum = new char[strlen(a) +strlen(b)+1];
strcpy(sum,a);
strcat(sum,b);
There should be enough legal memory to hold the entire string.
char *one = new char[128]; //allocating enough memory!
const char *two = "two"; //"two" is const char*
strcpy(one, "one");
strcat(one, two); //now the variable "one" has enough memory to hold the entire string
By the way, if you prefer using std::string over char* in C++, such thing would be easier to handle:
#include<string>
std::string one = "one";
std::string two = "two";
one = one + two; //Concatenate
std::cout << one;
Output:
onetwo
There are two reasons for this.
If you have a pointer initialized to a string literal, that memory is read-only and modifying it will result in undefined behavior. In this case, if you try to append a string to a string literal, you'll be modifying this sort of memory, which will result in problems.
When using strcat you need to guarantee that space exists for the concatenation of the string at the location you're specifying. In this case, you cannot guarantee that, since a string literal is only guaranteed to have enough space to hold the literal itself.
To fix this, you'll want to explicitly allocate a buffer large enough to hold the concatenation of the two strings, including the null terminator. Here's one approach:
char* buffer = malloc(strlen(one) + strlen(two) + 1);
strcpy(buffer, one);
strcat(buffer, two);
Hope this helps!
The seg fault is because you attempt to write to read only memory. The first action of the strcat is to copy of 't' from the first entry of two into the null at the end of "one". So strictly the seg fault is not due to lack of storage - we never get that far. In fact this code will also likely give you a seg fault:
char* one = "one";
char* two = "";
strcat(one, two);
All this tries to do is copy a null over a null, but in read-only memory. I suppose a optimiser might happen to stop this on some platforms.
Oddly enough the following (incorrect) code will (probably) not give you a seg fault, and even give the "right" answer:
char one[] = "one";
char two[] = "two";
strcat(one, two);
printf("%s\n", one);
This successfully writes "onetwo" to stdout on my machine. We get a stack scribble, which we happen to get away with.
On the other hand this does seg fault:
char* one = "one "; // Plenty of storage, but not writable.
char two[] = "two";
strcat(one,two);
Hence the solution:
const unsigned enoughSpace = 32;
char one[enoughSpace] = "one";
char two[] = "two";
strcat(one,two);
printf("%s\n", one);
The issue with this is of course, how large to make enoughSpace in order to store what ever is coming?
Hence the functions strncat, or strcat_s, or more easily std::string.
Moral of the story: in C++, just like C, you really need to know what your memory layout is.
There are several problems here. Firstly, though you have casted the strings to mutable versions, they really are string literals and hence should not be written. Secondly, you are using strcat which will write into the string buffer, completely ignoring the length of the string buffer (it's better to use strncat which requires you to specify the length of the buffer). Lastly, since this is C++, it would be way better to use:
#include <string>
// ...
string one = "one";
string two = "two";
one.append(two);
You never reserved some space for your strings.
#include <string.h>
#include <stdio.h>
int main(void){
char str[20] = "";
strcat(str, "one");
strcat(str, "two");
printf("%s", str);
}
Would be one correct way to do this. The other (and way better) is to use the std::string class.
#include <string>
#include <cstdio>
int main(void){
std::string str;
str += "one";
str += "two";
std::printf("%s", str.c_str());
}
Your destination string should be large enough to hold both the destination and the source string. So an example would be
char one[10] = "one";
char two[4] = "two";
strcat(one,two);
strcat needs a "writeable" buffer as the target. In your example, it is a pointer to a string constant (or literal), which you cannot write to, thus it results in an exception. The target buffer can be a buffer on the stack or one dynamically allocated (e.g., with malloc).
This is not the problem of "not enough space".
char *a = "str";
Look at the code above, the pointer a is point to "static memory". The string "str" is stored in the static place in the PCB, which means it can't be overwritten.
So, the codes below will be better:
#include <string>
using std::string;
string a = "stra";
string b = "strb";
a += b;