Weird behavior of printf in C++ with a newline character - c++

This could be simple or lame question. I have been playing with printf function in C++. When I execute following code on Debian Wheezy with any 9 character argument (eg:"argument1") ;
#include<stdio.h>
int main(int argc, char** argv){
printf(argv[1]+'\n');
}
the out put was;
SSH_AGENT_PID=4375
Then I checked the environment variable $SSH_AGENT_PID and its value is 4375.
Could you please tell me what is going on with my code ?
(I tried to examine the disassembled code in gdb. But my assembly knowledge is poor to understand exactly whats going on)

Simple fix:
printf("%s\n", argv[1]);
This is an improvement on your code for two reasons: First, it's generally a good idea to have the first argument to printf be a string constant, as this prevents printf code injection. Second, it fixes the bug because as #Captain Oblivious pointed out, the code you wrote doesn't do what you think it does.

char* strings and string literals and character literals can't be added together. You need to create a new string and use strcat to concatenate one to the other.
What you're actually getting is undefined behavior, since you're adding some constant to a pointer and not checking that you've passed the end of the string. It's only a coincidence that the output was readable at all and that your program didn't crash.
If C++ is an option you can use std::string instead, which does allow + to concatenate two strings.

To explain what you are seeing from argv[1] + '\n'. This is pointer plus integer (character constants have type int in C).
The definition of that in C is that the pointer is advanced by as many units as are in the integer. \n is 10 in ASCII so this will advance the pointer by 10 characters. If your string is shorter than 10 then you're now reading whatever is in memory beyond the end of that string.
The fix suggested by Ben Braun is a good one; another option is puts( argv[1] ); which will output the string without doing any printf-like translations, and output a newline.

Related

C++ string copy() gives me extra characters at the end, why is that?

I am studying c++, In a blog they introduced the concept of copy function. When I tried the same in my system, the result is not matching to what I expected. Please let me know what I did wrong here in the below code.
#include <iostream>
main(){
std::string statement = "I like to work in Google";
char compName[6];
statement.copy(compName, 6, 18);
std::cout<<compName;
}
I expected Google but actual output is Googlex
I am using windows - (MinGW.org GCC-6.3.0-1)
You are confusing a sequence of characters, C style string, and std::string. Let's break them down:
A sequence of characters is just that, one character after another in some container (in your case a C style array). To a human being several characters may look like a string, but there is nothing in your code to make it such.
C style string is an array of characters terminated by a symbol \0. It is a carry over from C, as such a compiler will assume that if even if you don't tell it otherwise the array of characters may potentially be such a string.
C++ string (std::string) is a template class that stores strings. There is no need to worry how it does so internally. Although there are functions for interoperability with the first two categories, it is a completely different thing.
Now, let's figure out how a compiler sees your code:
char compName[6];
This creates an array of characters with enough space to store 6 symbols. You can write C style strings into it as long as they are 5 symbols or less, since you will need to also write '\0' at the end. Since in C++ C style arrays are unsafe, they will allow you to write more characters into them, but you cannot predict in advance where those extra characters will be written into memory (or even if your program will continue to execute). You can also potentially read more characters from the array... But you cannot even ask the question where that data will be coming from, unless you are simply playing around with your compiler. Never do that in your code.
statement.copy(compName, 6, 18);
This line writes 6 characters. It does not make it into a C style string, it is simply 6 characters in an array.
std::cout<<compName;
You are trying to output to the console a C style string... which you have not provided to a compiler. So a an operator<< receives a char [], and it assumes that you knew what you were doing and works as if you gave it C string. It displays one character after another until it reaches '\0'. When will it get such a character? I have no idea, since you never gave it one. But due to C style arrays being unsafe, it will have no problem trying to read characters past the end of an array, reading some memory blocks and thinking that they are a continuation of your non-existent C style sting.
Here you got "lucky" and you only got a single byte that appeared as an 'x', and then you got a byte with 0 written in it, and the output stopped. If you run your program at a different time, with a different compiler, or compiled with different optimisations you might get a completely different data displayed.
So what should you have done?
You can try this:
#include <iostream>
#include <string>
int main()
{
std::string statement = "I like to work in Google";
char compName[7]{};
statement.copy(compName, 6, 18);
std::cout<<compName;
return 0;
}
What did i change? I made an array able to hold 7 characters (leaving enough space for a C style string of 6 characters) and i have provided an empty initialisation list {}, which will fill the array with \0 characters. This means that when you will replace the first 6 of them with your data, there will be a terminating character in the very end.
Another approach would be to do this:
#include <iostream>
#include <string>
int main()
{
std::string statement = "I like to work in Google";
char compName[7];
auto length = statement.copy(compName, 6, 18);
compName[length] = '\0';
std::cout<<compName;
return 0;
}
Here i do not initialise the array, but i get the length of the data that is written there with a .copy method and then add the needed terminator in the correct position.
What approach is best depends on your particular application.
When inserting pointer to a character into the stream insertion operator, the pointer is required to point to null terminated string.
compName does not contain the null terminator character. Therefore inserting inserting (a pointer to an element of) it into a character stream violates the requirement above.
Please let me know what I did wrong here
You violate the requirement above. As a consequence, the behaviour of your program is undefined.
I expected Google but actual output is Googlex
This is because the behaviour of the program is undefined.
How to terminate it?
Firstly, make sure that there is room in the array for the null terminator character:
char compName[7];
Then, assign the null terminator character:
compName[6] = '\0';

