Why should I specify a data type in C every time?

As you can see from the code snippet below, I declared one char variable and one int variable. When the code compiles, it must identify the data types of the str and i variables.

Why do I need to indicate again during the scan of my variable that it is a string or an integer variable, pointing %s or %d to scanf ? Isn't the compiler mature enough to determine when I declared my variables?

 #include <stdio.h> int main () { char str [80]; int i; printf ("Enter your family name: "); scanf ("%s",str); printf ("Enter your age: "); scanf ("%d",&i); return 0; } 
+64
c compiler-construction printf scanf
Aug 13 '13 at 7:57 on
source share
11 answers

Since there is no portable way for variable arguments, such as scanf and printf , to know the types of variable arguments, not even how many arguments are passed.

See C FAQ: How do I find out how many arguments a function called with?




This is why there must be at least one fixed argument to determine the number and possibly types of variable arguments. And this argument (the standard calls it parmN , see C11 ( ISO / IEC 9899: 201x ) §7.16 Variable Arguments) plays this special role and will be passed to the va_start macro. In other words, you cannot have a function with a prototype like this in standard C:

 void foo(...); 
+120
Aug 13 '13 at 8:05
source share

The reason the compiler cannot provide the necessary information is simply because the compiler is not involved. The prototype of functions does not define types, since these functions have variable types. Thus, the actual data types are not determined at compile time, but at run time. The function then takes one argument from the stack, after another. These values ​​do not have any type information associated with it, therefore the only way the function knows how to interpret the data is to use the information provided by the caller, which is a format string.

the functions themselves do not know what data types are passed, and do not know the number of arguments passed, so there is no way that printf can solve this on its own.

In C ++, you can use operator overloading, but this is a completely different mechanism. Because here the compiler selects the appropriate function based on the data types and the available overloaded function.

To illustrate this, printf , when compiled, looks like this:

  push value1 ... push valueN push format_string call _printf 

And the prototype of printf is this:

 int printf ( const char * format, ... ); 

Thus, type information is not transferred except for what is indicated in the format string.

+29
Aug 13 '13 at 8:23
source share

The compiler may be smart, but the printf or scanf functions are stupid - they don’t know what type of parameter you pass for each call. This is why you need to pass %s or %d every time.

+13
Aug 13 '13 at 8:00
source share

printf not a built-in function . It is not part of the C language as such. The whole compiler makes the code to call printf , passing all the parameters. Now, since C does not provide reflection as a mechanism for determining type information at runtime, the programmer must explicitly provide the necessary information.

+13
Aug 13 '13 at 8:06 on
source share

The first parameter is a format string. If you print a decimal number, it might look like this:

  • "%d" (decimal)
  • "%5d" (decimal number with a width of 5 with spaces)
  • "%05d" (decimal number with a width of 5 with zeros)
  • "%+d" (decimal, always signed)
  • "Value: %d\n" (some content before / after the number)

etc., see, for example, Format placeholders on Wikipedia to have an idea of ​​which lines of lines can contain.

There may also be several parameters:

"%s - %d" (string, then some content, then a number)

+10
Aug 13 '13 at 8:06 on
source share

Didn't the compiler ripen to determine when I declared the variable?

No.

You are using the language specified several decades ago. Don't expect a modern design aesthetic from C, because it's not a modern language. Modern languages ​​will tend to trade some efficiency in compilation, interpretation or execution to improve usability or clarity. C is from the time when computer processing time was expensive and had a limited supply, and its design reflects this.

In addition, why C and C ++ remain preferable for you when you really care about fast, efficient or close to metal.

+8
Aug 13 '13 at 9:41
source share

scanf , since the prototype is int scanf ( const char * format, ... ); says that the data is stored in accordance with the format of the parameters in the places indicated by the optional arguments.

This is not related to the compiler, it's all about the syntax defined for scanf . The file format is required so that scanf knows about the size for backup for data entry.

+4
Aug 13 '13 at 8:05
source share

GCC (and possibly other C compilers) keep track of argument types, at least in some situations. But the language is not designed that way.

