Trouble Understanding sprintf with 'char * str + int' - c++

I was looking at a project and came across the following code and am unable to figure out what the sprintf is doing in this context and was hoping someone might be able to help me figure it out.
char storage[64];
int loc = 0;
int size = 35;
sprintf(storage+(loc),"A"); //Don't know what this does
loc+=1;
sprintf(storage+(loc),"%i", size); //Don't know what this does
loc+=4;
sprintf(storage+(loc), "%i", start); //Don't know what this does
start += size;
loc += 3;
The code later does the following in another part
string value;
int actVal;
int index = 0;
for(int j = index+1; j < index+4; j++)
{
value += storage[j];
}
istringstream iss;
iss.str(value);
iss >> actVal; //Don't understand how this now contains size
The examples I have seen online regarding sprintf never covered that the above code was possible, but the program executes fine. I just can't figure out how the "+loc" affects storage in this instance and how the values would be saved/stored. Any help would be appreciated.

Ugly code! Regardless, for the first part, storage+(loc) == &storage[loc]. You end up with a string "A35\0<unknown_value>1234\0", assuming start = 1234, or in long form:
sprintf(&storage[0],"A");
sprintf(&storage[1],"%i", size);
sprintf(&storage[5], "%i", start);
For the second part, assuming we have the "A35\0<unknown_value>1234\0" above, we get:
value += '3';
value += '5';
value += '\0';
value += '<unknown_value>'; // This might technically be undefined behaviour
So now value = "35". [1]
iss.str(value);
iss >> actVal;
This turns the string into an input stream and reads out the first string representing an integer, "35", and converts it into an integer, giving us basically actVal = atoi(value.c_str());.
Finally, according to this page, yes, reading an uninitialised ("indeterminate value" is the official term) array element is undefined behaviour thus should be avoided.
[1] Note that in a usual implementation, there is a theoretical 10/256 chance that the <unknown_value> could contain an ASCII digit, so value could end up being between 350 and 359, which is obviously not a good outcome and is why one shouldn't ignore undefined behaviour.

The function sprintf() works just like printf(), except the result is not printed in stdout, rather it is store in a string variable. I suggest you read the sprintf() man page carefully:
https://linux.die.net/man/3/sprintf
Even if you are not on a Linux, that function is pretty much similar across different platforms, be it Windows, Mac or other animals. That said, this piece of code you have presented seems to be unnecessarily complicated.
The first part could be written as:
sprintf(storage,"A %i %i", size, start);
For a similar-but-not-equal result, but then again, it all depends on what exactly the original programmer intended this storage area to hold. As Ken pointed out, there are some undefined bytes and behaviors coming from this code as-is.

From the standard:
int sprintf ( char * str, const char * format, ... );
Write formatted data to string
Composes a string with the same text that would be printed if format was used on printf, but instead of being printed, the content is stored as a C string in the buffer pointed by str.
sprintf(storage+(loc),"A");
writes "A" into a buffer called storage. The storage+(loc) is pointer arithmetic. You're specifying which index of the char array you're writing into. So, storage = "A".
sprintf(storage+(loc),"%i", size);
Here you're writing size into storage[1]. Now storage = "A35\0", loc = 1, and so on.
Your final value of storage = "A35\0<garbage><value of start>\0"
actVal: Don't understand how this now contains size
The for loop goes through storage[1] through storage[5], and builds up value using the contents of storage. value contains the string "35\0<garbage>", and iss.str(value) strips it down to "35\0".
iss >> actVal
If you have come across std::cin, it's the same concept. The first string containing an integer value is written into actVal.

Related

Sizeof is a negative number when I try to find out the length of a string

In my code, I've been trying to find the length of a specific string in a string array. However, when I do it, no matter what method I try to find the length of the specific string, always returns -858993460
The code I've been working with is
void drawChoices(int x, std::string s [], int iChoice)
{
setCurPos(x, 1); //this function just sets the console cursor
for (int i = 0; i <= iChoice; i++)
{
color(15, 4); //this function just sets the console color
std::cout << "[" << s[i] << "]";
color(8, 8);
int asfa = sizeof s[i]; //this is the one causing trouble
std::cout << " \n";
}
}
I tried finding the length of the string in the array with sizeof, string::length, and strlen, but those yielded the same results.
You can never obtain the length of a string with sizeof. The sizeof operator is evaluated at compile time and returns the size of a type or variable. Your use of sizeof is:
sizeof s[i]
Since s[i] has type std::string, that expression evaluates to whatever size your implementation's std::string happens to be. But that is determined at compile time, and so has no dependency on the contents of the buffer, and is something that you are simply not interested in knowing.
Use either std::string::size() or std::string::length() to obtain the length of a std::string.
So, in your code you would use
s[i].length()
to obtain the length, that is the number of character elements, in the string s[i]. Do note also that the size() and length() methods, which are interchangeable, return std::string::size_type which you should use rather than int.
As for where the negative value comes from, we cannot say. Your code doesn't output the value that sizeof yielded, which would not be negative in any case.
Should your loop really be
for (int i = 0; i <= iChoice; i++)
rather than
for (int i = 0; i < iChoice; i++)
Rather than passing, and presumably storing, arrays of std::string, it would be more idiomatic to use a C++ container. In this case std::vector<std::string>.
From what #VladfromMoscow mentioned, I have figured out the problem.
The problem was when I was using Visual Studio 2013's debugging and local windows, I was debugging the same time the variable was initializing.
With the local window compiling the order of the line and how Visual Studio's debugging works, it gives out a random number because the variable wasn't initialized.
It reads size_t asfa first, stops it, local window gets the value, and then executes s[i].length(); once I continue it.
I thought it would be the program's fault because I tried outputting std::cout << asfa;, but I remebered that you can't have an int or size_t to a string directly, without a cast or conversion. Thank you for all of you who helped.

