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;
Первый работает, но второй нет. Я не понимаю, почему это происходит.
В спецификации C указано, что array и & array – это один и тот же адрес указателя.
Использование имени массива при передаче массива функции автоматически преобразует аргумент в указатель на спецификацию C (выделение мое).
6.3.2.1-4
За исключением случаев, когда это операнд оператора sizeof или унарный оператор & или строковый литерал, используемый для инициализации массива, выражение, которое имеет тип ” array of type ”, преобразуется в выражение с указателем типа ” to type ”, который указывает на начальный элемент объекта массива и не является значением lvalue. Если объект массива имеет class хранения регистров, поведение не определено.
Поэтому вызов func (array) приведет к передаче указателю char [] в функцию. Но есть специальный случай использования адреса-оператора в массиве. Поскольку массив имеет тип «массив типа», он попадает в категорию «Иначе» спецификации (акцент мой).
6.5.3.2-3
Унарный оператор & дает адрес своего операнда. Если операнд имеет тип ” type ”, результат имеет тип ” указатель на тип ”. Если операнд является результатом унарного * оператора, ни этот оператор, ни оператор & не оцениваются, и результат, как если бы оба были опущены, за исключением того, что ограничения для операторов все еще применяются, а результат не является значением l. Аналогично, если операнд является результатом оператора [], ни оператор &, ни унарный *, который подразумевается [], не оцениваются, а результат выглядит так, как если бы оператор & был удален, а оператор [] был изменен на a +. В противном случае результатом будет указатель на объект или функцию, обозначенные его операндом
Поэтому вызов func (& array) по-прежнему вызывает передачу одного указателя на функцию, аналогичную вызову func (array), поскольку оба массива и & array являются одним и тем же значением указателя.
Common-sense заставит вас поверить, что & array – это двойной указатель на первый элемент массива, потому что использование этого оператора обычно ведет себя таким образом. Но массивы разные. Поэтому, когда вы удаляете ссылку на переданный указатель массива в виде двойного указателя на массив, вы получаете ошибку сегментации.
Проблема в том, что когда мы делаем &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
is 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» интерпретируется как адрес, который вы выполняете.
Для 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; }
Это не прямой ответ на ваш вопрос, но он может быть полезен вам в будущем.
Массивы не являются указателями:
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
.
Одна из проблем заключается в том, что ваш массив символов НЕ НЕОБХОДИМО, чтобы быть завершенным нулем. Поскольку array
– это автоматическая переменная, которая локально размещается в стеке, не гарантируется быть обнуленной памятью. Итак, хотя вы инициализируете первые 4 символа, последние 6 остаются неопределенными.
Тем не мение …
Простой ответ на ваш вопрос заключается в том, что &bla != &array
поэтому ваша функция check () предполагает, что он найдет массивы символов с нулевым символом с двумя разными адресами.
Справедливы следующие уравнения:
array == &array // while not the same types exactly, these are equivalent pointers array == bla &array == bla *bla == array[0]
&bla
никогда не станет равным вам, потому что этот синтаксис ссылается на адрес переменной bla
в локальном стеке и не имеет ничего общего с его значением (или тем, что он указывает).
Надеюсь, это поможет.