Я - обучающий помощник вводного хода программирования, и некоторые студенты сделали этот тип ошибки:
char name[20];
scanf("%s",&name);
который не удивителен, как они узнают... То, что удивительно, то, что, помимо предупреждения gcc, работ кода (по крайней мере, эта часть). Я пытался понять, и я написал следующий код:
void foo(int *v1, int *v2) {
if (v1 == v2)
printf("Both pointers are the same\n");
else
printf("They are not the same\n");
}
int main() {
int test[50];
foo(&test, test);
if (&test == test)
printf("Both pointers are the same\n");
else
printf("They are not the same\n");
}
Компиляция и выполнение:
$ gcc test.c -g
test.c: In function ‘main’:
test.c:12: warning: passing argument 1 of ‘foo’ from incompatible pointer type
test.c:13: warning: comparison of distinct pointer types lacks a cast
$ ./a.out
Both pointers are the same
Both pointers are the same
Кто-либо может объяснить, почему они не отличаются?
Я подозреваю, что это - потому что я не могу получить адрес массива (поскольку я не могу иметь & &x
), но в этом случае код не должен компилировать.
Править: Я знаю, что массив отдельно совпадает с адресом первого элемента, но это не связано с этой проблемой, я думаю. Например:
int main() {
int a[50];
int * p = a;
printf("%d %d %d\n", p == a, p == &a[0], &p[0] == a);
printf("%d %d %d\n", p == &a, &p == a, &p == &a);
}
печать:
$ ./a.out
1 1 1
1 0 0
Я не понимаю, почему вторая строка начинается 1
.
В вашем примере массив test
представляет собой блок из 50 целых
. Это выглядит так:
| int | int | ... | int |
Когда вы применяете унарный оператор &
к массиву, вы получаете адрес массива . Точно так же, как когда вы применяете его к чему-либо еще. Итак, & test
- это указатель, который указывает на этот блок из 50 целых чисел
:
(&test) -----------> | int | int | ... | int |
Указатель, указывающий на массив из 50 целых чисел, имеет тип int (*) [50 ]
- это тип и тест
.
Когда вы просто используете имя test
в любом месте, где оно не является операндом операторов sizeof
или унарных операторов- и
, оно оценивается как указатель на его первый элемент. Таким образом, тест
, который вы передаете в foo ()
, оценивает указатель на элемент test [0]
:
(test) -----------------\
v
(&test) -----------> | int | int | ... | int |
Вы можете видеть, что они оба являются указывает на тот же адрес - хотя & test
указывает на весь массив, а test
указывает на первый элемент массива (который отображается только в разных типах , которые имеют эти значения).
Если вы определяете массив типа
char name[20];
, имя
неявно преобразуется в char *
, но & name
имеет тип char (*) [20 ]
(указатель на массив из 20 символов). Адреса такие же.
Проверьте адрес (& name + 1)
. Он отличается от и имени
размером sizeof (char [20])
.
Имя массива, в большинстве случаев вычисляет адрес его начального элемента. Два исключения - это когда это операнд sizeof
или унарный &
.
Унарный &
дает адрес своего аргумента. Адрес массива совпадает с адресом его начального элемента, поэтому (void *) & array == (void *) array
всегда будет истинным.
array
при преобразовании в указатель на его начальный элемент имеет тип T *
. Тип & array
- T (*) [n]
, где n
- количество элементов в массиве. Таким образом,
int* p = array; // Works; p is a pointer to array[0]
int* q = &array; // Doesn't work; &array has type int (*)[10]
int (*r)[10] = &array; // Works; r is a pointer to array
На самом деле они разные, по крайней мере, у них разные типы.
Но в C адрес массива совпадает с адресом первый элемент в массиве, поэтому «они не разные», в основном, указывают на одно и то же.
Я считаю, что это оптимизация gcc. Думаю об этом.
& test
указывает на адрес test
test
указывает на первый элемент test
или & test [0]
[0]
то же самое (по большей части) как *
Таким образом, согласно этому & test
может отличаться от test
, но gcc оптимизирует это, потому что нет цели имея дополнительный уровень косвенности в этой точке.