2009-06-29 2 views
4

Мы имеем следующий фрагмент кода:Вопрос о параметрах printf. C/C++

char tab[2][3] = {'1', '2', '\0', '3', '4', '\0'}; 
printf("%s\n", tab); 

И я не понимаю, почему мы не получаем ошибку/предупреждение в вызове printf. Я получаю предупреждение, но не ошибку, и программа работает нормально. Он печатает «12».
printf ожидает аргумент типа char *, то есть указатель на char. Так что если я объявил char arr[3], то arr является адресом блока памяти, который содержит char, так что если я назвал printf с этим, было бы затухать до указателя на символ, т.е. char *.
Аналогично, tab представляет собой адрес блока памяти, который содержит тип массив 3 Чара, который, в свою очередь, адрес блока памяти содержит char, поэтому tab будет затухать до char **, и это должно быть проблемой, так как printf ожидает char *.

Может кто-нибудь объяснить эту проблему?

Добавление:

Предупреждение я получаю:
a.c:6: warning: char format, different type arg (arg 2)

+0

Ниже вы говорите, что это работает, но здесь вы заявляете, что получаете сообщение «ошибка/предупреждение». Что вы получаете точно? Логично, что вы получаете предупреждение с этим кодом, подверженным ошибкам, но получаете ли вы ошибку? –

+0

Я добавил и исправил. –

ответ

6

Пример Источник

#include <stdio.h> 

int main(void) { 
    char tab[2][3] = {'1', '2', '\0', '3', '4', '\0'}; 
    printf("%s\n", tab); 

    return 0; 
} 

Вкомпилировать Предупреждение

 
$ gcc test.c 
test.c: In function ‘main’: 
test.c:5: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘char (*)[3]’

Указатели Указатели

%s аргумент printf указывает на функцию, которая его будет получать указатель (в строку). Строка в C - это просто серия байтов, завершаемая ASCII-Z. Переменная tab[2][3] является указателем. Некоторые компиляторы выдают предупреждение о несоответствии указателя. Тем не менее, код должен по-прежнему распечатывать 12, потому что код printf перемещается по памяти, начиная с указателя, который он задавал (печатает символы по мере его поступления), пока не найдет нулевой байт. 1, 2 и \ 0 смежно задаются в памяти, начиная с адреса, представляемого переменной tab.

Эксперимент

В качестве эксперимента, что происходит, когда вы скомпилировать и запустить следующий код:

#include <stdio.h> 

int main(void) { 
    char tab[2][3] = {'1', '2', '\0', '3', '4', '\0'}; 
    printf("%s\n", tab[1]); 

    return 0; 
} 

Не бойся экспериментировать. Посмотрите, сможете ли вы найти ответ на основе того, что вы сейчас знаете. Как бы вы теперь ссылались на tab (в свете эксперимента), чтобы избавиться от предупреждения и по-прежнему отображать 12?

+0

Это легко. Это правильный способ вызова printf. tab [1] имеет тип char [], и он совпадает с вызовом printf с char *. Так что все оптимально. –

+0

Точно. Таким образом, чтобы отобразить «12», вы должны использовать вкладку [0]. –

1

Вы, кажется, объяснил это самостоятельно, я не вижу, что осталось сказать.

tab - это массив из двух char *. Каждый элемент из tab является строкой, которую может принимать printf, но tab сам по себе неприемлем, так как это указатель на указатель на символ.

+0

Но это работает! и он печатает '12 '. –

+1

вкладка и вкладка [0] указывают на то же место (строка «12»), поэтому они оба работают, несмотря на несоответствие. –

+0

Ваша проблема заключается в том, что компилятор принимает код. См. Ответ Нила Баттерворта, почему это так. Честно говоря, я удивлен, что компилятор знает о семантике printf() и делает какие-то проверки типов на своих аргументах. – Ari

4

Параметр табуляции соответствует elipsis в вызове printf(). Компиляторы C и C++ не обязаны проверять такие параметры.

3

Ваше предположение, что tab будет затухать до char ** неправильно: tab имеет тип char [2][3], то есть она будет распадаться на char (*) [3]. Важно понимать, что хотя массивы и указатели часто ведут себя одинаково, это не одно и то же. printf() ожидает char *, поэтому он принимает бит char (*) [3] и интерпретирует их соответственно. Несмотря на то, что он работает на вашей платформе, стандарт C не гарантирует этого: оба указателя ссылаются на одну и ту же ячейку памяти, но их представление не обязательно должно быть идентичным.

Проверьте my answer на номер this related question.

+0

+1, я знал, что на это уже что-то ответили, но я не нашел вопроса. Единственное, что я нашел, это следующее: http://stackoverflow.com/questions/232303/so-you-think-you-know-pointers – quinmars

Смежные вопросы