简体   繁体   中英

Scanf a char in C with an example

I was doing a tutorial at Cplusplus

There was this example:

/* scanf example */
#include <stdio.h>

int main ()
{
  char str [80];
  int i;

  printf ("Enter your family name: ");
  scanf ("%79s",str);  
  printf ("Enter your age: ");
  scanf ("%d",&i);
  printf ("Mr. %s , %d years old.\n",str,i);
  printf ("Enter a hexadecimal number: ");
  scanf ("%x",&i);
  printf ("You have entered %#x (%d).\n",i,i);

  return 0;
}

I tried it on my Macbook and when I do "make example" I get this error;

example.c: In function ‘main’:
example.c:25: warning: format ‘%79s’ expects type ‘char *’, but argument 2 has type ‘char (*)[80]’

When I do "valgrind ./example" I get this message;

==1326== Memcheck, a memory error detector
==1326== Copyright (C) 2002-2010, and GNU GPL'd, by Julian Seward et al.
==1326== Using Valgrind-3.6.1 and LibVEX; rerun with -h for copyright info
==1326== Command: ./example
==1326== 
--1326-- ./example:
--1326-- dSYM directory is missing; consider using --dsymutil=yes
Enter your family name: Park
Enter your age: 20
Mr. Park, 20 years old.
==1326== 
==1326== HEAP SUMMARY:
==1326==     in use at exit: 8,280 bytes in 3 blocks
==1326==   total heap usage: 3 allocs, 0 frees, 8,280 bytes allocated
==1326== 
==1326== LEAK SUMMARY:
==1326==    definitely lost: 0 bytes in 0 blocks
==1326==    indirectly lost: 0 bytes in 0 blocks
==1326==      possibly lost: 0 bytes in 0 blocks
==1326==    still reachable: 8,280 bytes in 3 blocks
==1326==         suppressed: 0 bytes in 0 blocks
==1326== Rerun with --leak-check=full to see details of leaked memory
==1326== 
==1326== For counts of detected and suppressed errors, rerun with: -v
==1326== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0)

Question 1. The "make example" gives an error but the program does what it should do. Then what is the error here?

Question 2 I did little research and I learned that you can't assign a char in C since a char is not mutable. That is what a char differs from int or float or double. If so, how could I have assigned str here?

Question 3 What is the Char array? I read several articles but I don't get how it works here!!!

char str [80];

means str can take up to 80 bytes, right? Then what is

scanf ("%79s",str);  

?

Thank you so much for your help guys and happy 2014! :)

This:

scanf ("%79s", str);

means "read a string ( s ) but don't use more than 79 characters of space starting from the pointer I've given you", sort of. It's a way to prevent buffer overflow when reading strings. It's using 79 rather than 80 to leave room for the string terminator.

The documentation says:

String input conversions store a terminating null byte ( '\\0' ) to mark the end of the input; the maximum field width does not include this terminator.

I was doing a tutorial at Cplusplus

Please don't. That site is full of bad, incorrect and/or misleading examples. Do yourself a favor and choose another site.

I tried it on my Macbook and when I do "make example" I get this error;

Probably you misspelled the source code and you wrote &str instead of str (I think it's a bad habit: throwing in the address-of operator without thinking when you encounter scanf() ). That results in a pointer-to-array whereas the conversion specifier expects a pointer-to-char.

The "make example" gives an error but the program does what it should do. Then what is the error here?

The program does not do what it should. Instead, it invokes undefined behavior, and it pretends to work correctly.

I did little research and I learned that you can't assign a char in C since a char is not mutable

Wrong.

What is the Char array?

The char array in your example is str . It's just that: an array of 80 chars. Unfortunately, arrays have some bizarre properties in C, such as implicitly decaying into pointers when passed to a function. That is the reason scanf() wants you to pass a pointer and not an array argument: when you write

scanf("format string", str);

then what is actually passed is a pointer to the first element in str (aka &str[0] ).


Also, if you accept one more piece of good advice: don't use scanf() . It's dangerous, its usage is counter-intuitive, and it doesn't do any good in general. For accepting user input, consider using fgets() instead.

The error

example.c:25: warning: format ‘%79s’ expects type ‘char *’, but argument 2 has type ‘char (*)[80]’

only makes sense if you had written something like

scanf ("%79s", &str); // wrong

instead of

scanf ("%79s",str);  // right

Is the code you posted what you're actually compiling?

As to your specific questions:

Question 1. The "make example" gives an error but the program does what it should do. Then what is the error here?

First, a little background: except when it is the operand of the sizeof or unary & operators, or is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T " will be converted ("decay") to an expression of type "pointer to T ", and the value of the expression will be the address of the first element of the array.

When you write

scanf("%79s", str);

the expression str has type "80-element array of char "; since str is not the operand of the sizeof or unary & operators, it is converted to an expression of type "pointer to char ", or char * , and its value is the address of the first element in the array.

If you had written

scanf("%79s", &str);

the expression str is the operand of the unary & operator, so the conversion doesn't happen; instead, the type of the expression &str is "pointer to 80-element array of char ", or char (*)[80] (look familiar?) and the value of the expression is the address of the array.

Now, it just so happens that the address of an array and the address of the first element of an array are the same; the expressions str and &str will yield the same value , but the types of the two expressions are different.

So, assuming you had written scanf("%79s, &str) , the code will still "work" (the value of &str is the same as str ), but since the type isn't want scanf expects for the %s conversion specifier, the compiler emits a warning.

Question 2 I did little research and I learned that you can't assign a char in C since a char is not mutable. That is what a char differs from int or float or double. If so, how could I have assigned str here?

Either you've misunderstood, or you're reading a very bad reference. Strings in C are not immutable; attempting to modify the contents of a string literal invokes undefined behavior (some platforms will throw an exception, some won't), but that's not the same as saying they are immutable in the same way Java strings are immutable.

Strings in C are stored as arrays of char . You can update string contents by using the strcpy library function or by modifying the array elements directly. For example:

char str[80];
...
strcpy( str, "a string" );

copies the contents of the string literal "a string" to str . You can certainly update this string, such as

str[0] = 'A'; // str now contains "A string"

or append another string to it:

strcat( str, " with more stuff" ); // str now contains "A string with more stuff"

String literals like "a string" are stored as arrays of char such that their storage is allocated when the program starts and held until the program exits, and they are visible over the entire program. Trying to modify the contents of a string literal invokes undefined behavior; it may work, it may not, the compiler may throw an error, you may get an error at runtime. So you don't want to do something like

char *p = "This is a test";
strcpy( p, "This is another test" );
Question 3 What is the Char array? I read several articles but I don't get how it works here!!!
 char str [80]; 
means str can take up to 80 bytes, right?

It takes up 80 char s; it's usually true that a char maps to a native 8-bit byte, but there are exceptions (mostly either very old architectures or oddball embedded systems).

Then what is
 scanf ("%79s",str); 
?

In that call, all scanf receives is a pointer to the first element of the target buffer; it has no idea how big the target buffer actually is. If you wrote

scanf("%s", str);

and the user typed in a string that was more than 80 characters long, scanf would happily store those extra characters to the memory immediately following the end of str , potentially clobbering something important and leading to a crash (or worse, a malware exploit). The %79s directs scanf to only read up to 79 characters from the input stream. Why 79 instead of 80? You need to leave one element available for the 0 terminator (in C, a string is a sequence of characters terminated by a 0-valued byte).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM