На языке C Printf () работает что-то новое? [Дубликат]

Это обратная ссылка.

Из http://www.selectorweb.com/grep_tutorial.html :

Backreference - это выражение\n, где n - число. Он соответствует содержимому n-го набора круглых скобок в выражении.

Кроме того, ответ: (d):

$ grep -E '(^.{4})(.{2}).*[ ]\2' test.txt
If there's a bustle in your hedgerow.

126
задан Lundin 22 May 2017 в 07:00
поделиться

8 ответов

Выражение:

(expression1,  expression2)

Вычисляется первое выражение1, затем вычисляется выражение 2, и значение выражения2 возвращается для всего выражения.

102
ответ дан lillq 17 August 2018 в 09:28
поделиться
  • 1
    то если я напишу i = (5,4,3,2,1,0), то в идеале он должен вернуть 0, правильно? но мне присваивается значение 5? Не могли бы вы помочь мне понять, где я ошибаюсь? – Jayesh 13 November 2010 в 07:55
  • 2
    @James: значение операции с запятой всегда будет значением последнего выражения. Ни в коем случае i не будет иметь значений 5, 4, 3, 2 или 1. Это просто 0. Это практически бесполезно, если выражения не имеют побочных эффектов. – Jeff Mercado 13 November 2010 в 08:06
  • 3
    Обратите внимание, что существует полная точка последовательности между оценкой LHS выражения для запятой и оценкой RHS (см. ответ [f1] ответа Шафика Ягмура ] для цитаты из C99). Это важное свойство оператора запятой. – Jonathan Leffler 20 June 2014 в 21:47
  • 4
    Глядя на docs.microsoft.com/en-us/cpp/cpp/comma-operator , похоже, существует разница, если скобки не используются. В случае i = b, c; b присваивается i. Как это происходит? В случае функционального параметра использование скобок является обязательным, и Microsoft четко объясняет это. – Rajesh 26 April 2018 в 04:25

Он вызывает оценку нескольких операторов, но использует только последнее значение в качестве результирующего значения (rvalue, я думаю).

Итак ...

int f() { return 7; }
int g() { return 8; }

int x = (printf("assigning x"), f(), g() );

should результат: x устанавливается в 8.

8
ответ дан Ben Collins 17 August 2018 в 09:28
поделиться

Оператор comma будет оценивать левый операнд, отбрасывать результат и затем оценивать правый операнд, и это будет результат. Использование идиоматического , как указано в ссылке, при инициализации переменных, используемых в цикле for, и дает следующий пример:

void rev(char *s, size_t len)
{
  char *first;
  for ( first = s, s += len - 1; s >= first; --s)
      /*^^^^^^^^^^^^^^^^^^^^^^^*/ 
      putchar(*s);
}

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

Из черновика стандарта C99 грамматика выглядит следующим образом:

expression:
  assignment-expression
  expression , assignment-expression

и параграф 2 говорит:

Левый операнд оператора запятой оценивается как выражение void; после его оценки появляется точка последовательности. Затем оценивается правый операнд; результат имеет свой тип и значение. 97). Если делается попытка изменить результат работы с запятой или получить доступ к ней после следующей точки последовательности, поведение не определено.

Сноска 97 говорит:

Оператор запятой не дает lvalue.

, что означает, что вы не можете назначить результат запятой .

Важно отметить, что оператор с запятой имеет самый низкий приоритет , и поэтому бывают случаи, когда использование () может иметь большое значение, например:

#include <stdio.h>

int main()
{
    int x, y ;

    x = 1, 2 ;
    y = (3,4) ;

    printf( "%d %d\n", x, y ) ;
}

будет иметь следующий выход:

1 4
23
ответ дан Community 17 August 2018 в 09:28
поделиться

Как было сказано ранее, он оценивает все утверждения, но использует последнее в качестве значения выражения. Лично я нашел его полезным в выражениях цикла:

for (tmp=0, i = MAX; i > 0; i--)
3
ответ дан DGentry 17 August 2018 в 09:28
поделиться

Оператор запятой объединяет два выражения с обеих сторон в один, оценивая их как в порядке слева направо. Значение правой части возвращается как значение всего выражения. (expr1, expr2) походит на { expr1; expr2; }, но вы можете использовать результат expr2 в вызове функции или присваивании.

Часто в циклах for можно инициализировать или поддерживать несколько переменных:

for (low = 0, high = MAXSIZE; low < high; low = newlow, high = newhigh)
{
    /* do something with low and high and put new values
       in newlow and newhigh */
}