Junk after C++ string when returned

I've just finished C++ The Complete Reference and I'm creating a few test classes to learn the language better. The first class I've made mimics the Java StringBuilder class and the method that returns the string is as follows:
char *copy = new char[index];
register int i;
for(i = 0; i <= index; i++) {
*(copy + i) = *(stringArray + i);
} //f
return copy;
stringArray is the array that holds the string that is being built, index represents the amount of characters that have been entered.
When the string returns there is some junk after it, such as if the string created is abcd the result is abcd with 10 random characters after it. Where is this junk coming from? If you need to see more of the code please ask.
You need to null terminate the string. That null character tells the computer when when string ends.
char * copy = new char[ length + 1];
for(int i = 0; i < length; ++i) copy[i] = stringArray[i];
copy[length] = 0; //null terminate it
Just a few things. Declare the int variable in the tighest scope possible for good practice. It is good practice so that unneeded scope wont' be populate, also easier on debugging and kepping track. And drop the 'register' keyword, let the compiler determine what needs to be optimized. Although the register keyword just hints, unless your code is really tight on performance, ignore stuff like that for now.
Does index contain the length of the string you're copying from including the terminating null character? If it doesn't then that's your problem right there.
If stringArrary isn't null-terminated - which can be fine under some circumstances - you need to ensure that you append the null terminator to the string you return, otherwise you don't have a valid C string and as you already noticed, you get a "bunch of junk characters" after it. That's actually a buffer overflow, so it's not quite as harmless as it seems.
You'll have to amend your code as follows:
char *copy = new char[index + 1];
And after the copy loop, you need to add the following line of code to add the null terminator:
copy[index] = '\0';
In general I would recommend to copy the string out of stringArray using strncpy() instead of hand rolling the loop - in most cases strncpy is optimized by the library vendor for maximum performance. You'll still have to ensure that the resulting string is null terminated, though.

Stringstream write() problem while "overwriting"

Currently, I have a stringstream called Data. I am seeking to the beginning of the stringstream by using:
Data.seekp(0, std::ios::beg);
Then, I try writing 2 integers to the first 8 bytes of the stringstream (previously, the first 8 bytes were set to 0)
Data.write(reinterpret_cast<char*>(&dataLength),sizeof(int));
Data.write(reinterpret_cast<char*>(&dataFlags),sizeof(int));
Using the Visual C++ debugger and when I set a breakpoint, I can see that dataLength is equal to 12, and dataFlags is equal to 0, so therefore it should be writing 12 and 0 respectively.
After writing the 2 integers, it seemed to have no effect. I then print my stringstream data using the following code:
char* b = const_cast<char*>(Data.str().c_str());
for (int i = 0; i < dataLength; i++)
{
printf("%02X ",(unsigned char)b[i]);
}
I can see that the first 8 bytes of my data are still 0's even though I just overwrote the first 12 bytes with two integers (where the first integer != 0).
Why isn't the data in my stringstream being overwritten properly?
char* b = const_cast<char*>(Data.str().c_str());
Data.str() is a temporary, which is destroyed at the end of this statement; the value of that temporary's c_str() can only be used while the temporary is alive (and you've made no modifications to it, the invalidation rules are complex for std::string). You can never use b without Undefined Behavior.
std::string b = Data.str();
for (int i = 0; i < b.size(); i++) {
printf("%02X ", (unsigned char) b[i]);
}
I assume you really want to write the string "12" into the stringstream. You can't convert the int 12 to a char* by merely casting the int to char*. That is, I believe this part of your might be incorrect:
reinterpret_cast<char*>(&dataLength)
If dataLength is really an int, this is not the correct way to turn it into a char*. Perhaps this:
Data << dataLength << dataFlags;
I hope I haven't totally misunderstood what you're trying to achieve.

Please suggest what is wrong with this string reversal function?

