"Адрес" (&) - массив/адрес игнорирования gcc?
Я преподаватель курса вводного курса программирования, и некоторые ученики совершили такую ошибку:
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
.
Ответы
Ответ 1
В вашем примере массив test
представляет собой блок из 50 ints
. Итак, это выглядит так:
| int | int | ... | int |
Когда вы применяете унарный оператор &
к массиву, вы получаете адрес массива. Точно так же, когда вы применяете его ко всему остальному, действительно. Итак, &test
- это указатель, который указывает на этот блок из 50 ints
:
(&test) -----------> | int | int | ... | int |
Указатель, указывающий на массив из 50 ints, имеет тип int (*)[50]
- тип &test
.
Когда вы просто используете имя test
в любом месте, где он не является операндом операторов sizeof
или unary- &
, он вычисляется указателем на его первый элемент. Таким образом, test
, который вы передаете в foo()
, оценивается указателем на элемент test[0]
:
(test) -----------------\
v
(&test) -----------> | int | int | ... | int |
Вы можете видеть, что оба они указывают на один и тот же адрес, хотя &test
указывает на весь массив, а test
указывает на первый элемент массива (который появляется только в разных типах, эти значения имеют).
Ответ 2
Собственно, они разные, они не имеют того же типа, по крайней мере.
Но в C адрес массива совпадает с адресом
первый элемент в массиве, почему "они не разные", в основном они указывают на одно и то же.
Ответ 3
Если вы определяете массив вроде
char name[20];
name
неявно конвертируется в char*
, но &name
имеет тип char (*)[20]
(указатель на массив из 20 символов). Адреса совпадают.
Проверьте адрес (&name + 1)
. Он отличается формой &name
на sizeof(char [20])
.
Ответ 4
Имя массива, в большинстве случаев, вычисляется по адресу его исходного элемента. Два исключения - это операнд sizeof
или унарный &
.
Унарный &
дает адрес своего аргумента. Адрес массива совпадает с адресом его начального элемента, поэтому (void*)&array == (void*)array
всегда будет true.
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
Ответ 5
Я считаю, что это gcc-оптимизация. Подумайте об этом.
-
&test
указывает на адрес test
-
test
указывает на первый элемент test
или &test[0]
-
[0]
совпадает (по большей части) как *
Таким образом, в соответствии с этим &test
может быть отличным от test
, но gcc оптимизирует это, потому что нет цели иметь дополнительный уровень косвенности в этой точке.