Кроме этого, я использовал его только «в гневе» в одном другом случае, когда вы завершаете две операции, которые всегда должны совпадать в макросе. У нас был код, который копировал различные двоичные значения в байтовый буфер для отправки по сети, и указатель поддерживался там, где мы вставали:

unsigned char outbuff[BUFFSIZE];
unsigned char *ptr = outbuff;

*ptr++ = first_byte_value;
*ptr++ = second_byte_value;

send_buff(outbuff, (int)(ptr - outbuff));

Где значения были short s или int s мы сделали это:

*((short *)ptr)++ = short_value;
*((int *)ptr)++ = int_value;

Позже мы читаем, что это не было действительно действительным C, потому что (short *)ptr больше не является l-значением и не может быть увеличено, хотя наш компилятор на время не против. Чтобы исправить это, мы разделили это выражение на два:

*(short *)ptr = short_value;
ptr += sizeof(short);

. Однако этот подход основывался на том, что все разработчики помнят, что оба оператора все время заставляют. Нам нужна функция, в которой вы могли бы передать указатель на выход, значение и тип значения. Это C, а не C ++ с шаблонами, мы не могли бы иметь функцию произвольного типа, поэтому мы остановились на макросе:

#define ASSIGN_INCR(p, val, type)  ((*((type) *)(p) = (val)), (p) += sizeof(type))

Используя оператор запятой, мы смогли использовать это в выражения или как заявления, которые мы хотели:

if (need_to_output_short)
    ASSIGN_INCR(ptr, short_value, short);

latest_pos = ASSIGN_INCR(ptr, int_value, int);

send_buff(outbuff, (int)(ASSIGN_INCR(ptr, last_value, int) - outbuff));

Я не предлагаю, чтобы какой-либо из этих примеров был хорошим стилем! Действительно, я, кажется, помню, что Code Complete Стив МакКоннелл предупреждал даже использовать запятые в цикле for: для удобочитаемости и ремонтопригодности цикл должен управляться только одной переменной, а выражения в for сама строка должна содержать только код управления контуром, а не другие дополнительные биты инициализации или обслуживания цикла.

26
ответ дан Luis 17 August 2018 в 09:28
поделиться
  • 1
    Благодаря! Это был мой первый ответ на StackOverflow: с тех пор я, возможно, узнал, что краткость следует ценить :-). – Paul Stephenson 16 July 2009 в 18:36
  • 2
    Иногда я оцениваю немного многословия, как здесь, где вы описываете эволюцию решения (как вы туда попали). – green diod 6 December 2016 в 11:19

Оператор запятой не делает ничего значимого, это 100% избыточная функция. Основное его использование - «люди, пытающиеся быть умными» и, следовательно, использовать их для (непреднамеренно) обфускации читаемого кода. Основной областью использования является обфускация для циклов, например:

for(int i=0, count=0; i<x; i++, count++)

Если int i=0, count=0 на самом деле не является оператором запятой, а является списком деклараций (мы уже смущены здесь). i++, count++ - это оператор запятой, который сначала оценивает левый операнд, а затем правый операнд. Результатом оператора запятой является результат правильного операнда. Результат левого операнда отбрасывается.

Но приведенный выше код можно записать гораздо более читаемым способом без оператора запятой:

int count = 0;
for(int i=0; i<x; i++) // readable for loop, no nonsense anywhere
{
  ...
  count++;
}

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

Итак, если у вас есть неопределенный код поведения например:

printf("%d %d", i++, i++);

Фактически вы можете превратить его в просто неопределенное поведение (порядок оценки параметров функции), написав

printf("%d %d", (0,i++), (0,i++));

. Теперь есть точка последовательности между оценка i++, поэтому, по крайней мере, программа не будет рисковать, чтобы сбой и сжечь дольше, хотя порядок оценки параметров функции остается неуказанным.

Конечно, никто не напишет такой код в реальном приложений, это полезно только для разговоров по языку-юристу о точках последовательности на языке С.

. Оператор запятой запрещен MISRA-C: 2 004 и MISRA-C: 2012 с обоснованием того, что он создает менее читаемый код.