This code is compiling clean. But when I run this, it gives exception "Access violation writing location" at line 9.
void reverse(char *word)
{
int len = strlen(word);
len = len-1;
char * temp= word;
int i =0;
while (len >=0)
{
word[i] = temp[len]; //line9
++i;--len;
}
word[i] = '\0';
}
Have you stepped through this code in a debugger?
If not, what happens when i (increasing from 0) passes len (decreasing towards 0)?
Note that your two pointers word and temp have the same value - they are pointing to the same string.
Be careful: not all strings in a C++ program are writable. Even if your code is good it can still crash when someone calls it with a string literal.
When len gets to 0, you access the location before the start of the string (temp[0-1]).
Try this:
void reverse(char *word)
{
size_t len = strlen(word);
size_t i;
for (i = 0; i < len / 2; i++)
{
char temp = word[i];
word[i] = word[len - i - 1];
word[len - i - 1] = temp;
}
}
The function looks like it would not crash, but it won't work correctly and it will read from word[-1], which is not likely to cause a crash, but it is a problem. Your crashing problem is probably that you passed in a string literal that the compiler had put into a read-only data segment.
Something like this would crash on many operating systems.
char * word = "test";
reverse(word); // this will crash if "test" isn't in writable memory
There are also several problems with your algorithm. You have len = len-1 and later temp[len-1] which means that the last character will never be read, and when len==0, you will be reading from the first character before the word. Also, temp and word are both pointers, so they both point to the same memory, I think you meant to make a copy of word rather than just a copy of the pointer to word. You can make a copy of word with strdup. If you do that, and fix your off-by-one problem with len, then your function should work,
But that still won't fix the write crash, which is caused by code that you have not shown us.
Oh, and if you do use strdup be sure to call free to free temp before you leave the function.
Well, for one, when len == 0 len-1 will be a negative number. And that's pretty illegal. Second, it's quite possible that your pointer is pointing at an unreserved area of memory.
If you called that function as followed:
reverse("this is a test");
then with at least one compiler will pass in a read only string due to backwards compatibility with C where you can
pass string literals as non-const char*.

Very strange char array behaviour

.
unsigned int fname_length = 0;
//fname length equals 30
file.read((char*)&fname_length,sizeof(unsigned int));
//fname contains random data as you would expect
char *fname = new char[fname_length];
//fname contains all the data 30 bytes long as you would expect, plus 18 bytes of random data on the end (intellisense display)
file.read((char*)fname,fname_length);
//m_material_file (std:string) contains all 48 characters
m_material_file = fname;
// count = 48
int count = m_material_file.length();
now when trying this way, intellisense still shows the 18 bytes of data after setting the char array to all ' ' and I get exactly the same results. even without the file read
char name[30];
for(int i = 0; i < 30; ++i)
{
name[i] = ' ';
}
file.read((char*)fname,30);
m_material_file = name;
int count = m_material_file.length();
any idea whats going wrong here, its probably something completely obvious but im stumped!
thanks
Sounds like the string in the file isn't null-terminated, and intellisense is assuming that it is. Or perhaps when you wrote the length of the string (30) into the file, you didn't include the null character in that count. Try adding:
fname[fname_length] = '\0';
after the file.read(). Oh yeah, you'll need to allocate an extra character too:
char * fname = new char[fname_length + 1];
I guess that intellisense is trying to interpret char* as C string and is looking for a '\0' byte.
fname is a char* so both the debugger display and m_material_file = fname will be expecting it to be terminated with a '\0'. You're never explicitly doing that, but it just happens that whatever data follows that memory buffer has a zero byte at some point, so instead of crashing (which is a likely scenario at some point), you get a string that's longer than you expect.
Use
m_material_file.assign(fname, fname + fname_length);
which removes the need for the zero terminator. Also, prefer std::vector to raw arrays.
std::string::operator=(char const*) is expecting a sequence of bytes terminated by a '\0'. You can solve this with any of the following:
extend fname by a character and add the '\0' explicitly as others have suggested or
use m_material_file.assign(&fname[0], &fname[fname_length]); instead or
use repeated calls to file.get(ch) and m_material_file.push_back(ch)
Personally, I would use the last option since it eliminates the explicitly allocated buffer altogether. One fewer explicit new is one fewer chance of leaking memory. The following snippet should do the job:
std::string read_name(std::istream& is) {
unsigned int name_length;
std::string file_name;
if (is.read((char*)&name_length, sizeof(name_length))) {
for (unsigned int i=0; i<name_length; ++i) {
char ch;
if (is.get(ch)) {
file_name.push_back(ch);
} else {
break;
}
}
}
return file_name;
}
Note:
You probably don't want to use sizeof(unsigned int) to determine how many bytes to write to a binary file. The number of bytes read/written is dependent on the compiler and platform. If you have a maximum length, then use it to determine the specific byte size to write out. If the length is guaranteed to fewer than 255 bytes, then only write a single byte for the length. Then your code will not depend on the byte size of intrinsic types.