C 쉼표 연산자 사용
이 질문에 이미 답변이 있습니다.
- 쉼표 연산자는 무엇을합니까? 8 답변
for 루프 문에서 사용되는 것을 볼 수 있지만 어디에서나 법적 구문입니다. 다른 곳에서는 어떤 용도로 사용 되었습니까?
C 언어 (C ++ 포함)는 역사적으로 완전히 다른 두 가지 프로그래밍 스타일의 혼합으로, 하나는 "문장 프로그래밍"과 "표현식 프로그래밍"이라고 할 수 있습니다. 아시다시피 모든 절차 적 프로그래밍 언어는 일반적으로 시퀀싱 및 분기 와 같은 기본 구조를 지원합니다 ( 구조적 프로그래밍 참조 ). 이러한 기본 구조는 C / C ++ 언어에 두 가지 형태로 존재합니다. 하나는 문 프로그래밍 용이고 다른 하나는 표현식 프로그래밍 용입니다.
예를 들어, 명령문으로 프로그램을 작성할 때로 구분 된 일련의 명령문을 사용할 수 있습니다 ;
. 분기를 수행하려면 if
문 을 사용 합니다. 순환 및 기타 제어 전송 명령문을 사용할 수도 있습니다.
표현식 프로그래밍에서 동일한 구조를 사용할 수도 있습니다. 이것은 실제로 ,
운영자가 작동하는 곳 입니다. 운영자는 ,
C의 연속 식의 구분보다는 다른 아무것도, 즉 운전자 ,
표현 프로그래밍은 같은 역할을 역할을 ;
문 프로그래밍을 수행합니다. 식 프로그래밍에서 분기는 ?:
연산자를 통해 수행 &&
되며 또는 ||
연산자 및 연산자 의 단락 평가 속성을 통해 수행됩니다 . (표현식 프로그래밍에는 순환이 없습니다. 순환으로 대체하려면 명령문 프로그래밍을 적용해야합니다.)
예를 들어, 다음 코드
a = rand();
++a;
b = rand();
c = a + b / 2;
if (a < c - 5)
d = a;
else
d = b;
이는 전통적인 문장 프로그래밍의 예이며, 표현 프로그래밍 측면에서 다음과 같이 다시 작성할 수 있습니다.
a = rand(), ++a, b = rand(), c = a + b / 2, a < c - 5 ? d = a : d = b;
또는
a = rand(), ++a, b = rand(), c = a + b / 2, d = a < c - 5 ? a : b;
또는
d = (a = rand(), ++a, b = rand(), c = a + b / 2, a < c - 5 ? a : b);
또는
a = rand(), ++a, b = rand(), c = a + b / 2, (a < c - 5 && (d = a, 1)) || (d = b);
말할 필요도없이, 실제로 명령문 프로그래밍은 일반적으로 훨씬 더 읽기 쉬운 C / C ++ 코드를 생성하므로 일반적으로 매우 잘 측정되고 제한된 양으로 표현 프로그래밍을 사용합니다. 그러나 많은 경우에 편리합니다. 그리고 받아 들일 수있는 것과 그렇지 않은 것 사이의 경계는 대체로 개인적인 취향과 기존 관용구를 인식하고 읽을 수있는 능력의 문제입니다.
추가 참고 사항 : 언어의 디자인 자체가 진술에 맞게 조정되었습니다. 문은 자유롭게 식을 호출 할 수 있지만 식은 미리 정의 된 함수를 호출하는 것 외에는 문을 호출 할 수 없습니다. 이 상황은 GCC 컴파일러에서 다소 흥미로운 방식으로 변경됩니다. GCC 컴파일러는 "문 표현식" 을 확장으로 지원합니다 (표준 C의 "표현 문"에 대칭). "문 표현식"을 사용하면 표준 C의 명령문에 표현식 기반 코드를 삽입 할 수있는 것처럼 사용자가 명령문 기반 코드를 표현식에 직접 삽입 할 수 있습니다.
또 다른 추가 사항 : C ++ 언어에서 functor 기반 프로그래밍은 중요한 역할을하며 이는 "표현식 프로그래밍"의 또 다른 형태로 볼 수 있습니다. C ++ 디자인의 현재 추세에 따르면 많은 상황에서 기존 명령문 프로그래밍보다 선호되는 것으로 간주 될 수 있습니다.
일반적으로 C의 쉼표는 놓치기가 매우 쉽기 때문에 사용하기에 좋은 스타일이 아니라고 생각합니다. 다른 사람이 코드를 읽고 / 이해 / 수정하려고하거나, 한 달이 지나면 여러분 자신이 직접 사용합니다. 물론 변수 선언 및 for 루프 외부에서 관용적입니다.
예를 들어 여러 명령문을 삼항 연산자 (? :), ala로 압축하는 데 사용할 수 있습니다.
int x = some_bool ? printf("WTF"), 5 : fprintf(stderr, "No, really, WTF"), 117;
하지만 내 신들, 왜?!? (실제 코드에서 이런 식으로 사용되는 것을 보았지만 불행히도 보여줄 액세스 권한이 없습니다)
매크로가 함수 인 척하고 값을 반환하고 싶지만 다른 작업을 먼저 수행해야하는 매크로에서 사용되는 것을 보았습니다. 항상 추하고 종종 위험한 해킹처럼 보입니다.
단순화 된 예 :
#define SomeMacro(A) ( DoWork(A), Permute(A) )
여기서 B=SomeMacro(A)
Permute (A)의 결과를 "반환"하고 "B"에 할당합니다.
C ++의 두 가지 킬러 쉼표 연산자 기능 :
a) 특정 문자열이 나타날 때까지 스트림에서 읽습니다 (코드를 DRY 유지하는 데 도움이 됨).
while (cin >> str, str != "STOP") {
//process str
}
b) 생성자 이니셜 라이저에서 복잡한 코드를 작성합니다.
class X : public A {
X() : A( (global_function(), global_result) ) {};
};
잠금이 대기 하기 전에 메시지를 넣으려면 쉼표를 사용하여 뮤텍스 잠금을 디버그 해야 했습니다.
파생 된 잠금 생성자의 본문에있는 로그 메시지를 제외 할 수 없었기 때문에 초기화 목록에서 baseclass ((log ( "message"), actual_arg))를 사용하여 기본 클래스 생성자의 인수에 넣어야했습니다. 추가 괄호에 유의하십시오.
다음은 클래스의 발췌입니다.
class NamedMutex : public boost::timed_mutex
{
public:
...
private:
std::string name_ ;
};
void log( NamedMutex & ref__ , std::string const& name__ )
{
LOG( name__ << " waits for " << ref__.name_ );
}
class NamedUniqueLock : public boost::unique_lock< NamedMutex >
{
public:
NamedUniqueLock::NamedUniqueLock(
NamedMutex & ref__ ,
std::string const& name__ ,
size_t const& nbmilliseconds )
:
boost::unique_lock< NamedMutex >( ( log( ref__ , name__ ) , ref__ ) ,
boost::get_system_time() + boost::posix_time::milliseconds( nbmilliseconds ) ),
ref_( ref__ ),
name_( name__ )
{
}
....
};
부스트 할당 라이브러리는 유용하고 읽기 쉬운 방식으로 쉼표 연산자를 오버로드의 좋은 예입니다. 예를 들면 :
using namespace boost::assign;
vector<int> v;
v += 1,2,3,4,5,6,7,8,9;
C 표준에서 :
쉼표 연산자의 왼쪽 피연산자는 void 표현식으로 평가됩니다. 평가 후 시퀀스 포인트가 있습니다. 그런 다음 오른쪽 피연산자가 평가됩니다. 결과에는 유형과 값이 있습니다. (쉼표 연산자는 lvalue를 생성하지 않습니다.)) 쉼표 연산자의 결과를 수정하거나 다음 시퀀스 지점 이후에 액세스하려고 시도하면 동작이 정의되지 않습니다.
In short it let you specify more than one expression where C expects only one. But in practice it's mostly used in for loops.
Note that:
int a, b, c;
is NOT the comma operator, it's a list of declarators.
It is sometimes used in macros, such as debug macros like this:
#define malloc(size) (printf("malloc(%d)\n", (int)(size)), malloc((size)))
(But look at this horrible failure, by yours truly, for what can happen when you overdo it.)
But unless you really need it, or you are sure that it makes the code more readable and maintainable, I would recommend against using the comma operator.
You can overload it (as long as this question has a "C++" tag). I have seen some code, where overloaded comma was used for generating matrices. Or vectors, I don't remember exactly. Isn't it pretty (although a little confusing):
MyVector foo = 2, 3, 4, 5, 6;
Outside of a for loop, and even there is has can have an aroma of code smell, the only place I've seen as a good use for the comma operator is as part of a delete:
delete p, p = 0;
The only value over the alternative is you can accidently copy/paste only half of this operation if it is on two lines.
I also like it because if you do it out of habit, you'll never forget the zero assignment. (Of course, why p isn't inside somekind of auto_ptr, smart_ptr, shared_ptr, etc wrapper is a different question.)
Given @Nicolas Goy's citation from the standard, then it sounds like you could write one-liner for loops like:
int a, b, c;
for(a = 0, b = 10; c += 2*a+b, a <= b; a++, b--);
printf("%d", c);
But good God, man, do you really want to make your C code more obscure in this way?
In general I avoid using the comma operator because it just makes code less readable. In almost all cases, it would be simpler and clearer to just make two statements. Like:
foo=bar*2, plugh=hoo+7;
offers no clear advantage over:
foo=bar*2;
plugh=hoo+7;
The one place besides loops where I have used it it in if/else constructs, like:
if (a==1)
... do something ...
else if (function_with_side_effects_including_setting_b(), b==2)
... do something that relies on the side effects ...
You could put the function before the IF, but if the function takes a long time to run, you might want to avoid doing it if it's not necessary, and if the function should not be done unless a!=1, then that's not an option. The alternative is to nest the IF's an extra layer. That's actually what I usually do because the above code is a little cryptic. But I've done it the comma way now and then because nesting is also cryptic.
It's very useful in adding some commentary into ASSERT
macros:
ASSERT(("This value must be true.", x));
Since most assert style macros will output the entire text of their argument, this adds an extra bit of useful information into the assertion.
I often use it to run a static initializer function in some cpp files, to avoid lazy initalization problems with classic singletons:
void* s_static_pointer = 0;
void init() {
configureLib();
s_static_pointer = calculateFancyStuff(x,y,z);
regptr(s_static_pointer);
}
bool s_init = init(), true; // just run init() before anything else
Foo::Foo() {
s_static_pointer->doStuff(); // works properly
}
For me the one really useful case with commas in C is using them to perform something conditionally.
if (something) dothis(), dothat(), x++;
this is equivalent to
if (something) { dothis(); dothat(); x++; }
This is not about "typing less", it's just looks very clear sometimes.
Also loops are just like that:
while(true) x++, y += 5;
Of course both can only be useful when the conditional part or executable part of the loop is quite small, two-three operations.
The only time I have ever seen the ,
operator used outside a for
loop was to perform an assingment in a ternary statement. It was a long time ago so I cannot remeber the exact statement but it was something like:
int ans = isRunning() ? total += 10, newAnswer(total) : 0;
Obviously no sane person would write code like this, but the author was an evil genius who construct c statements based on the assembler code they generated, not readability. For instance he sometimes used loops instead of if statements because he preferred the assembler it generated.
His code was very fast but unmaintainable, I am glad I don't have to work with it any more.
I've used it for a macro to "assign a value of any type to an output buffer pointed to by a char*, and then increment the pointer by the required number of bytes", like this:
#define ASSIGN_INCR(p, val, type) ((*((type) *)(p) = (val)), (p) += sizeof(type))
Using the comma operator means the macro can be used in expressions or as statements as desired:
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));
It reduced some repetitive typing but you do have to be careful it doesn't get too unreadable.
Please see my overly-long version of this answer here.
It can be handy for "code golf":
The ,
in if(i>0)t=i,i=0;
saves two characters.
qemu has some code that uses the comma operator within the conditional portion of a for loop (see QTAILQ_FOREACH_SAFE in qemu-queue.h). What they did boils down to the following:
#include <stdio.h>
int main( int argc, char* argv[] ){
int x = 0, y = 0;
for( x = 0; x < 3 && (y = x+1,1); x = y ){
printf( "%d, %d\n", x, y );
}
printf( "\n%d, %d\n\n", x, y );
for( x = 0, y = x+1; x < 3; x = y, y = x+1 ){
printf( "%d, %d\n", x, y );
}
printf( "\n%d, %d\n", x, y );
return 0;
}
... with the following output:
0, 1
1, 2
2, 3
3, 3
0, 1
1, 2
2, 3
3, 4
The first version of this loop has the following effects:
- It avoids doing two assignments, so the chances of the code getting out of sync is reduced
- Since it uses
&&
, the assignment is not evaluated after the last iteration - Since the assignment isn't evaluated, it won't try to de-reference the next element in the queue when it's at the end (in qemu's code, not the code above).
- Inside the loop, you have access to the current and next element
Found it in array initialization:
In C what exactly happens if i use () to initialize a double dimension array instead of the {}?
When I initialize an array a[][]
:
int a[2][5]={(8,9,7,67,11),(7,8,9,199,89)};
and then display the array elements.
I get:
11 89 0 0 0
0 0 0 0 0
참고URL : https://stackoverflow.com/questions/1613230/uses-of-c-comma-operator
'Programing' 카테고리의 다른 글
CSS로 비스듬히 치는 방법 (0) | 2020.09.09 |
---|---|
Twitter Bootstrap의 태그가 제대로 작동하지 않습니까? (0) | 2020.09.09 |
파이썬에 단어를 목록으로 분할하는 기능이 있습니까? (0) | 2020.09.09 |
"프로시 저는 'ntext / nchar / nvarchar'유형의 '@statement'매개 변수를 예상합니다."라는 메시지가 표시되는 이유는 무엇입니까? (0) | 2020.09.09 |
월 및 연도 기준으로 만 mySQL 선택 (0) | 2020.09.09 |