Ссылка на массив по отношению к указателю на массив

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 в локальном стеке и не имеет ничего общего с его значением (или тем, на что он указывает).

Надеюсь, что это поможет.