Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

& operator definition for arrays in C

A recent question prompted a discussion centering on arrays and pointers. The question was in reference to scanf("%s", &name) vs scanf("%s", name).

For the following code, Microsoft actually resolves this for you in VS2010 (and maybe earlier versions?),

#include <stdio.h>

int main()
{
    char name[30];

    printf("Scan \"name\" - ");
    scanf("%s", name);
    printf("Print \"&name\" - %s\n", &name);
    printf("Print \"name\"  - %s\n", name);

    printf("Pointer to &name - %p\n", &name);
    printf("Pointer to name  - %p\n", name);

    printf("\n\n");

    printf("Scan \"&name\" - ");
    scanf("%s", &name);
    printf("Print \"&name\" - %s\n", &name);
    printf("Print \"name\"  - %s\n", name);

    printf("Pointer to &name - %p\n", &name);
    printf("Pointer to name  - %p\n", name);

    return 0;
}

Is this actually defined in the ANSI C Standard, or is this allowed to be compiler dependent? Does this work because MS is treating everything as C++? Please ignore buffer overflow issues for now.

like image 383
Jess Avatar asked Jan 19 '23 20:01

Jess


2 Answers

Both name and &name should give the same result. Strictly speaking, only name is valid per the C language standard and &name results in undefined behavior, so you should definitely use name, but in practice both will work.

name is an array, so when you use it as an argument to a function (as you do when you pass it to printf), it "decays" to a pointer to its initial element (which is a char* here).

&name gives you the address of the array; this address is the same as the address of the initial element (because there can be no padding bytes before the initial element of an array or between elements in an array), so &name and name have the same pointer value.

However, they have different types: &name is of type char (*)[30] (a pointer to an array of 30 char) while name, when it decays to a pointer to its initial element, is of type char* (a pointer to a char, in this case, the initial char element of the array name).

Since they have the same value and since the printf and scanf functions reinterpret the argument as a char* anyway, there should be no difference whether you pass name or &name.

like image 110
James McNellis Avatar answered Jan 29 '23 16:01

James McNellis


Undefined Behaviour as per the Standard.

The printf conversion specifier "%p" expects a void*: anything else invokes UB The printf conversion specifier "%s" expects a char* that includes a null byte somewhere inside the object pointed to: anything else invokes UB The scanf conversion specifier "%s" expects a char* with enough space for the input and an extra null terminating byte: anything else invokes UB

If any implementation defines the behaviour, then it should be ok to use in that implementation.

Most often printing a char* or a char(*)[30] instead of a void* with printf("%p") results in a UB manifestation that is indistinguishable from the intended behaviour.

Most often printing a char(*)[30] instead of a char* with printf("%s") results in a UB manifestation that is indistinguishable from the intended behaviour.

From what you say, it appears MS manifestation of UB in these cases is the same as intended.

But it's still Undefined Behaviour.

like image 25
pmg Avatar answered Jan 29 '23 14:01

pmg