converting a string to a c string

m working on some homework but don't even know where to start on this one. If you could can you throw me in the right direction. This is what i'm suppose to do
Write your own version of the str_c function that takes a C++ string as an argument (with the parameter set as a constant reference variable) and returns a pointer to the equivalent C-string. Be sure to test it with an appropriate driver.
There are different possibilities to write such a function.
First, take a look at the C++ reference for std::string, which is the starting point for your problem.
In the Iterator section on that page, you might find some methods which can help you to get the string character by character.
It can also help to read the documentation for the std::string::c_str method, you'd like to imitate: string::c_string. It's important to understand, how the system works with normal C-strings (char*):
Due to the fact, that a C-string has now length- or size-attribute, a trick is used to determine the end of the string: The last character in the string has to be a '\0'.
Make sure you understand, that a char* string can also be seen as array of characters (char[]). This might help you, when understanding and solving your problem.
as we know, C-string is null-terminated array of char. you can put char by char from std::string to an array of char, and then closed with '\0'. and remember a pointer to a char (char*) is also representation of array of char. you can use this concept

C++: Format not a string literal and no format arguments [duplicate]

This question already has answers here:
warning: format not a string literal and no format arguments
(3 answers)
Closed 8 years ago.
I've been trying to print a string literal but seems like I'm doing it wrong, since I'm getting a warning on compilation. It's probably due to wrong formatting or my misunderstanding of c_str() function, which I assume should return a string.
parser.y: In function ‘void setVal(int)’:
parser.y:617:41: warning: format not a string literal and no format arguments [-Wformat-security]
Line 617:
sprintf(temp, constStack.top().c_str());
Having those declarations
#include <stack>
const int LENGTH = 15;
char *temp = new char[LENGTH];
stack<string> constStack;
How can I provide a proper formating to string?
Simple - provide a format string:
sprintf(temp, "%s", constStack.top().c_str());
But much, much better:
string temp = constStack.top();
You are telling me in your comment that the problem is not so much the warning as the fact that your code doesn't do what you expect it to.
The solution to this and other, similar problems is to get rid of the strong C influence in your C++ code. Specifically, don't use raw dynamically allocated char arrays or sprintf. Use std::string instead.
In this case, you are using sprintf very incorrectly. Have you ever seen its signature? It goes like this:
sprintf(char *str, char const *format, ...)
str is the output of the operation. format describes what the output should be. The rest are the format arguments, which must by pure convention match what's described in format.
Now this "rest", written as ..., means that you can pass any number of arguments, even zero. And this is why your code even compiles (delivering a nice example for why ... is a dangerous feature, by the way).
In your code, the output string is, possibly incorrectly, your temp string. And the format to describe the output is, almost certainly incorrectly, what happens to sit on top of your stack.
Is this just about assigning one string to another, using sprintf simply because it more or less can do that as a very special case of what its feature set offers? There's no need for such hacks, as C++ has string assignment out of the box with std::string:
std::string temp = constStack.top();
Notice that this also eliminates the need to know the length of the string in advance.
If, for some reason, you really need formatting (but your question doesn't really show any need for it), then learn more about string streams as an alternative solution to format strings.
As the warning indicates it is issued as a result of the -Wformat-security option; you could simply disable the warning by removing the option; but it would be perhaps unwise.
The security issue is perhaps theoretical unless your code is to be widely distributed. Of perhaps more immediate concern is the possibility of your code crashing or behaving abnormally.
The problem is that the string is variable, and may at runtime contain formatting characters that cause it to attempt to read non-existent arguments. If for example the string is received from user input and the user entered "%s" it would attempt to read a string from some somewhere on the stack. That would at best place junk in temp, but worse if the memory read happened not to contain a nul character in the first 15 bytes, it would overrun temp, and corrupt the heap (in this case). Heap corruptions are probably worse than stack corruptions - the latent bug can remain unnoticed in your code for a long time only to start crashing after some unrelated change; and if it does crash, it is unlikely to be in any proximity to the cause.

strncpy char string issue when adding length

I'm having a problem with comparing 2 char strings that are both the same:
char string[50];
strncpy(string, "StringToCompare", 49);
if( !strcmp("StringToCompare", string) )
//do stuff
else
//the code runs into here even tho both strings are the same...this is what the problem is.
If I use:
strcpy(string, "StringToCompare");
instead of:
strncpy(string, "StringToCompare", 49);
it solves the problem, but I would rather insert the length of the string rather than it getting it itself.
What's going wrong here? How do I solve this problem?
You forgot to put a terminating NUL character to string, so maybe strcmp run over the end. Use this line of code:
string[49] = '\0';
to solve your problem.
You need to set the null terminator manually when using strncpy:
strncpy(string, "StringToCompare", 48);
string[49] = 0;
Lots of apparent guesses in the other answers, but a quick suggestion.
First of all, the code as written should work (and in fact, does work in Visual Studio 2010). The key is in the details of 'strncpy' -- it will not implicity add a null terminating character unless the source length is less than the destination length (which it is in this case). strcpy on the other hand does include the null terminator in all cases, suggesting that your compiler isn't properly handling the strncpy function.
So, if this isn't working on your compiler, you should likely initialize your temporary buffer like this:
char string[50] = {0}; // initializes all the characters to 0
// below should be 50, as that is the number of
// characters available in the string (not 49).
strncpy(string, "StringToCompare", 50);
However, I suspect this is likely just an example, and in the real world your source string is 49 (again, you should pass 50 to strncpy in this case) characters or longer, in which case the NULL terminator is NOT being copied into your temporary string.
I would echo the suggestions in the comments to use std::string if available. It takes care of all of this for you, so you can focus on your implementation rather than these trite details.
The byte count parameter in strncpy tells the function how many bytes to copy, not the length of the character buffer.
So in your case you are asking to copy 49 bytes from your constant string into the buffer, which I don't think is your intent!
However, it doesn't explain why you are getting the anomalous result. What compiler are you using? When I run this code under VS2005 I get the correct behavior.
Note that strncpy() has been deprecated in favor of strncpy_s, which does want the buffer length passed to it:
strncpy_s (string,sizeof(string),"StringToCompare",49)
strcopy and strncpy: in this situation they behave identically!!
So you didn't tell us the truth or the whole picture (eg: the string is at least 49 characters long)

Why this sprintf statement crashing? [duplicate]

This question already has answers here:
How to read and write a STL C++ string?
(3 answers)
Closed 4 years ago.
char filebuf[256];
path current = current_path();
std::cout<<"CurrentWorking "<<current<<endl;
string _UserDir = "TTiTraceLogs";
sprintf(filebuf,"%s/%s",current,_UserDir); ///crashing here
If I format only current, then it is ok.
sprintf(filebuf,"%s",current);
Output:
CurrentWorking D:/working/eclipse_projects/sadftp/CollectTTiTraceSept10_1009_174
_higher/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/Release
In the standard sprintf() function (and the other printf()-like functions), the %s specifier doesn't work with C++ stringss; it only works with C-style strings. Try _UserDir.c_str(), which gives you a const char * that points to the internal array of the string and can therefore be used with many C functions.
If current_path() returns something that is not a (possibly const) pointer to char or unsigned char, you need to convert that value too.
Is string actually std::string? And path a boost::filesystem::path? In that case, you should know that C library functions (like sprintf and printf) don't support C++ classes like std::string. That's only natural.
What you need to do is:
sprintf(filebuf, "%s%s", current.c_str(), _UserDir.c_str());
But a more elegant solution, if you're already using C++, is to either use std::stringstream or boost::format. As a bonus won't have to mess with allocating buffers on the stack and worrying that the result might be longer than the buffer (which may lead to buffer overruns and security exploits - sprintf() is probably the culprit behind many of those...).
std::stringstream
std::stringstream filebuf;
filebuf << current_path().c_str() << _UserDir;
std::string filename = filebuf.str();
boost::format
std::string filename = "%s%s" % current_path().c_str() % _UserDir;
By the way, if you just want to concatenate directories, the 'proper' way to do that with boost::filesystem::path would be:
boost::filesystem::path fullPath = current_path() / _UserDir;
Yes, the / operator is used for adding path components. They are separated by slashes after all, aren't they?
That being said, if you still choose, against all good advice, to use the old C library functions, please, for all that's good in the world, don't use sprintf(). Use the slightly-safer snprintf(), which takes the maximum buffer size as an argument.
Some of your code is unclear (e.g., what on earth is a path type?), but it looks like you're trying to print a std::string via a C-style string formatter (sprintf %s), which is totally invalid.
Look at std::stringstream. Also, for simple concatenation, std::string provides a + operator overload. So you could be doing simply:
current + "/" + whatever
sprintf only works with char*, you cannot use it with strings. Try _UserDir.c_str() and you should be fine.
What I don't understand is why your compiler doesn't complain. You can't pass non-POD types like std::string or boost::filepath as varargs; my compiler says that this will abort at runtime, and any compiler will know here that you're passing class types to a vararg. (Formally, it's undefined behavior, but unless the compiler defines it somehow as an extention, then there's no reason for it not to at least warn.)
current is not a char* pointer, it is a path so it might lead to complication
also, sprintf can't print to string variables like you are trying to do.
How long is current_path? Maybe strlen(current_path)+strlen(_UserDir)+2 > 256.
Since _UserDir is a C++ string, you'll need to get its C string to call sprintf(), which is a C function.
sprintf(filebuf,"%s/%s",current,_UserDir.c_str()); // gets C string
Also, what is a path? If it isn't like a typedef for char*, then you can't just sprintf() that either. It may be that operator<<() is overloaded for path, which is why the std::cout works.
%s indicates a C string. You have a C++ string. You need to call .c_str()
The reason of the crash has already been explained - I can only add one piece of advice - just ditch sprintf if possible and use string streams. It will make your code better. You will not have to worry about the length of the buffer or other errors like the one you encountered.