I'm trying to understand un-managed code. I come from a background of C# and I'm playing around a little with C++.
Why is that this code:
#include <iostream>
using namespace std;
int main()
{
char s[] = "sizeme";
cout << sizeof(s);
int i = 0;
while(i<sizeof(s))
{
cout<<"\nindex "<<i<<":"<<s[i];
i++;
}
return 0;
}
prints out this:
7
index 0:s
index 1:i
index 2:z
index 3:e
index 4:m
index 5:e
index 6:
????
Shouldn't sizeof() return 6?
C strings are "nul-terminated" which means there is an additional byte with value 0x00 at the end. When you call sizeof(s), you are getting the size of the entire buffer including the nul terminator. When you call strlen(s), you are getting the length of the string contained in the buffer, not including the nul.
Note that if you modify the contents of s and put a nul terminator somewhere other than at the end, then sizeof(s) would still be 7 (because that's a static property of how s is declared) but strlen(s) could be somewhat less (because that's calculated at runtime).
No, all trings in C are terminated by the null character (ascii 0). So s is actually 7 bytes
s i z e m e \0
This is due to the fact, that C-strings contain the value 0 (or '\0') as last character to mark the end of the string.
s is seven bytes, 6 for the string and one for the null termination.
Related
I played with the string function,i wrote the following one, obviously I set the first character in the ret string to be written in a place that is out of bounds, but instead of an exception, I get a string that has one extra place .
std::string StringManipulations::rev(std::string s)
{
std::string ret(s.size(), ' ');
for (int i = 0; i < s.size(); i++)
{
std::string ch;
ch.push_back(s[i]);
int place = s.size() -i;
ret.replace(place,1,ch);
}
return ret;
}
I write by mistake in a position that corresponds to a place that is one larger than the original string size that I assign at the beginning of the function.
Why don't we get an error ?
s = StringManipulations::rev("abcde");
std::cout << s.size();
std::cout << s;
output is : 6 _edcba
any help ?
solved: adding ch as a String adds a null terminator automatically, and by doing so we can get a new string with size+1.
C++ has a zero-overhead rule.
This means that no overhead, (like checking if an index is in-bounds) should be done unintentionally.
You don't get an exception because c++ simply doesn't verify if the index is valid.
For the extra character, this might have something to do with (regular) c strings.
In c, strings are arrays of type char (char*) without a defined size.
The end of a string is denoted with a null terminator.
C++ strings are backwards compatible, meaning that they have a null terminator too.
It's possible that you replaced the terminator with an other character but the next byte was also a zero meaning that you added one more char.
In addition to the information above about null terminators, another answer to your question is that the docs says it will only throw if the position is greater than the string size, rather than beyond the end of the string.
string replace api
I have a string like,
string str="aaa\0bbb";
and I want to copy the value of this string to a char* variable. I tried the following methods but none of them worked.
char *c=new char[7];
memcpy(c,&str[0],7); // c="aaa"
memcpy(c,str.data(),7); // c="aaa"
strcpy(c,str.data()); // c="aaa"
str.copy(c,7); // c="aaa"
How can I copy that string to a char* variable without loosing any data?.
You can do it the following way
#include <iostream>
#include <string>
#include <cstring>
int main()
{
std::string s( "aaa\0bbb", 7 );
char *p = new char[s.size() + 1];
std::memcpy( p, s.c_str(), s.size() );
p[s.size()] = '\0';
size_t n = std::strlen( p );
std::cout << p << std::endl;
std::cout << p + n + 1 << std::endl;
}
The program output is
aaa
bbb
You need to keep somewhere in the program the allocated memory size for the character array equal to s.size() + 1.
If there is no need to keep the "second part" of the object as a string then you may allocate memory of the size s.size() and not append it with the terminating zero.
In fact these methods used by you
memcpy(c,&str[0],7); // c="aaa"
memcpy(c,str.data(),7); // c="aaa"
str.copy(c,7); // c="aaa"
are correct. They copy exactly 7 characters provided that you are not going to append the resulted array with the terminating zero. The problem is that you are trying to output the resulted character array as a string and the used operators output only the characters before the embedded zero character.
Your string consists of 3 characters. You may try to use
using namespace std::literals;
string str="aaa\0bbb"s;
to create string with \0 inside, it will consist of 7 characters
It's still won't help if you will use it as c-string ((const) char*). c-strings can't contain zero character.
There are two things to consider: (1) make sure that str already contains the complete literal (the constructor taking only a char* parameter might truncate at the string terminator char). (2) Provided that str actually contains the complete literal, statement memcpy(c,str.data(),7) should work. The only thing then is how you "view" the result, because if you pass c to printf or cout, then they will stop printing once the first string terminating character is reached.
So: To make sure that your string literal "aaa\0bbb" gets completely copied into str, use std::string str("aaa\0bbb",7); Then, try to print the contents of c in a loop, for example:
std::string str("aaa\0bbb",7);
const char *c = str.data();
for (int i=0; i<7; i++) {
printf("%c", c[i] ? c[i] : '0');
}
You already did (not really, see edit below). The problem however, is that whatever you are using to print the string (printf?), is using the c string convention of ending strings with a '\0'. So it starts reading your data, but when it gets to the 0 it will assume it is done (because it has no other way).
If you want to simply write the buffer to the output, you will have to do this with something like
write(stdout, c, 7);
Now write has information about where the data ends, so it can write all of it.
Note however that your terminal cannot really show a \0 character, so it might show some weird symbol or nothing at all. If you are on linux you can pipe into hexdump to see what the binary output is.
EDIT:
Just realized, that your string also initalizes from const char* by reading until the zero. So you will also have to use a constructor to tell it to read past the zero:
std::string("data\0afterzero", 14);
(there are prettier solutions probably)
#include<iostream>
#include<string.h>
#include<stdio.h>
int main()
{
char left[4];
for(int i=0; i<4; i++)
{
left[i]='0';
}
char str[10];
gets(str);
strcat(left,str);
puts(left);
return 0;
}
for any input it should concatenate 0000 with that string, but on one pc it's showing a diamond sign between "0000" and the input string...!
You append a possible nine (or more, gets have no bounds checking) character string to a three character string (which contains four character and no string terminator). No string termination at all. So when you print using puts it will continue to print until it finds a string termination character, which may be anywhere in memory. This is, in short, a school-book example of buffer overflow, and buffer overflows usually leads to undefined behavior which is what you're seeing.
In C and C++ all C-style strings must be terminated. They are terminated by a special character: '\0' (or plain ASCII zero). You also need to provide enough space for destination string in your strcat call.
Proper, working program:
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main(void)
{
/* Size is 4 + 10 + 1, the last +1 for the string terminator */
char left[15] = "0000";
/* The initialization above sets the four first characters to '0'
* and properly terminates it by adding the (invisible) '\0' terminator
* which is included in the literal string.
*/
/* Space for ten characters, plus terminator */
char str[11];
/* Read string from user, with bounds-checking.
* Also check that something was truly read, as `fgets` returns
* `NULL` on error or other failure to read.
*/
if (fgets(str, sizeof(str), stdin) == NULL)
{
/* There might be an error */
if (ferror(stdin))
printf("Error reading input: %s\n", strerror(errno));
return 1;
}
/* Unfortunately `fgets` may leave the newline in the input string
* so we have to remove it.
* This is done by changing the newline to the string terminator.
*
* First check that the newline really is there though. This is done
* by first making sure there is something in the string (using `strlen`)
* and then to check if the last character is a newline. The use of `-1`
* is because strings like arrays starts their indexing at zero.
*/
if (strlen(str) > 0 && str[strlen(str) - 1] == '\n')
str[strlen(str) - 1] = '\0';
/* Here we know that `left` is currently four characters, and that `str`
* is at most ten characters (not including zero terminaton). Since the
* total length allocated for `left` is 15, we know that there is enough
* space in `left` to have `str` added to it.
*/
strcat(left, str);
/* Print the string */
printf("%s\n", left);
return 0;
}
There are two problems in the code.
First, left is not nul-terminated, so strcat will end up looking beyond the end of the array for the appropriate place to append characters. Put a '\0' at the end of the array.
Second, left is not large enough to hold the result of the call to strcat. There has to be enough room for the resulting string, including the nul terminator. So the size of left should at least 4 + 9, to allow for the three characters (plus nul terminator) that left starts out with, and 9 characters coming from str (assuming that gets hasn't caused an overflow).
Each of these errors results in undefined behavior, which accounts for the different results on different platforms.
I do not know why you are bothering to include <iostream> as you aren't using any C++ features in your code. Your entire program would be much shorter if you had:
#include <iostream>
#include <string>
int main()
{
std::string line;
std::cin >> line;
std::cout << "You entered: " << line;
return 0;
}
Since std::string is going to be null-terminated, there is no reason to force it to be 4-null-terminated.
Problem #1 - not a legal string:
char left[4];
for(int i=0; i<4; i++)
{
left[i]='0';
}
String must end with a zero char, '\0' not '0'.
This causes what you describe.
Problem #2 - fgets. You use it on a small buffer. Very dangerous.
Problem #3 - strcat. Yet again trying to fill a super small buffer which should have already been full with an extra string.
This code looks an invitation to a buffer overflow attack.
In C what we call a string is a null terminated character array.All the functions in the string.h library are based on this null at the end of the character array.Your character array is not null terminated and thus is not a string , So you can not use the string library function strcat here.
I always get a garbage value like this 'Íýýýý««««««««îþîþ' at the end when i output my array. What am I doing wrong?
void func()
{
const int size = 100;
char * buffer = new char[size];
for(int i=0; i<size; i++)
buffer[i] = ' ';
cout<<buffer;
}
However if I use a for loop to output the buffer, there is no garbage value.
Because you don't null terminate your buffer, std::cout.operator<<(char*) will try to find \0 as its terminating character.
As pointed out in comments, feel free to append that \0 to the end of your buffer :).
ScarletAmaranth is right. C style strings (an array of char) must finish with the char '\0'. That's the way functions that play with char arrays (cout in this case) know when the string finish in memory. If you forget the '\0' character at the end of the array, cout prints all the chars in the array and then goes on printing any data in memory after the array. These other data is rubbish, and that's why you see these strange characters.
If you use the string type (more C++ style) instead of char arrays, you won't have this problem because string type don't use '\0' as a string delimiter.
On the other hand, you don't see rubbish when use a loop to iterate over the 100 elements of the array just because you only print these 100 chars. I mean, you control exactly what you are printing and know when to stop, instead of leaving the cout function figure out when to stop printing.
NoobQuestion:
I heard that filling a char array can be terminated early with the null char. How is this done?
I've searched every single google result out there but still have come up empty handed.
Do you mean something like this:
char test[11] = "helloworld";
std::cout << test << std::endl;
test[2] = 0;
std::cout << test;
This outputs
helloworld
he
?
That's a convention called "null-terminated string". If you have a block of memory which you treat as a char buffer and there's a null character within that buffer then the null-terminated string is whatever is contained starting with the beginning of the buffer and up to and including the null character.
const int bufferLength = 256;
char buffer[bufferLength] = "somestring"; //10 character plus a null character put by the compiler - total 11 characters
here the compiler will place a null character after the "somestring" (it does so even if you don't ask to). So even though the buffer is of length 256 all the functions that work with null-terminated strings (like strlen()) will not read beyond the null character at position 10.
That is the "early termination" - whatever data is in the buffer beyond the null character it is ignored by any code designed to work with null-terminated strings. The last part is important - code could easily ignore the null character and then no "termination" would happen on null character.