8

When we usually input the string, we do this:

#include <stdio.h>
int main()
{
    char str[256];
    scanf("%s",str);
    //Other Operation
}

But, today, in programming class, one of my friends wrote scanf line like this:

scanf("%s",&str);

and it pass the compilation, and works.

The question is, I'd like to know if this is "legal" in C or not, or just an undefined behavior?

Makoto
  • 104,088
  • 27
  • 192
  • 230
Sarun Intaralawan
  • 1,092
  • 3
  • 13
  • 30
  • 2
    [A good enough compiler can warn you that the expected type of the `scanf()` argument does not match what is being passed in.](http://ideone.com/6xjGS5) – jxh Aug 13 '13 at 18:42

3 Answers3

6

It's undefined behavior (because the type scanf() expects is char *, but you pass in a char (*)[256]), but it usually "works" (appears to be working) since the address of an array is often the same (regarding the numeric value of the pointer) as the address of its first element.

From the official documentation:

If this object does not have an appropriate type, or if the result of the conversion cannot be represented in the space provided, the behavior is undefined.

(emphasis mine)

  • 2
    When isn't it the first element's address? – BlackVegetable Aug 13 '13 at 16:45
  • 3
    Doesn't C define the address of the array to be the address of it's first element? – sfstewman Aug 13 '13 at 16:45
  • @sfstewman I'm not sure (but as far as I know, it needn't be), but it's still UB because of the type mismatch. –  Aug 13 '13 at 16:47
  • @BlackVegetable AFAIK whenever the implementation opts to implement it like so. –  Aug 13 '13 at 16:48
  • @sfstewman: [I suggest reading over this question and answer](http://stackoverflow.com/questions/2528318/how-come-an-arrays-address-is-equal-to-its-value-in-c). (which yes, I had to google this because I thought the same as you originally). – Cornstalks Aug 13 '13 at 16:50
  • @H2CO3 Just for clarification, the array name is the pointer pointing to the first element, right? And the `&arrayname` is what? – Sarun Intaralawan Aug 13 '13 at 16:54
  • 2
    @SarunIntaralawan No, the array is the array. It's not a pointer. It **decays into** a pointer, though. And `&array` is a pointer to the array. –  Aug 13 '13 at 16:55
  • @H2CO3: Sure. I just came across [this](http://c-faq.com/aryptr/aryptrequiv.html), which suggests that arrays are evaluated differently when using unary `&`. Does this mean that `&array` may not yield the same value/address as `array`? – Cornstalks Aug 13 '13 at 17:00
  • @EricPostpischil The standard requires a cast, but that's not the same thing as asking if the comparison is meaningful. The standard, quite cheerfully, allows both pointers to be cast to, coincidentally, `char*` and compared. – sfstewman Aug 13 '13 at 17:11
  • Found it. C99 standard 6.5.2.1 (Array subscripting), paragraph 2: "Because of the conversion rules that apply to the binary + operator, if E1 is an array object (equivalently, a pointer to the initial element of an array object) and E2 is an integer, E1[E2] designates the E2-th element of E1 (counting from zero)." How am I wrong in reading this as defining a pointer to an array as be coincident with a pointer to the first element of an array? – sfstewman Aug 13 '13 at 17:13
  • 1
    @sfstewman: I don't see anywhere in that quote that mentions a pointer to an array. – Cornstalks Aug 13 '13 at 17:14
  • Wouldn't it decay into the proper pointer type (char *) before becoming undefined behavior ? – hdante Aug 13 '13 at 17:18
  • 1
    @sfstewman That quote is about the equivalence of `*(p + i)` and `p[i]`, and **not** about `arr` and `&arr`. –  Aug 13 '13 at 17:19
  • @hdante No, if you have a pointer, it doesn't decay to anything further. Only an array does. –  Aug 13 '13 at 17:21
  • 3
    `str`, after the implicit conversion, is of type `char*`, which is what's required for `scanf` with a `"%s"` format. `&str` is of type `char(*)[256]`, or pointer to 256-element array of `char`. Both expressions refer to the same memory address, but they are of different types. Either is likely to "work" on a system that uses the same representation for all pointer types (unless an optimizing compiler does something funny, which is always a possibility). `scanf("%s", &str)` is exactly as undefined, and for exactly the same reasons, as `scanf("%s", (double*)&str[0])`. – Keith Thompson Aug 13 '13 at 17:29
  • @KeithThompson Sorry, is this comment directed towards me? Basically this is exactly what I was trying to say. –  Aug 13 '13 at 17:35
  • 1
    @H2CO3: Yes, I was agreeing with and expanding on what you wrote. (BTW, I just noticed you linked to the POSIX definition of `fscanf`; the ISO C standard is more "official", though since it's a single PDF for the whole standard it's harder to link to a single section.) – Keith Thompson Aug 13 '13 at 18:18
2

It is technically undefined behaviour. However, both methods work in practice with char arrays because the reference to str being passed to scanf() turns into a pointer to the first element, which will be equal* to a pointer to the array itself (the address of str, or &str), so the same value gets passed either way.

I'm not sure whether you've only been working with strings so far, but bear in mind that if you look at something that's not an array, it's easier to tell that your friend's method would be correct:

int myInt;
scanf("%d", &myInt);

scanf() is looking for a pointer, so you want to give it the address of your integer variable. This is because passing myInt gives it a value (currently garbage), whereas &myInt tells it where to put the value that it reads.

*Except on Win16.

Michelle
  • 2,830
  • 26
  • 33
  • 1
    No, the friend's method is **wrong.** Passing the address pf the array **is undefied behavior.** –  Aug 13 '13 at 16:53
  • 4
    @H2CO3 You're welcome to downvote, shout in the comments, and/or suggest improvements where I'm incorrect, but if you're going to edit my post, please stick to "clarifying meaning without changing it". I've edited my post with corrections/clarifications. – Michelle Aug 13 '13 at 17:01
  • @Michelle Sorry, I was intending to roll back, but you beat me. – 1'' Aug 13 '13 at 17:05
  • @Michelle Still not good (enough). First, [pointers are not arrays](http://c-faq.com/aryptr/aryptr2.html), they only **decay into** one. Also, OP specifically asked if it is UB which you failed to answer (with "yes", obviously) - "it will always work" is not only misleading but incorrect. –  Aug 13 '13 at 17:11
  • @H2CO3 I'd be fascinated to see an example where it doesn't work. – Michelle Aug 13 '13 at 17:12
  • 2
    @Michelle: Technically, the compiler is allowed to use different internal representations for different types of pointers. For example, Win16 used 2-byte near pointers by default, while `void*` were required to bar far pointers and were 4 bytes. So it's possible (though unlikely) that `char*` might be 2 bytes while `char (*)[]` might be 4 bytes on a particular system. – Adam Rosenfield Aug 13 '13 at 17:15
  • 1
    @michelle Technically, **it never works.** It only seems to work. It's undefined behavior, so any result is an acceptable outcome - this includes a seemingly correct conversion, a crash, demons flying out of your nose, etc. –  Aug 13 '13 at 17:15
  • @Adam I wasn't kidding, that's actually fascinating (I am way too young to know anything about Win16). – Michelle Aug 13 '13 at 17:40
  • @Michelle Since all mistakes have been fixed, I've removed my downvote. –  Aug 13 '13 at 19:20
0

Both str and &str are defined to take the value of the memory address of the first element of the array. Notwithstanding H2CO3's indication that this is undefined behaviour, this will always work in practice.

1''
  • 26,823
  • 32
  • 143
  • 200
  • 3
    It ***IS UNDEFINED BEHAVIOR DUE TO MISMATCHING TYPES!*** –  Aug 13 '13 at 16:57
  • @H2CO3 Where in the C standard is this specified? – 1'' Aug 13 '13 at 16:59
  • 3
    @H2CO3 Would you mind citing the C99 or C11 standard if you're going to continue making these claims. – sfstewman Aug 13 '13 at 17:03
  • 1
    @H2CO3 Are you referring to "If this object does not have an appropriate type, or if the result of the conversion cannot be represented in the space provided, the behavior is undefined."? – 1'' Aug 13 '13 at 17:10
  • 1
    @1'' Exactly, that one! –  Aug 13 '13 at 17:12