Ссылка на массив по отношению к указателю на массив
void check(void* elemAddr){
char* word = *((char**)elemAddr);
printf("word is %s\n",word);
}
int main(){
char array[10] = {'j','o','h','n'};
char * bla = array;
check(&bla);
check(&array);
}
Вывод:
word is john
RUN FINISHED; Segmentation fault; core dumped;
Первый работает, но второй нет. Я не понимаю, почему это происходит.
Ответы
Ответ 1
В спецификации C указано, что массив и & массив - это один и тот же адрес указателя.
Использование имени массива при передаче массива функции автоматически преобразует аргумент в указатель на спецификацию С (выделение мое).
6.3.2.1-4
За исключением случаев, когда это операнд оператора sizeof или унарный & operator, или является строковым литералом, используемым для инициализации массива, выражение, которое имеет тип '' массив типа, преобразуется в выражение с типом '' указатель на тип, указывающий на начальный элемент объекта массива и не является значением lvalue. Если объект массива имеет класс хранения регистра, поведение undefined.
Таким образом, вызов func (array) приведет к передаче указателю на char [] функции. Но есть специальный случай использования адреса-оператора в массиве. Поскольку массив имеет тип "массив типа", он попадает в категорию "Иначе" спецификации (выделение мое).
6.5.3.2-3
Унарный и оператор дает адрес своего операнда. Если операнд имеет тип '' type, результат имеет тип '' указатель на тип. Если операнд является результатом унарного * оператора, ни тот оператор, ни оператор и вычисляется, и результат выглядит так, как если бы оба были опущены, за исключением того, что ограничения для операторов все еще применяются, и результат не является значением lvalue. Аналогично, если операнд является результатом [], ни оператор, ни унарный *, который подразумевается [] оценивается, и результат выглядит так, как если бы оператор и удалены, а оператор [] был заменен на оператор+. В противном случае, результатом является указатель на объект или функцию, обозначенные ее Операнд
Таким образом, вызов func (& array) по-прежнему вызывает передачу одного указателя на функцию так же, как вызов func (array), так как массив и array являются одинаковыми значениями указателя.
Common-sense заставит вас поверить, что & array - это двойной указатель на первый элемент массива, потому что использование этого и оператора обычно ведет себя таким образом. Но массивы разные. Поэтому, когда вы удаляете ссылку на переданный указатель массива как двойной указатель на массив, вы получаете ошибку сегментации.
Ответ 2
Проблема заключается в том, что когда мы делаем &array
, получаем char (*)[10]
из char [10]
вместо char **
.
Прежде чем мы сделаем наш эксперимент, я подчеркну, что когда мы передаем массив в качестве аргумента функции, C фактически передает массив указателю. Большой ведро данных не копируется.
Таким образом, int main(int argc, char **argv)
идентично int main(int argc, char *argv[])
в C.
Это позволило нам печатать адрес массива с помощью простого printf
.
Сделайте эксперимент:
char array[] = "john";
printf("array: %p\n", array);
printf("&array: %p\n", &array);
// Output:
array: 0x7fff924eaae0
&array: 0x7fff924eaae0
Узнав об этом, дайте ссылку на свой код:
char array[10] = "john";
char *bla = array;
check(&bla);
check(&array);
bla
- char *
, а &bla
- char **
.
Однако array
есть char [10]
, а &array
- char (*)[10]
вместо char **
.
Поэтому, когда вы передаете &array
в качестве аргумента, char (*)[10]
действует как char *
при передаче в качестве аргумента, как сказано выше.
Поэтому **(char **) &bla == 'j'
while *(char *) &array == 'j'
. Проведите несколько простых экспериментов, и вы это докажете.
И вы отбрасываете void *elemAddr
в char **
и пытаетесь отнестись к нему. Это будет работать только с &bla
, так как это char **
. &array
приведет к segfault, потому что "john" интерпретируется как адрес, который вы выполняете.
Ответ 3
Для check(&bla);
вы отправляете pointer to pointer
void check(void* elemAddr){
char* word = *((char**)elemAddr); // works fine for pointer to pointer
printf("word is %s\n",word);
}
Это прекрасно работает.
Но для check(&array);
вы указали только указатель
void check(void* elemAddr){
char* word = *((char**)elemAddr); // This is not working for pointer
char* word = *(char (*)[10])(elemAddr); // Try this for [check(&array);]
printf("word is %s\n",word);
}
Полный код -
Код для check(array);
:
void check(void* elemAddr){
char* word = *(char (*)[10])(elemAddr);
printf("word is %s\n",word);
}
int main() {
char array[10] = {'j','o','h','n'};
check((char*)array);
return 0;
}
Код для check(&bla);
:
void check(void* elemAddr){
char* word = *((char**)elemAddr);
printf("word is %s\n",word);
}
int main() {
char array[10] = {'j','o','h','n'};
char* bla = array;
check(&bla);
return 0;
}
Ответ 4
Это не прямой ответ на ваш вопрос, но он может быть полезен для вас в будущем.
Массивы не являются указателями:
-
type arr[10]
:
-
Используется количество sizeof(type)*10
байтов
-
Значения arr
и &arr
обязательно идентичны
-
arr
указывает на действительный адрес памяти, но не может быть установлен для указания на другой адрес памяти
-
type* ptr = arr
:
-
Используется дополнительное количество sizeof(type*)
байтов
-
Значения ptr
и &ptr
обычно различаются, если вы не установите ptr = (type*)&ptr
-
ptr
может быть настроен так, чтобы указывать как допустимые, так и неверные адреса памяти, столько раз, сколько вы будете
Что касается вашего вопроса: &bla != bla == array == &array
и, следовательно, &bla != &array
.
Ответ 5
Одна из проблем заключается в том, что ваш массив char НЕОБХОДИМО, чтобы быть завершенным нулем. Поскольку array
- это автоматическая переменная, которая локально размещается в стеке, не гарантируется быть обнуленной памятью. Итак, хотя вы инициализируете первые 4 символа, последние 6 остаются undefined.
Однако...
Простой ответ на ваш вопрос заключается в том, что &bla != &array
, поэтому ваша функция check() предполагает, что он найдет массивы символов с нулевым символом с двумя разными адресами.
Верны следующие уравнения:
array == &array // while not the same types exactly, these are equivalent pointers
array == bla
&array == bla
*bla == array[0]
&bla
никогда не станет равным вам, потому что этот синтаксис ссылается на адрес переменной bla
в локальном стеке и не имеет ничего общего с его значением (или тем, на что он указывает).
Надеюсь, что это поможет.