The printf function is a regular function that takes variable arguments. Variable arguments require some kind of runtime type identification scheme, but in C, the values ​​do not contain information about the type of runtime. (Of course, C programmers can create I / O circuits using structures or bit manipulation tricks, but they are not integrated into the language.)

When we develop such a function:

 void foo(int a, int b, ...); 

we can pass "any" number of additional arguments after the second, and it is up to us to decide how many there are and what their types are, using some protocol that is outside the function transfer mechanism.

For example, if we call this function as follows:

 foo(1, 2, 3.0); foo(1, 2, "abc"); 

there is no way that the caller can distinguish between cases. There are only a few bits in the parameter transfer area, and we don’t know whether they represent a pointer to character data or a floating point number.

The possibilities for transmitting this type of information are numerous. For example, in POSIX, the exec family of functions uses variable arguments of the same type, char * , and a null pointer is used to indicate the end of the list:

 #include <stdarg.h> void my_exec(char *progname, ...) { va_list variable_args; va_start (variable_args, progname); for (;;) { char *arg = va_arg(variable_args, char *); if (arg == 0) break; /* process arg */ } va_end(variable_args); /*...*/ } 

If the caller forgets to pass the null pointer terminator, the behavior will be undefined because the function will continue to reference va_arg after it destroys all the arguments. Our my_exec function should be called as follows:

 my_exec("foo", "bar", "xyzzy", (char *) 0); 

Casting to 0 is required because there is no context for interpreting it as a null pointer constant: the compiler does not know that the intended type for this argument is a pointer type. Also, (void *) 0 incorrect because it will just be passed as a void * type, not a char * , although the two are almost certainly binary compatible, so it will work in practice. A common mistake with this type of exec function is the following:

 my_exec("foo", "bar", "xyzzy", NULL); 

where the NULL compiler NULL defined as 0 without quotes (void *) .

Another possible scheme is to require the caller to move a number that indicates how many arguments exist. Of course, this number may be incorrect.

In the case of printf the format string describes a list of arguments. The function parses it and extracts the arguments accordingly.

As mentioned earlier, some compilers, in particular the GNU C compiler, can parse format strings at compile time and perform a static type check on the number and type of arguments.

However, note that the format string may be different from the literal and may be calculated at startup time, which is immune to type checking schemes. Dummy example:

 char *fmt_string = message_lookup(current_language, message_code); /* no type checking from gcc in this case: fmt_string could have four conversion specifiers, or ones not matching the types of arg1, arg2, arg3, without generating any diagnostic. */ snprintf(buffer, sizeof buffer, fmt_string, arg1, arg2, arg3); 
+4
Aug 13 '13 at 18:55
source share

This is because it is the only way to tell functions (for example, printf scanf ) what type of value you are passing. eg -

 int main() { int i=22; printf("%c",i); return 0; } 

this code will print the character not an integer 22. because you told the printf function to treat the variable as char.

+2
Aug 14 '13 at 6:05
source share

printf and scanf are I / O functions that are designed and defined in such a way as to get a control string and a list of arguments.

The functions do not know the type of the parameter passed to it, and the compiler also cannot pass this information to it.

0
Aug 13 '13 at 8:07
source share

Because in printf you do not specify the data type, you specify the data format. This is an important difference in any language, and it is doubly important in C.

When you look at a string using %s , you do not say: "Parse string input for my string variable." You cannot say this in C because C is not of string type. The closest thing C to the string variable is an array of fixed-size characters that contains characters representing the string, with the termination of the string indicated by a null character. So, what you actually say is: “Here is an array for storing a string, I promise that it is large enough to input the string that I want you to parse.”

Primitive? Sure. C was invented more than 40 years ago when a typical machine had no more than 64 KB of RAM. In such an environment, saving RAM was a higher priority than complex string processing.

However, the %s scanner is stored in more complex programming environments where there are string data types. Because it concerns scanning, not input.

0
Aug 15 '13 at 22:34
source share



All Articles