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