Синтаксис printf в C

#c #printf #implicit-conversion #string-literals

Вопрос:

Я новичок в C и в основном использовал следующие два формата printf

 #include <stdio.h>

//Compiler version gcc  6.3.0

int main()
{
  int c=5;
  printf("Hello, World!n");
  printf("%d",c);
  return 0;
}
 

Но недавно я узнал, что есть еще один способ написания printf,т. Е. printf(указатель строки), чем этот формат так отличается от двух других, в нем нет кавычек и почему есть строка, вопрос может быть глупым, но постарайтесь понять, что я всего лишь новичок.

Комментарии:

1. На самом деле, это точно то же самое, что вы используете с printf("Hello, World!n") . И также обратите внимание, что на самом деле это не разные формы printf функции, просто в некоторых вы передаете дополнительные аргументы, а в некоторых-нет.

2. Также обратите внимание (и это довольно важно): вы никогда не должны передавать переменную в качестве строки формата. Если вы используете printf с пользовательским вводом, что помешает пользователю указать спецификаторы формата во входных данных, что приведет print к попытке использовать аргументы, которых на самом деле не существует. Если вы хотите напечатать строку пользовательского ввода, используйте printf("%sn", user_input_string) или puts(user_input_string) .

3. существует только одно определение printf : int printf(const char *restrict format, ...); . Просто прочитайте документацию

4. Пожалуйста, обратите внимание, что printf это вариативная функция .

Ответ №1:

Хотя в вашем редакторе это выглядит по-другому, на самом деле это одно и то же.

Когда вы пишете

 printf("Hello, World!n");
 

в вашем редакторе ваш компилятор в принципе изменит его на

 char* hidden_unnamed_string = "Hello, World!n";
printf(hidden_unnamed_string);
 

Строка «Привет, мир!n» называется строковым литералом. Компилятор (автоматически) поместит его где-нибудь в памяти, а затем вызовет printf с этим адресом памяти.

Вот пример из godbolt.org

введите описание изображения здесь

С левой стороны у вас есть программа на языке Си, как она выглядит в вашем редакторе. Справа у вас есть скомпилированная программа.

Обратите внимание, что строка расположена за пределами блока кода и помечена символом LC0. Затем внутри блока кода LC0 загружается в edi (т. е. адрес/указатель на строку загружается в edi) непосредственно перед вызовом функции печати.

Также обратите внимание, что компилятор решил использовать puts вместо printf . Далее обратите внимание, что строка хранится без n . Причина в том, что puts в отличие printf от автоматически добавляет n .

Ответ №2:

Для начала вместо этого вызова printf

 printf("Hello, World!n");
 

вы могли бы использовать вызов таких команд, как

 puts( "Hello, World!" );
 

Строковый литерал "Hello, World!n" внутренне представлен в виде массива символов, например

 char string_literal[] = 
{ 
    'H', 'e', 'l', 'l', 'o', ',', ' ', 'W', 'o', 'r', 'l', 'd', '!', 'n', '' 
};
 

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

Так что в этом звонке

 printf("Hello, World!n");
 

массив символов, используемый в качестве аргумента вызова функции, преобразуется в указатель типа char * на его первый элемент 'H' . Вы можете представить себе, что следующим образом

 printf( amp;"Hello, World!n"[0] );
 

Таким образом, функция имеет дело с указателем типа char * . Вы могли бы сделать то же самое, введя промежуточную переменную

 char *s = "Hello, World!n";
printf( s );
 

или

 char *s = "Hello, World!n";
printf( "%s", s );
 

или

 char *s = "Hello, World!";
printf( "%sn", s );
 

или

 char *s = "Hello, World!";
puts( s );