-1

I had some confusion about %s in c. I am new to programming and I am learning c as my fist P.L.

How does %s works in c. I had found that you don't need to use & in scanf while using %s. Why is this so? Why %s gives segmentation fault when I use

char *a="how are you";
printf("%s",*a);

I had read this link:- Using %s in C correctly - very basic level but I want the answers of questions that are in bold.

SiggiSv
  • 1,219
  • 1
  • 10
  • 20
  • Time for you to dust off your copy of K & R and read it. – Bathsheba Jul 20 '17 at 12:57
  • The question you link to has the answer you seek. – Chris Turner Jul 20 '17 at 12:59
  • When you use `scanf` you need to provide the __pointer__ to the variable. That's why you use the `&` sign, `&x` meaning "address of x". Now when you scanf a _string_ like in `char string[100]; ... scanf("%s", s);`, `s` __is__ already the pointer to the string. – Jabberwocky Jul 20 '17 at 13:00
  • Try to understand why the following works instead, and everything will be clearer: `printf("%s",&(*a));` – Davide Spataro Jul 20 '17 at 13:02
  • It works because C is internally inconsistent in the way it handles arrays vs. other types. Dennis R. was drunk when defining how to pass arrays as arguments. – Martin James Jul 20 '17 at 13:08
  • Learn about pointers. Any book will help you with. SO is not a learning site – 0___________ Jul 20 '17 at 13:23
  • Take the compiler's warning serious. Read them and learn to understand them. – alk Jul 22 '17 at 07:54

3 Answers3

4

You need to understand the difference between a pointer and a char variable.

In scanf() you usually pass an array to match a %s specifier, then a pointer to the first element of the array is used in it's place. For other specifiers like %d you need to pass the address of the target variable to allow scanf() to store the result in it.

In contrast, printf() does not store results anywhere, so it simply needs the value of the variable except for the "%s" specifier where it needs the address to the first element of an array of non '\0' char values terminated by a '\0'.

In your example, you are passing a char variable, printf() will take it as an address and attempt to dereference it which will cause a Segmentation Fault, because it expects the variable to be the address of an array filled with chars and terminated with a ' 0'.


Note: printf("%s", *a); is equivalent to printf("%s", a[0]); so you can see clearly that the variable passed for "%s" is of type char and printf() will do something like *(char *) 'h' using (char *) 'h' as a valid pointer, but is most likely NOT.

Iharob Al Asimi
  • 52,653
  • 6
  • 59
  • 97
2

Statement "you dont need to use & in scanf while using %s" might be misleading. A scanf format specifier that is about reading in and storing a value always requires a pointer to an object of the respective type. This is usually achieved by unary &-operator, which returns the address of an object. Yet some types like arrays automatically decay to a pointer to the first element of the array, such that the following code actually passes a pointer:

char text[100] = "";
scanf("%s", text);  // array automatically decays to a pointer to the first element.
scanf("%s", &text[0]);  // explicitly take the address of the first element

If you do not have an array but a pointer to char, the code changes as follows:

char *text2 = malloc(100);
scanf("%s", text2);  // text2 is a pointer, not an array 

Again, you do not need an &, because you already pass the pointer value.

So statement "you dont need to use & in scanf while using %s" is true as %s always requires a pointer to a sequence of characters, and either you pass an array which automatically decays to a pointer or you pass a pointer directly.

Your code is wrong from two perspectives:

char *a="how are you";
printf("%s",*a);

First, *a is not a pointer but a single character value ('h' in your case), which - when converted to a pointer value - will be something like memory address 0x0000001A. Accessing this yields undefined behaviour / seg fault.

Second, it's a printf, not a scanf. If you want a printf, write:

char *a="how are you";
printf("%s",a);

If you want a scanf, write:

char a[100];;
scanf("%s",a);
Stephan Lechner
  • 34,891
  • 4
  • 35
  • 58
  • What happens when I use `printf("%s, *test);` where `char *a="how are you";`.I am keep getting segmentation fault. –  Jul 20 '17 at 13:17
2

The scanf() function expects a pointer the first element of a character array as an argument with the %s directive. Since an array identifier decays to a pointer to the first element of the array (in most expressions, including function calls), simply using the array name without the & address operator provides the correct pointer. In fact, using the & operator here is wrong.

Given the code:

char array[100];
int ret_val = scanf("%99s", array);

Here the identifier array decays to a pointer to the first element of array[], which is what scanf() expects. If instead, this:

int ret_val = scanf("%99s", &array);

This is incorrect, since &array is a pointer to the array array[]. The difference is, array decays to a pointer to one byte, while &array is a pointer to 100 bytes (in this case). This difference matters, for example, when pointer arithmetic is involved.

In your second example:

char *a = "how are you";
printf("%s", *a);

Here, a is already a pointer to the first element of an array (since a is a pointer to char and "how are you" is a string literal, represented in memory as a null terminated array of char, which decays to a pointer to the first element of this array in the expression). By using *a instead of a, the value 'h' is given as an argument to printf() instead of the expected pointer, leading to undefined behavior.

Also, note that in my first example, the return value of scanf() is stored in the int variable ret_val. scanf() returns the number of successful assignments made, and this value should be checked in robust code. Further, note that a maximum width should always be specified when using %s with scanf() to avoid buffer overflow. Here, %99s is used, leaving space for the null terminator (\0) at the end of the array.

ad absurdum
  • 19,498
  • 5
  • 37
  • 60