Intereting Posts
Получить текущее время в секундах в модуле ядра что означает линия int * (* (x ) ()) ; делать в C? Распределение памяти переменных Существуют ли фиксированные целые числа в GCC? Лучший алгоритм для разбиения ведущих и конечных пробелов в C MPI асинхронная / односторонняя связь Выделение памяти для массива char для конкатенации известной части текста и целого числа Разница между UINT32_C и uint32_t Почему (и когда) мне нужно использовать круглые скобки после sizeof? Как память, доступная только для чтения, реализована в C? Должен ли я вернуть 0 или 1 для успешной работы? Почему execv выходит из функции? Как сканировать только одно целое и повторять чтение, если пользователь вводит нецифровые символы? сделайте это так, когда вводится недопустимое значение, пользователь должен повторить новое значение в C Почему язык программирования требует ключевых слов?

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

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 * , а &blachar ** .

Однако array – это char [10] , а &arraychar (*)[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 в локальном стеке и не имеет ничего общего с его значением (или тем, что он указывает).

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