Массив
AC может быть неявно преобразован в указатель на его первый элемент (C99: TC3 6.3.2.1 §3), т.е. во многих случаях a
(тип char [100]
) будет вести себя одинаково путь как &a[0]
(который имеет тип char *
). Это объясняет, почему передача a
в качестве аргумента будет работать.
Но не думайте, что так будет всегда: между массивами и указателями есть важные различия, например, в отношении присваивания, sizeof
и того, о чем я сейчас не могу думать ...
&a
на самом деле является одной из этих ловушек: это создаст указатель на массив, т.е. он имеет тип char (*) [100]
(и не char **
). Это означает, что &a
и &a[0]
будут указывать на одну и ту же область памяти, но будут иметь разные типы.
Насколько я знаю, не существует неявного преобразования между этими типами, и они также не гарантируют совместимое представление. Все, что я мог найти, это C99: TC3 6.2.5 §27, который мало говорит о указателях на массивы:
[...] Указатели на другие типы не обязательно должны иметь одинаковые требования к представлению или выравниванию.
Но есть также 6.3.2.3 §7:
[...] Когда указатель на объект преобразуется в указатель на тип символа, результат указывает на младший адресуемый байт объекта. Последовательные приращения результата, вплоть до размера объекта, дают указатели на оставшиеся байты объекта.
Таким образом, приведение (char *)&a
должно работать как положено. На самом деле, я предполагаю, что младший адресуемый байт массива будет самым младшим адресуемым байтом его первого элемента - не уверен, гарантировано ли это, или если компилятор может добавить произвольный отступ перед массивом, но если это так, это было бы серьезно странно ...
В любом случае, чтобы это работало, &a
все еще необходимо привести к char *
(или void *
- стандарт гарантирует, что эти типы имеют совместимые представления). Проблема в том, что не будет никаких преобразований, применяемых к переменным аргументам, кроме продвижения аргументов по умолчанию, то есть вы должны выполнять приведение в явном виде самостоятельно.
Подведем итог:
&a
имеет тип char (*) [100]
, который может иметь битовое представление, отличное от char *
. Следовательно, программист должен выполнить явное приведение, потому что для переменных аргументов компилятор не может знать, во что он должен преобразовать значение. Это означает, что будет выполняться только продвижение аргумента по умолчанию, которое, как указывалось litb , не включает преобразование в void *
. Отсюда следует:
scanf("%s", a);
- хорошо
scanf("%s", &a);
- плохо
scanf("%s", (char *)&a);
- должно быть в порядке