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
Related
I'm writing in C and I need to read everything that arrives in input but I don't know how many characters I will receive. I wrote
while (scanf("%c", &read) != NULL)
but the compiler tells me: [Warning] comparison between pointer and integer, so what should I write instead?
Instead, of scanf("%c", &read) you may consider read = getc(stdin).
Please, be aware that getc()/fgetc() return int.
This allows to store any character as number in range [0, 255] as well as to return EOF (usually -1) in case of failure.
So, with getc() it would look like:
int read;
while ((read = getc(stdin)) != EOF)
Note:
You may assign read to a variable of type char – it will be implicitly converted. In case of getc() succeeded, there shouldn't be any data loss in this implicit conversion.
A little sample to show this at work:
#include <stdio.h>
int main(void)
{
enum { N = 10 };
char buffer[N];
/* read characters and print if buffer ful */
int read, n = 0;
while ((read = getc(stdin)) != EOF) {
if (n == N) {
printf("%.*s", N, buffer); n = 0;
}
buffer[n++] = read;
}
/* print rest if buffer not empty */
if (n > 0) printf("%.*s", n, buffer);
/* done */
return 0;
}
Note:
The read characters are stored in buffer without a termination '\0'. This is handled in printf() respectively by the formatter %.*s meaning string with max. width * where width and string are read as consecutive arguments.
Live Demo on ideone
Your
scanf("%c", &read) != NULL
is a typing mistake : the types of left and right operands to != don't match. Read about type systems.
Read the documentation of scanf. It says that scanf returns some int value.
But NULL is a pointer value (it is (void*)0).
How can a pointer be meaningfully compared to an int? On my Debian/x86-64, they don't even have the same size (as returned by sizeof): a pointer takes 8 bytes but an int takes 4 bytes.
So the compiler is right in warning you.
You probably want to code instead something like while (scanf("%c", &read) >0) or even while (scanf("%c", &read) == 1) since for a successful scan of "%c" the scanf function is documented to give 1.
Of course, in that particular case, using fgetc is better (more readable, and probably slightly faster). Read documentation of fgetc and follow the advice given by Sheff's answer.
Next time:
be sure to read the documentation of every function that you are using. Never use a function whose documentation you have not read and understood.
ask the compiler to give all warnings and debug info, so with GCC compile with gcc -Wall -Wextra -g. Trust your compiler, and improve your code to get no warnings at all.
read How To Debug Small Programs.
read the documentation of gdb (and perhaps of valgrind).
study, for inspiration, the source code of some small free software similar to yours (perhaps on github).
PS. Naming your variable read is poor taste. For most people it conflicts with the POSIX read function.
I am working on creating a project that has a custom header file, and ran into some problems:
I'm not exactly sure what happened to this code, but after I started using the fgets() function, it seems to have stopped the output of some printf()'s in my code. Here are the important parts of the code, probably missing some #include statements here:
main.cpp
#include "ichat.h"
int main() {
prompts(P_IDLE, response);
prompts(P_USERNAME, response);
}
ichat.cpp
#include "ichat.h"
int prompts(int p_tag, char *response) {
if (p_tag == P_IDLE) {
printf("\nWaiting for connections. Press [ENTER] to start a chat.");
char msg[50];
char *ptr;
while (true) {
fgets(msg, strlen(msg), stdin);
ptr = (char*)memchr(msg, '\n', strlen(msg));
if (ptr != NULL) {
break;
}
} else if (p_tag == P_USERNAME) {
printf("\nPlease enter a username you'd like to use: ");
....
} ...
some of ichat.h, incase you were curious...
#define P_IDLE 0
#define P_USERNAME 1
compiled with gcc main.cpp ichat.cpp.
The issue is that when adding in the while loop with the fgets() function, none of the printf() functions produce output in the command line. I'm not exactly sure what is happening here, because those print functions were working, and sometimes they work if I remove the \n, or if I add another printf() function right before them. It's pretty confusing...
Is there a better way to get input from the user? I'm open to any hints, critiques, and ideas about any part of my code.
The nub of your problem is
char msg[50];
/* no initialisation of data in msg */
fgets(msg, strlen(msg), stdin);
msg is uninitialised, so the values of any of the characters in it are indeterminate - they are not guaranteed to be zero. Formally, even accessing their values (which strlen() does to search for a '\0') gives undefined behaviour.
What you need to do is change strlen(msg) to sizeof msg (which will consistently give the value of 50).
fgets() will modify msg based on input. Then you can use strlen() on the result.
Also: try not to use (char *) type conversions on the result of functions like memchr() that return void *. It is only needed in C if you have forgotten to #include <string.h>. Including the right header is the better solution than the type conversion. If your C compiler is actually a C++ compiler, the conversion will be needed, but C++ provides alternative approaches that are often preferable (more type safe, etc).
I have this issue where gcc using the Werror argument, comes up with the error 'ignoring return value of scanf'. Now, I checked the forums and I did do stuff such as putting it in an if statement which seemed to work fine, but the thing is, I do not get the error if I were to compile it at my University.
#include <stdio.h>
#include <stdlib.h>
#define EXAMPLE1 5
int main (int argc, char* argv[]) {
int example;
scanf ("%d", &example);
if (example <= EXAMPLE1) {
printf ("Woohoo\n");
} else {
printf ("Oohoow\n");
}
return EXIT_SUCCESS;
}
So this code for example would compile fine using gcc -Wall -Werror -O -o namehere namehere.c at my uni, but if I were to use it at home, the aforementioned error comes up. My uni is using gcc 4.9.2. I tried it at home on gcc 4.8.4, 4.8.5, 4.9.3.
Ignoring the return value of scanf() is not good because it means you are not checking for possible errors in input. Though some compilers or settings don't give warnings for that, you shouldn't ignore return values of scanf().
Try this:
#include <stdio.h>
#include <stdlib.h>
#define EXAMPLE1 5
int main (int argc, char* argv[]) {
int example;
if (scanf ("%d", &example) != 1) {
printf ("Read error\n");
}
else if (example <= EXAMPLE1) {
printf ("Woohoo\n");
} else {
printf ("Oohoow\n");
}
return 0;
}
You are not getting an error, the compiler is so friendly to give you a warning. And correctly! Upon erroneous input example will have a random value.
Always check that the return value of scanf matches the number of arguments you want to read.
scanf ("%d", &example);
This tells scanf to read a sequence of decimal digit characters (possibly with a prefixed negative sign) up to the first non-decimal character, and convert that sequence into an int value that gets stored into example.
What if the user enters something that is entirely non-decimal, or if stdin is closed? The scanf call would fail, indicating such a failure using a corresponding return value... but what would the value of example be in such a situation?
You need to check the return value to avoid using example when it contains garbage. If you try to use that variable, the behaviour of your program may be erratic. This might include segfaults.
Your compiler is within its rights to refuse to compile this code, because using an uninitialised (or otherwise indeterminate) variable is undefined behaviour.
In summary, it's well within your best interests to always check the return value because:
Some compilers might refuse to compile code that doesn't have such checks, as you've noticed.
Those that do compile your code will allow your program to behave erratically, using garbage when the user enters garbage or in some cases, possibly segfaulting due to use of a trap representation.
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"
In my program, I use sscanf to check whether a string is of a given format. To do so, I provide the number of arguments in the format string and check whether sscanf returns that same number when parsing the input.
As part of a primitive parser, I want to check whether a string matches one of many formats. The sscanf function is variadic, so how do I deal with the varying number of arguments I need to pass?
Currently, I just pass a very large number of arguments (e.g. 50) to the function, and just hope that the format strings don't contain more arguments.
Is there any better way to do this?
You really need something heavier than scanf. You have to tell scanf what format your input is in; it can't figure anything out on its own.
If you have access to POSIX, look at regex.h it's probably everything you need.
Otherwise, you're stuck rolling your own. lex and yacc are nice if the format is rather complex, but otherwise, either strtok or (getchar+switch) is probably the way to go.
Edit:
Since you can use POSIX, here's an simple example of how to extract data from a regex in c. (error checking excluded for brevity.)
char txt[] = "232343341235898dfsfgs/.f";
regex_t reg;
regmatch_t refs[MAX_REFS]; //as in, the maximum number of data you want to extract
regcomp(®, "3433\\([0-5]*\\).*", 0); //replace 0 with REG_EXTENDED if desired
regexec(®, txt, MAX_REFS, refs, 0);
regfree(®);
txt[refs[0].rm_eo+1] = '\0';
int n = atoi(txt+refs[0].rm_so);
printf("%d\n", n);
Prints
41235
You should probably use lex/yacc to build a proper parser. Alternatively, first tokenizing the string with strtok might simplify your problem. (Beware: It is really tricky to use strtok correctly -- read its documentation very carefully.)
I'm not sure it answers your question, but you use varargs in C to allow a variable number of arguments to a function.
void myscanf(const char *fmt, ...)
{
}
The unhelpful answer is "don't do that, write a parser properly, maybe using lex and/or yacc or bison".
The answer to the question you asked is "yes, you could do that". I don't believe there's any reason why there can't be more variadic parameters than the format requires, although to few would be a bad thing. I'm presuming that you have an array or list of possible formats and you're calling sscanf in a loop.
You can write a validation function using the variable length arguments using the macros available in stdarg.h.
For example,
int my_validation_func(const char *format, ...) {
va_list ap;
char *p, *sval;
int ival;
float fval;
va_start(ap, format);
for(p=format; *p ; p++) {
if (*p != '%') {
continue;
}
switch(*++p) {
case 'd':
ival = va_arg(ap, int);
break;
case 'f':
fval = va_arg(ap, float);
break;
case 's':
for (sval = va_arg(ap, char *); *sval; sval++);
break;
default:
break;
}
}
va_end(ap);
}
Hope this helps!
If you don't know when you're writing the code the number and type(s) of the arguments, sscanf() cannot safely do what you're trying to do.
Passing 50 arguments to sscanf() is ok (arguments not consumed by the format string are evaluated but otherwise ignored), but the arguments that correspond to the format string have to be of the expected type, after promotion; otherwise, the behavior is undefined. So if you want to detect whether a string can be scanned with either "%d" or "%f", you can't safely do it with a single sscanf() call. (It's likely you could get away with passing a void* that points to a sufficiently large buffer, but the behavior is still undefined.)
Another nasty problem with sscanf() is that it doesn't handle numeric overflow. This:
char *s = "9999999999999999999999999";
int n;
int result = sscanf(s, "%d", &n);
printf("result = %d, n = %d\n", result, n);
has undefined behavior (assuming 9999999999999999999999999 is too big to be stored in an int).
Something you might be able to do is find an open-source sscanf implementation and modify it so it just verifies the string against the format, without storing anything. (Dealing with the license for the implementation is left as an exercise.) This makes sense if you find sscanf-style format strings particularly convenient for your problem. Otherwise, regular expressions are probably the way to go (not in the C standard, but it's easy enough to find an implementation).