#arrays #c #pointers #printf
#массивы #c #указатели #printf
Вопрос:
Как работает этот код?
#include lt;stdio.hgt; #include lt;string.hgt; int main() { #define NAME_LEN 10 char name[NAME_LEN 1]; // line 8 strcpy(name,"Hi"); // line 9 char (* name_ptr)[NAME_LEN 1] = amp; name; // line 10 printf("%s=%sn%s=%sn", // line 12 "name",name, // line 13 "name_ptr",name_ptr // line 14 ); }
Все идет по заведенному порядку до 10-й строки. Для меня строка 10 смешивается с объявлением a char *
и char [length]
, поэтому я ожидал бы получить массив из 10 char *
(очевидно, компилятор не согласен, потому что это работает). Когда я компилирую это с -Wall
помощью , я получаю следующее:
$ cc -g -Wall a.C a.C: In function ‘int main()’: a.C:16:2: warning: format ‘%s’ expects argument of type ‘char*’, but argument 5 has type ‘char (*)[11]’ [-Wformat=] );
Что я упускаю?
Комментарии:
1. @paulsm4 наверняка компилятор не думает, что это функция.
2. Объявление указателей на массивы кажется сложным и необычным в C. Вы можете просто использовать обычный
char *
указатель:char * name_ptr = name;
Ответ №1:
char (* name_ptr)[NAME_LEN 1]
name_ptr
является указателем на char
массив NAME_LEN 1
элементов. Таким образом, тип этого указателя не совпадает, char *
даже если они ссылаются на один и тот же объект.
Проще всего обнаружить и понять разницу в арифметике указателей.
Пример:
int main() { #define NAME_LEN 10 char name[NAME_LEN 1]; // line 8 strcpy(name,"Hi"); // line 9 char (* name_ptr)[NAME_LEN 1] = amp; name; // line 10 printf("name = %p (name 1) = %p, difference in chars = %tdn", (void *)name, (void *)(name 1), (char *)(name 1) - (char *)name); // line 12 printf("name_ptr = %p (name_ptr 1) = %p, difference in chars = %tdn", (void *)name_ptr, (void *)(name_ptr 1), (char *)(name_ptr 1) - (char *)name_ptr); // line 12 }
И результат:
name = 0x7fff645264b5 (name 1) = 0x7fff645264b6, difference in chars = 1 name_ptr = 0x7fff645264b5 (name_ptr 1) = 0x7fff645264c0, difference in chars = 11
Как вы видите, указатель на массив добавляет весь размер массива к исходному адресу.
Поэтому, если вы хотите использовать этот указатель в printf
, вам нужно разыменовать его:
printf("name_ptr = %sn",name_ptr[0]); printf("name_ptr = %sn",*name_ptr);
Комментарии:
1. Вы должны категорически ПРЕПЯТСТВОВАТЬ такому синтаксису:
char (* name_ptr)[NAME_LEN 1] = amp; name;
. ПРЕДПОЧТИТЕЛЬНО:char * name_ptr = amp;name[0] ;
(если это то, что на самом деле подразумевал ОП). Четко отличайте объявление типа (слева) от присвоенного значения (справа).2. @paulsm4 извините, но вы просто не понимаете этот код. Это назначение на 100% правильно, так как оно присваивает ссылку на массив указателю на массив . И этот ответ объясняет это и показывает, как использовать такого рода указатели.
Ответ №2:
В c тип указателя на самом деле не меняет того, на что он указывает.
Как уже упоминалось, 0___________, единственное различие между char*
(указателем на символы), ожидаемым printf
%s
флагом s, и char (*)[11]
(указателем на массивы символов длиной 11 символов)-это арифметика указателя.
Теперь то, что вы делаете в строке 10 char (*)[11]
, — это указываете на место в памяти, где случайно есть ['h', 'i', '']
. Затем в своем вызове printf вы приказываете ему прочитать строку из этого места. И там действительно есть строка, заканчивающаяся нулем и все такое.
Поэтому он жалуется вам на тип указателя, но все равно делает то, что ему говорят, потому что у него есть все необходимое в том месте, на которое указывает указатель.