I searched around a little bit for information on this but didn't find anything satisfactory. Is there some special behavior to the function call
sprintf(someString, "");
that explains why this is warning (on gcc with -Wall)? I only managed to find that the C standard allows zero-length format strings.
I tried the following example
#include <stdio.h>
int main()
{
char str[2] = {'a', 'a'};
sprintf(str, "");
printf("\'%c\'\'%c\'\n", str[0], str[1]);
return 0;
}
which prints out
'''a'
which is exactly what I expected to see.
So, why the warning?
The fact that GCC issues a warning usually has nothing to do with whether the construct is legal C, but whether the GCC developers consider it either a likely indication that you meant something other than what you write, or just bad style. Here are some examples:
if (x = 0) — you almost surely meant if (x == 0).
printf(str) — you almost surely meant either fputs(str, stdout) or printf("%s", str); as written, the code is very dangerous.
if (foo == bar & MASK) — you almost surely meant if (foo == (bar & MASK)).
etc.
In your case, I think GCC is questioning why you're calling sprintf(String, "") to do the equivalent of String[0]=0; (the latter is much shorter, faster, and clearer).
You're getting the warning because gcc knows that the second argument to sprintf() should be a non-empty string, typically one with various format specifications — a functionally equivalent and "more legal" call to the one you're doing in your code would be sprintf(str, "%s", ""). Also, there's almost always one to N additional arguments, enough to fulfill the format specifications. As you're using it here, you're using it as a kind of strcpy(), which, while technically valid, is a very odd way to use the standard library.
It's simply a warning by GCC. If you wish to suppress it for one part of your application, you can do the following:
#pragma GCC diagnostic ignored "-Wformat-zero-length"
int main()
{
// code that produces a warning
}
#pragma GCC diagnostic warning "-Wformat-zero-length"
Related
So I have this code:
#include <stdio.h>
int main()
{
printf("enter character\n>>>");
char input[0];
scanf("%5s",input);
printf("%s",input);
}
that excepts 5 chars from the user. I am new to C and this one thing makes no sense to me. Why does gcc allow me to compile a program that assigns values to an array with a length of 0? Surely this is not possible? Please explain.
Your compiler ought to reject the declaration as invalid
If the expression is a constant expression, it shall have a value greater than zero. (6.7.6.2).
However, as #Joshua points out below, some compilers support this feature as an extension:
Declaring zero-length arrays is allowed in GNU C as an extension (info gcc; 6.18)
gcc -pendatic -pedantic-errors will generate an error and only warning without -pedantic-errors.
scanf() and printf() will also be undefined behavior.
C doesn't check for buffer overrun.
This bears repeating.
C doesn't check for buffer overrun.
This has been a source of bugs for a very long time; but also it's inherent in C and cannot be changed.
There are some simple cases where the compiler can detect buffer overrun (usually with optimizations enabled as well) but in the general case it cannot; nor will it generate any runtime checks. It will just do something unexpected. This is usually a security problem if you let it.
You must check yourself that you don't overrun buffers.
It seems like there is problem in scanf_s
Here is my code.
#include <stdio.h>
#include "stack.h"
int main(){
int disk;
int hanoi[3][9];
char input[3] = { 0,0,0 };
int moveDisk;
for (int i = 0; i < 9; i++) {
hanoi[0][i] = i + 1;
hanoi[1][i] = 0;
hanoi[2][i] = 0;
}
printf("Insert the number of disks(1~9): ");
scanf_s("%d", &disk);
while (input[0] != 'q') {
printf("%3c %3c %3c\n", 'A', 'B', 'C');
for (int i = 0; i < disk; i++) {
printf("%3d %3d %3d\n", hanoi[0][i], hanoi[1][i], hanoi[2][i]);
}
scanf_s("%s", &input); //getting moving disk -- LOCATION OF ERROR
}
}
I have no idea how to solve this
No doubt you tried to use scanf() in the normal way and Visual Studio reported an error instructing you to use scanf_s()? It is not a direct replacement. For all %c, %s and %[ format specifiers you must provide two arguments - the target receiving the input, and the size of target (or strictly the number of elements).
In VS2019 even at /W1 warning level, it issues a clear explanation of the problem in this case:
warning C4473: 'scanf_s' : not enough arguments passed for format string
message : placeholders and their parameters expect 2 variadic arguments, but 1 were provided
message : the missing variadic argument 2 is required by format string '%s'
message : this argument is used as a buffer size
Don't ignore the warnings, and certainly don't disable them globally (/W0).
So in this case:
scanf_s("%s", input, sizeof(input) ) ;
again more strictly:
scanf_s("%s", input, sizeof(input)/sizeof(*input) ) ;
but the latter is really only necessary for wscanf_s (wide characters). In both cases you could use the _countof() macro, but it is Microsoft specific.
scanf_s("%s", input, _countof(input) ) ;
Note also the lack of an & before input. You don't need it for an argument that is already array or pointer. That is true of scanf() too.
Whilst there are arguments for using scanf_s() over scanf() (which is intrinsically more dangerous), it can just make life difficult if you are learning from standard examples or using a different toolchain. The simpler solution is just to disable the warning, and understand that it is unsafe:
You cited the line
scanf_s("%s", &input);
There are several things wrong with this line:
You are reading a string into a character array. This is an exception to the normal pattern for scanf, in that you do not need the &.
You are using the semistandard scanf_s, instead of the normal scanf. scanf_s is supposed to be "safer", but in order for it to provide its safetiness guarantees you have to call it differently than normal scanf, too. You have to tell it the size of the array you're reading the string into. Combined with #1 above, I believe a more correct call would be scanf_s("%s", input, 3);.
For most purposes, a string of size 3 would be far too small for reading a line of input from the user. Since in this case I guess you're only reading a "line" to give yourself an opportunity to hit RETURN before the program makes another trip through its loop, I guess it's okay.
As I mentioned, scanf_s is not quite Standard, so using it is a mixed bag. Pros: 1. It's allegedly safer. 2. Some people (including perhaps your instructor) will recommend always using it for that reason. Cons: 3. It's nut fully standard (it's an optional part of the standard) meaning that not all C compilers and libraries will support it. 4. Its calling patterns are necessarily quite different than normal scanf; it is not a drop-in replacement, so confusion is likely. (I'm not saying "don't use scanf_s", but you should be aware of its somewhat dubious status.)
If you want to read a line of input from the user before continuing, and if the line might be a "q" or something else, scanf (of any variety) might not be the best choice. In particular, %s wants to read a non-whitespace string, so if you just hit the Return key, it's going to keep waiting. This might or might not be a problem for you. (Or it might not be something you need to worry about right now; you may have bigger fish to fry.)
How can I solve this problem during debugging?
Run your program step by step using the debugger. Then when you get the exception, you've found the line causing it.
Restart your program and go up to the line where the exception will occur. That is stop on that line without execution it.
Then with the debugger, you can look at all variables and try to understand if their value is what you expect.
Does this answered your question?
BTW: The compiler should at least emitted some warnings. You really should first fix those warnings. If you have no warning, make sure you have turned on all warnings in the compiler options.
I have the following program that causes a segmentation fault.
#include <stdio.h>
#include <string.h>
#include <ctype.h>
int main(int argc, char *argv[])
{
printf("TEST");
for (int k=0; k<(strlen(argv[1])); k++)
{
if (!isalpha(argv[1])) {
printf("Enter only alphabets!");
return 1;
}
}
return 0;
}
I've figured out that it is this line that is causing the problem
if (!isalpha(argv[1])) {
and replacing argv[1] with argv[1][k] solves the problem.
However, I find it rather curious that the program results in a segmentation fault without even printing TEST. I also expect the isalpha function to incorrectly check if the lower byte of the char* pointer to argv[1], but this doesn't seem to be the case. I have code to check for the number of arguments but isn't shown here for brevity.
What's happening here?
In general it is rather pointless to discuss why undefined behaviour leads to this result or the other.
But maybe it doesn't harm to try to understand why something happens even if it is outside the spec.
There are implementation of isalpha which use a simple array to lookup all possible unsigned char values. In that case the value passed as parameter is used as index into the array.
While a real character is limited to 8 bits, an integer is not.
The function takes an int as parameter. This is to allow entering EOF as well which does not fit into unsigned char.
If you pass an address like 0x7239482342 into your function this is far beyond the end of the said array and when the CPU tries to read the entry with that index it falls off the rim of the world. ;)
Calling isalpha with such an address is the place where the compiler should raise some warning about converting a pointer to an integer. Which you probably ignore...
The library might contain code that checks for valid parameters but it might also just rely on the user not passing things that shall not be passed.
printf was not flushed
the implicit conversion from pointer to integer that ought to have generated at least compile-time diagnostics for constraint violation produced a number that was out of range for isalpha. isalpha being implemented as a look-up table means that your code accessed the table out of bounds, therefore undefined behaviour.
Why you didn't get diagnostics might be in one part because of how isalpha is implemented as a macro. On my computer with Glibc 2.27-3ubuntu1, isalpha is defined as
# define isalpha(c) __isctype((c), _ISalpha)
# define __isctype(c, type) \
((*__ctype_b_loc ())[(int) (c)] & (unsigned short int) type)
the macro contains an unfortunate cast to int in it, which will silence your error!
One reason why I am posting this answer after so many others is that you didn't fix the code, it still suffers from undefined behaviour given extended characters and char being signed (which happens to be generally the case on x86-32 and x86-64).
The correct argument to give to isalpha is (unsigned char)argv[1][k]! C11 7.4:
In all cases the argument is an int, the value of which shall be representable as an unsigned char or shall equal the value of the macro EOF. If the argument has any other value, the behavior is undefined.
I find it rather curious that the program results in a segmentation fault without even printing TEST
printf doesn't print instantly, but it writes to a temporal buffer. End your string with \n if you want to flush it to actual output.
and replacing argv[1] with argv[1][k] solves the problem.
isalpha is intended to work with single characters.
First of all, a conforming compiler must give you a diagnostic message here. It is not allowed to implicitly convert from a pointer to the int parameter that isalpha expects. (It is a violation of the rules of simple assignment, 6.5.16.1.)
As for why "TEST" isn't printed, it could simply be because stdout isn't flushed. You could try adding fflush(stdout); after printf and see if this solves the issue. Alternatively add a line feed \n at the end of the string.
Otherwise, the compiler is free to re-order the execution of code as long as there are no side effects. That is, it is allowed to execute the whole loop before the printf("TEST");, as long as it prints TEST before it potentially prints "Enter only alphabets!". Such optimizations are probably not likely to happen here, but in other situations they can occur.
So I've read this question and other related questions, and I know that it's a good idea to check the return value of scanf to avoid surprises.
But I have the following scanf in my code:
/*
* If we don't gobble newlines,
* then they'll be interpreted as commands.
* Gobble both \n and \r (because the input files use 0x0D).
*/
(void) scanf("%*[\n\r]");
As you can see, I'm casting the result of scanf to void, but GCC still warns under -ansi -pedantic -Wall -O3, as noted here.
Is there anything clean I can do to suppress this warning without compiler-specific directives—that is, in pure ANSI C90?
(By clean I mean no if (scanf(...)); or similar.)
GCC 4.4 does warn in this case, but 4.7 and 4.9 do not. This lines up with the claim in that GCC bug you linked that it was fixed in 4.5. So one option is to use a version of the compiler less than five years old.
If you're stuck with GCC 4.4 or older, you can do this:
int dummy = scanf("%*[\n\r]");
(void)dummy;
This should be a familiar pattern to many C or C++ programmers.
If for some reason you object to using two statements and don't want to create an ignore() function as suggested by #pm100, you could also do this:
abs(scanf("%*[\n\r]"));
I would much prefer the (void)dummy solution though.
Finally, you could avoid scanf() altogether and do something like this (untested, not 100% sure it does what you need):
int ch;
do {
ch = getchar();
} while (ch == '\r' || ch == '\n');
ch = ungetc(ch, stdin);
assert(ch != EOF);
Obviously you'd put that all in a nice little function with a descriptive name, which is probably a good idea with the scanf() solutions too.
I have the following code :
void test(int N)
{
printf("%d", N);
}
int main(int ac, char **av)
{
test("");
return 0;
}
I have a function test that expects an integer argument, but in the main when I call the function I give a string argument and c converts it to a integer and prints it for me. But what I want is that if someone passes a string than I give an error. How to check whether the argument is a string though ?
Thanks !
void test(int N) { /* ... */ }
...
test("");
That function call is simply invalid. test requires an argument of type int, or of something that's implicitly convertible to int (any arithmetic type will do). "" is a string literal; in this context, it's converted to a char* value which points to the '\0' character which is the first (and last, and only) character of the array.
There is no implicit conversion from char* to int. A conforming compiler must issue a diagnostic for the invalid call, and it may (and IMHO should) reject it outright. It's exactly as invalid as trying to take the square root of a string literal, or add 42 to a structure.
Older versions of C (before the 1989 ANSI standard) were more lax about this kind of thing, and that laxity survives into some modern compilers. It's likely that, if your compiler doesn't reject the call, it will take the address of the string literal and convert it to an int. The result of this conversion is largely meaningless; such a compiler really isn't doing you any favors by permitting it.
If your compiler doesn't reject, or at the very least warn about, the call, you should enable whatever options are necessary to make it do so. For gcc, for example, you might try something like:
gcc -std=c99 -pedantic -Wall -Wextra filename.c
You can drop the -pedantic if you want to use gcc-specific extensions. There are several possible arguments for the -std= option. See the gcc documentation for more information -- or the documentation for whatever compiler you're using.
If you're asking about validating user input (i.e., input from someone running your program rather than writing C code), user input is not generally in the form of numbers. It's in the form of text, sequences of characters. For example, you might use the fgets() function to read a line of text from standard input. You can then, if you like, check whether that line has the form of an integer literal. One way to do that is to use the sscanf function. A quick example:
#include <stdio.h>
int main(void) {
char line[200];
int n;
printf("Enter an integer: ");
fflush(stdout);
fgets(line, sizeof line, stdin);
if (sscanf(line, "%d", &n) == 1) {
printf("You entered %d (0x%x)\n", n, (unsigned)n);
}
else {
printf("You did not enter an integer\n");
}
}
But if your question is about someone writing C code that calls a function you provide, the compiler will check that any arguments are of a valid type.
what I want is that if someone passes a string than I give an error
That's not really your problem. Most compilers will give a warning for this, I think -- presuming warnings are enabled.
The issue is that C always passes by value, but a string argument is an array, and its value is the address of the first character in the array -- a pointer, but pointer values can be treated as integers. Again, most compilers are smart enough to catch this ambiguity, if you use them properly.
You can't completely bulletproof your code against people who use it improperly. You write an API, you document it, but you don't have to cover cases for those who cannot use basic tools properly.
The core standard C library does not include checks of the sort you are looking for here, so it seems pointless to incorporate them into your API -- there are oodles of built-in standard commands with int args to which an array can be passed in the same way. Saving someone from doing something stupid with your library won't save them from doing the exact same thing with the base C lib -- i.e., you can't stop them from passing pointers in place of ints. Period.
The naive approach is something like this:
int is_integer( const char *s )
{
if( *s == '-' || *s == '+' ) s++;
while( isdigit(*s) ) s++;
return *s == 0;
}
That will tell you if all characters are digits, with an optional sign. It's not particularly robust, however. It can't handle whitespace, and it doesn't check the integer is in the valid range.
However, it might be enough for your needs.
Example:
int main( int argc, char **argv )
{
int val;
if( argc <= 1 || !is_integer(argv[1]) ) {
fprintf( stderr, "Syntax: %s val\n\nWhere val is an integer\n", argv[0] );
return 1;
}
val = strtol( argv[1], NULL, 10 );
test(val);
return 0;
}
Compile with -Wall and -Werror and your problem will magically go away.
gcc -Wall -Werror file.c