Scanning an integer using %c and a character using %d: unexpected results - c++

I am unable to give a proper reason for the following code's output. Can anybody help me.
int main()
{
int i;
char ch;
scanf("%c",&i);
scanf("%d",&ch);
printf("%d\n%c",i,ch);
return 0;
}
input output:
input:
a
45
output:
0
-

You're reading an int into a char. Assuming that sizeof(int) != sizeof(char), this will result in scanf writing beyond the end of ch with undefined consequences.
You should use the %i format specifier for int arguments and the %c for char arguments
int main()
{
int i;
char ch;
scanf("%c",&ch);
scanf("%d",&i);
printf("%d\n%c",i,ch);
return 0;
}

Don't lie to scanf or printf. Nothing good ever comes out of it.
In this case, you are trying to stuff 4 bytes into the space of 1 - which obviously doesn't work, however much you squeeze. The fact that your program doesn't crash is purely luck.

This is Undefined behavior.You are trying to confuse compiler by telling that to pop and push something different what exactly it should in printf and scanf statement respectively.

Related

swscanf can't read integer value

std::wifstream ifstream("JobList.txt");
ifstream.imbue(std::locale(std::locale::empty(), new std::codecvt_utf8<wchar_t>));
if (!ifstream.is_open()) {
std::cout << "파일을 찾을 수 없습니다!" << std::endl;
return 0;
}
std::wstring s;
wchar_t name[20];
int priority{};
int workingTime{};
int requestTime{};
while (ifstream) {
std::getline(ifstream, s);
swscanf(s.data(), L"%[^',']s, %d, %d, %d", name, &priority, &workingTime, &requestTime);
mRequestArrivationQueue.emplace(name, priority, workingTime, requestTime);
}
ifstream.close();
This is JobList.txt file
Good Boy, 1, 2, 5
도서 대출, 1, 2, 13
swscanf read only first wstring(name), but it doesn't read rest integer values
There is a little error in your code and a terrible (even if common) bad practice.
The error in that the conversion format specifier is [set] and it shall not be followed with a s. Here the format string requires a s character afer the first field (which is impossible) so the conversions stops after decoding the first field. The fix is trivial, remove that offending s (and the useless quotes, thanks to #AdrianMole for his comment):
swscanf(s.data(), L"%[^,], %d, %d, %d", name, &priority, &workingTime, &requestTime);
And the terrible practice is to fail to test the return value of a scanf family function. Had you tested it, you would have immediately found that it was 1 and that only the first field had been decoded.
IMHO, unless you are a C programmer and have used the C io functions for a long time, you should better use a C++ [w]stringstream. The syntax is not easier, but error detection is better...

Printf() - printed characters limit

I am trying to find what is the allowed limit for the number of characters to be printed by printf() in the C standard. I only found the answer in a discussion forum, which indicates INT_MAX.
For example, I checked the following:
#include <stdio.h>
// INT_MAX 2147483647
int main()
{
int x=3;
int y = printf("%2147483647d \n\n", x); --> Confirms INT_MAX ?
// If I change the above to 2147483648, y in set to -1
printf("y = %d\n\n", y);
return 0;
}
I wanted to ask why is printf() limited by INT_MAX ? Can anyone point out a C standard reasoning or source code reference ?
Edit
The closest answer I found was for fprintf(). At this link, on page 568, its mentioned (under undefined behavior):
The number of characters or wide characters transmitted by a formatted output
function (or written to an array, or that would have been written to an array) is
greater than INT_MAX.
Will the above justify for printf() as well ?
printf is stated to return number of characters printed or -1 in case or an error. Its return type is int. Maximum number you can store is int is INT_MAX.
What happens if you try to print more characters? Then printf cannot fulfill its contract: to return number of characters written. Standard does not says what to do when contract is impossible to fulfill, so the behavior in this case is undefined by not being defined in standard.
Well, if you print with the format specifier %d, which indicates a integer number, of course your maximum printable number would be INT_MAX. But your example is wrong. You are trying to tell it to print INT_MAX digits on a number, but that, of course, far exceeds the actual numerical value INT_MAX.
As to why your example fails, I suppose printf() stores the amount of digits to print in an integer number itself.
"Printf just prints the result".
In other words, the number of characters that printf can print depends on the format specifier specified.
The standard syntax for printf is,
int printf ( const char * format, ... );
here based on "format", the character count limit is set.
from you snippet:
int y = printf("%2147483647d \n\n", x);
because, the value specified is well with the range.
Likewise, when you changed the value to 2147483648 it goes beyond range.
This should have been a comment but due to space restrictions, I had to submit this as an answer.
#include<stdio.h>
#include<limits.h>
#include<malloc.h>
struct ec
{
char *c;
};
int main(void)
{
printf("INTMAX : %d\n",INT_MAX);
struct ec obj;
size_t max=(size_t)INT_MAX+1000;
size_t i;
obj.c=malloc(max*sizeof(char));
if(obj.c==NULL)
{
printf("Can't allocate such big memory chunk\n");
exit(0);
}
for(i=0;i<(max-1);i++)
obj.c[i]='0';
obj.c[i]='\0';
printf(obj.c); // This appear to print fine
/* Not bothered to put a free(obj.c) here*/
return 0;
}
I believe the problem is more with how int return type of the printf function. The return value is the number of characters printed and so it should be incremented per character print. I cannot think of what happens when printf prints (INT_MAX+1)nth character
Note: I neglected the compiler warnings.

What is proper size of buffer when using sprint()?

What is proper size of an char array (buffer) when i want to use sprintf function?
I dont know why this part of code is working if buffer can hold only 1 char? I put a lot more chars inside than 1.
/* sprintf example */
#include <stdio.h>
int main ()
{
char buffer[1];
int n, a=5, b=3;
n = sprintf (buffer, "%d plus %d is %d", a, b, a+b);
printf ("[%s] is a string %d chars long\n", buffer, n);
return 0;
}
Results:
[5 plus 3 is 8] is a string 13 chars long
What is proper size of an char array (buffer) when i want to use sprintf function?
There isn't one.
If you can work out an upper bound from the format string and types of input, then you might use that. For example, a 32-bit int won't take up more than 11 characters to represent in decimal with an optional sign, so your particular example won't need more than 44 characters (unless I miscounted).
Otherwise, use something safer: std::stringstream in C++, or snprintf and care in C.
I don't know why this part of code is working if buffer can hold only 1 char?
It isn't. It's writing past the end of the buffer into some other memory.
Maybe that won't cause any visible errors; maybe it will corrupt some other variables; maybe it will cause a protection fault and end the program; maybe it will corrupt the stack frame and cause all kinds of havoc when the function tries to return; or maybe it will cause some other kind of undefined behaviour. But it's certainly not behaving correctly.
In your code a buffer overflow occurred, there were no apparent consequences, but that doesn't mean it worked correctly, try using a memory debugger like valgrind and you will see what I mean.
You can't ensure that sprintf() will not overflow the buffer, that's why there is a snprintf() function to which you pass the size of the buffer.
Sample usage
char buffer[100];
int result;
result = snprintf(buffer, sizeof(buffer), "%d plus %d is %d", a, b, a + b);
if (result >= sizeof(buffer))
{
fprintf(stderr, "The string does not fit `buffer'.\n");
}
Assuming code must use sprintf() and not some other function:
pre-determine the worse case output size and add margin.
Unless there are major memory concerns, suggest a 2x buffer. Various locales can do interesting things like add ',' to integer output as in "123,456,789".
#include <stdio.h>
#include <limits.h>
#define INT_DECIMAL_SIZE(i) (sizeof(i)*CHAR_BIT/3 + 3)
#define format1 "%d plus %d is %d"
char buffer[(sizeof format1 * 3 * INT_DECIMAL_SIZE(int)) * 2];
int n = sprintf(buffer, format1, a, b, a + b);
A challenging example is when code tries sprintf(buf,"%Lf", some_long_double) as the output could be 1000s of characters should x == LDBL_MAX. About 5000 characters with binary128 as long double.
// - 123.............456 . 000000 \0
#define LDBL_DECIMAL_SIZE(i) (1 + 1 + LDBL_MAX_10_EXP + 1 + 6 1)

When to quantify ignored pattern match in the C sscanf function

Cppcheck 1.67 raised a portability issue in my source code at this line:
sscanf(s, "%d%*[,;.]%d", &f, &a);
This is the message I got from it:
scanf without field width limits can crash with huge input data on some versions of libc.
The original intention of the format string was to accept one of three possible limiter chars between two integers, and today - thanks to Cppcheck[1] - I see that %*[,;.] accepts even strings of limiter chars. However I doubt that my format string may cause a crash, because the unlimited part is ignored.
Is there possibly an issue with a buffer overrun? ...maybe behind the scenes?
[1]
How to get lost between farsightedness and blindness:
I tried to fix it by %1*[,;.] (after some API doc), but Cppcheck insisted in the issue, so I also tried %*1[,;.] with the same "success". Seems that I have to suppress it for now...
Congratulations on finding a bug in Cppcheck 1.67 (the current version).
You have basically three workarounds:
Just ignore the false positive.
Rework your format (assign that field, possible as you only want to match one character).
char tmp;
if(3 != sscanf(s, "%d %c%d", &f, &tmp, &a) || tmp!=',' && tmp!=';' && tmp!= '.')
goto error;
Suppress the warning directly (preferably inline-suppressions):
//cppcheck-suppress invalidscanf_libc
if(2 != sscanf(s, "%d%1*[,;.]%d", &f, &a))
goto error;
Don't forget to report the error, as "defect / false positive", so you can retire and forget that workaround as fast as possible.
When to quantify ignored pattern match in the C sscanf function?
Probably it's a good idea to always quantify (see below), but over-quantification may also distract from your intentions. In the above case, where a single separator char has to be skipped, the quantification would definitely be useful.
Is there possibly an issue with a buffer overrun? ...maybe behind the scenes?
There will be no crashes caused by your code. As to deal with the "behind the scenes" question, I experimented with large input strings. In the C library I tested, there was no internal buffer overflow. I tried the C lib that's shipped with Borland C++ 5.6.4 and found that I could not trigger a buffer overrun with large inputs (more than 400 million chars).
Surprisingly, Cppcheck was not totally wrong - there is a portability issue, but a different one:
#include <stdio.h>
#include <assert.h>
#include <sstream>
int traced_sscanf_set(const int count, const bool limited)
{
const char sep = '.';
printf("\n");
std::stringstream ss;
ss << "123" << std::string(count, sep) << "456";
std::string s = ss.str();
printf("string of size %d with %d '%c's in it\n", s.size(), count, sep);
std::stringstream fs;
fs << "%d%";
if (limited) {
fs << count;
}
fs << "*["<< sep << "]%d";
std::string fmt = fs.str();
printf("fmt: \"%s\"\n", fmt.c_str());
int a = 0;
int b = 0;
const sscanfResult = sscanf(s.c_str(), fmt.c_str(), &a, &b);
printf("sscanfResult=%d, a=%d, b=%d\n", sscanfResult, a, b);
return sscanfResult;
}
void test_sscanf()
{
assert(traced_sscanf_set(0x7fff, true)==2);
assert(traced_sscanf_set(0x7fff, false)==2);
assert(traced_sscanf_set(0x8000, true)==2);
assert(traced_sscanf_set(0x8000, false)==1);
}
The library I checked, internally limits the input consumed (and skipped) to 32767 (215-1) chars, if there is no explicitly specified limit in the format parameter.
For those who are interested, here is the trace output:
string of size 32773 with 32767 '.'s in it
fmt: "%d%32767*[.]%d"
sscanfResult=2, a=123, b=456
string of size 32773 with 32767 '.'s in it
fmt: "%d%*[.]%d"
sscanfResult=2, a=123, b=456
string of size 32774 with 32768 '.'s in it
fmt: "%d%32768*[.]%d"
sscanfResult=2, a=123, b=456
string of size 32774 with 32768 '.'s in it
fmt: "%d%*[.]%d"
sscanfResult=1, a=123, b=0

Stack overflow correction in C++

I read the code below in a book which said that this was vulnerable to stack overflow. Although fgets() has been used,I was not able to understand, why it is vulnerable?
My understanding is that using fgets() instead of gets() usually helps us get rid of buffer overflow by placing a null at the end. Am I missing something? What should be used instead of fgets() to correct the stack overflow?
void getinp(char *inp, int siz)
{
puts("Input value: ");
fgets(inp, siz, stdin);
printf("buffer3 getinp read %s\n", inp);
}
void display(char * val)
{
char tmp[16];
sprintf(tmp, "read val: %s\n", val);
puts(tmp);
}
int main(int argc, char *argv[])
{
char buf[16];
getinp(buf, sizeof(buf));
display(buf);
printf("buffer3 done\n");
}
In display tmp is declared as 16 chars long, but you are writing (with the sprintf) there not only val (which is guaranteed to be 16 characters or less), but also "read val: " and the final \n).
This means that if the user inserts more than 16-11=5 characters you have a buffer overflow in display.
One solution could be declaring buf in display to be large enough to store both val and the additional text, although in the real world you would just write to stdout using printf (without the intermediate buffer).
Also, usually when you have a sprintf and there's some potential risk of buffer overflow you use snprintf instead (actually, I use it always); snprintf, instead of overflowing the buffer, truncates the output if it would be too long, and returns the number of characters that would have been written if the output buffer was big enough.
In display, there is no way to be sure that val + 12 bytes is going fit into a 16 character buffer.