5
ответ дан Lundin 17 August 2018 в 09:28
поделиться
  • 1
    Ваш & quot; неопределенный & quot; версия по-прежнему не определена. Оценка аргументов может перекрываться, так что сначала оцениваются два 0s, тогда есть точка последовательности, и, наконец, вычисляются два приращения. Это удовлетворяет требованию, чтобы между запятыми LHS и RHS была точка последовательности. Что касается вашего переписанного цикла for, рассмотрите continue. То, что вы предлагаете, эквивалентно действительности. Что касается читаемости, рассмотрим src++, dst++, где сохранение обоих приращений вместе в коде значительно упрощает читателю проверку того, что оба указателя получают одинаковое увеличение. – hvd 24 March 2015 в 18:20
  • 2
    @hvd Я не верю, что строго соответствующая реализация позволяет удалять точки последовательности, о которых говорит ваш комментарий. Почему кто-нибудь подумает о продолжении? Это полезно только для кодирования спагетти (а также запрещено MISRA). И цикл эквивалентен эквивалентным так или иначе, как с точки зрения результата, так и с точки зрения «абстрактной машины» C. И я действительно не думаю, что src++, dst++; более читабельна, чем src++; (newline) dst++;. Я очень сомневаюсь. – Lundin 25 March 2015 в 08:16
  • 3
    Хорошо, затем оцените 0, оцените 0, точку последовательности, точку последовательности, оцените i ++, оцените i ++. Тот же результат, поэтому я не думаю, что это имеет значение. Что касается src ++, dst ++, я имел в виду, когда это появляется в for, так как в этом и заключается ваш ответ. – hvd 25 March 2015 в 08:20
  • 4
    О продолжении, код, отличный от MISRA, может и не использует его, поэтому его нельзя сразу отбросить. Но я хотел сказать, что продолжение будет пропускать count ++ в вашей перезаписанной версии. – hvd 25 March 2015 в 08:23
  • 5
    @hvd Хорошо, в любом случае, не важно, действует ли оператор запятой, в свою очередь, фиксирует UB или нет, так как мой пример только для того, чтобы называть обсуждения о точках последовательности, подобных этому, как одно потенциальное использование оператора запятой. – Lundin 25 March 2015 в 08:29

Единственное место, которое я видел в этом полезном, - это когда вы пишете цикл фанки, в котором вы хотите сделать несколько вещей в одном из выражений (возможно, выражение init или выражение цикла). Что-то вроде:

bool arraysAreMirrored(int a1[], int a2[], size_t size)
{
  size_t i1, i2;
  for(i1 = 0, i2 = size - 1; i1 < size; i1++, i2--)
  {
    if(a1[i1] != a2[i2])
    {
      return false;
    }
  }

  return true;
}

Простите меня, если есть какие-либо синтаксические ошибки или если я смешался с чем-то, что не является строгим C. Я не утверждаю, что оператор является хорошей формой, но для этого вы можете использовать его. Вероятно, вместо этого используется цикл while, поэтому несколько выражений в init и loop будут более очевидными. (И я инициализировал i1 и i2 inline вместо объявления и затем инициализации .... blah blah blah.)

2
ответ дан Owen 17 August 2018 в 09:28
поделиться

Я видел больше всего в циклах while:

string s;
while(read_string(s), s.len() > 5)
{
   //do something
}

Он выполнит операцию, затем выполнит тест, основанный на побочном эффекте. Другой способ - сделать это следующим образом:

string s;
read_string(s);
while(s.len() > 5)
{
   //do something
   read_string(s);
}
88
ответ дан phuclv 17 August 2018 в 09:28
поделиться
  • 1
    Эй, это изящно! Мне часто приходилось делать неортодоксальные вещи в цикле, чтобы исправить эту проблему. – staticsan 4 September 2009 в 02:47
  • 2
    Хотя это, вероятно, было бы менее неясным и более читаемым, если бы вы сделали что-то вроде: while (read_string(s) && s.len() > 5). Очевидно, что это не сработает, если read_string не имеет возвращаемого значения (или не имеет значимого). (Изменить: Извините, не заметил, сколько лет этот пост был.) – jamesdlin 25 March 2010 в 09:05
  • 3
    @staticsan Не бойтесь использовать while (1) с выражением break; в теле. Пытаясь заставить прорывную часть кода перейти во время теста или вниз в тест-тест, часто является пустой тратой энергии и делает код более сложным для понимания. – potrzebie 27 September 2012 в 07:20
  • 4
    @jamesdlin ... и люди все еще читают его. Если у вас есть что-то полезное, скажите это. Форумы имеют проблемы с воскрешенными потоками, потому что потоки обычно сортируются по дате последнего сообщения. У StackOverflow таких проблем нет. – Dimitar Slavchev 29 November 2012 в 15:36
  • 5
    @potrzebie Мне нравится подход с запятой гораздо лучше, чем while(1) и break; – Michael 11 March 2016 в 15:19
Другие вопросы по тегам:

Похожие вопросы: