Вопрос об аргументах printf. C / C ++ - PullRequest
4 голосов
/ 29 июня 2009

У нас есть следующий фрагмент кода:

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)

Ответы [ 4 ]

6 голосов
/ 29 июня 2009

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

#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?

4 голосов
/ 29 июня 2009

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

Проверьте мой ответ на этот связанный вопрос для деталей.

4 голосов
/ 29 июня 2009

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

1 голос
/ 29 июня 2009

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...