Вопрос о назначении символа * [длина]

#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 вы приказываете ему прочитать строку из этого места. И там действительно есть строка, заканчивающаяся нулем и все такое.

Поэтому он жалуется вам на тип указателя, но все равно делает то, что ему говорят, потому что у него есть все необходимое в том месте, на которое указывает указатель.