수색…


소개

프로그래밍 언어의 연산자는 컴파일러 나 인터프리터에게 특정 수학, 관계 또는 논리 연산을 수행하고 최종 결과를 생성하도록 지시하는 기호입니다.

C에는 많은 강력한 연산자가 있습니다. 많은 C 연산자는 2 진수 연산자이므로 두 개의 피연산자가 있습니다. 예를 들어 a / b 에서 / 는 두 개의 피연산자 ( a , b )를 허용하는 2 진 연산자입니다. 하나의 피연산자 (예 : ~ , ++ )와 단 하나의 삼항 연산자를 취하는 단항 연산자가 몇 개 ? : .

통사론

  • expr1 연산자
  • 연산자 expr2
  • expr1 연산자 expr2
  • expr1? expr2 : expr3

비고

운영자는 인수에 대응하는 우선 순위와 연관성이있다.

  • Arity 는 피연산자 수를 나타냅니다. C에는 세 가지 연산자가 있습니다.

    • 단항 (1 피연산자)
    • 2 진 (2 피연산자)
    • 3 진 (3 피연산자)
  • 우선 순위 는 먼저 어떤 연산자가 피연산자에 "바인딩"되는지 나타냅니다. 즉, 어떤 연산자가 피연산자에 대해 우선 순위를가집니다. 예를 들어, C 언어는 곱셈과 나눗셈이 덧셈과 뺄셈보다 우선하는 규칙을 따릅니다.

    a * b + c
    

    동일한 결과를 제공합니다.

    (a * b) + c
    

    이것이 원하는 것이 아니라면, 모든 연산자의 우선 순위가 가장 높기 때문에 괄호를 사용하여 우선 순위를 지정할 수 있습니다.

    a * (b + c)
    

    이 새로운 표현식은 이전 두 표현식과 다른 결과를 생성합니다.

    C 언어에는 많은 우선 순위 수준이 있습니다. 모든 연산자의 우선 순위가 내림차순으로 표가 아래에 나와 있습니다.

    우선 순위 표

    연산자 연관성
    () [] -> . 왼쪽에서 오른쪽으로
    ! ~ ++ -- + - * (역 참조) (type) sizeof 오른쪽에서 왼쪽으로
    * (곱하기) / % 왼쪽에서 오른쪽으로
    + - 왼쪽에서 오른쪽으로
    << >> 왼쪽에서 오른쪽으로
    < <= > >= 왼쪽에서 오른쪽으로
    == != 왼쪽에서 오른쪽으로
    & 왼쪽에서 오른쪽으로
    ^ 왼쪽에서 오른쪽으로
    | 왼쪽에서 오른쪽으로
    && 왼쪽에서 오른쪽으로
    || 왼쪽에서 오른쪽으로
    ?: 오른쪽에서 왼쪽으로
    = += -= *= /= %= &= ^= |= <<= >>= 오른쪽에서 왼쪽으로
    , 왼쪽에서 오른쪽으로
  • Associativity 는 우선 순위 연산자가 기본적으로 바인딩되는 방식을 나타내며 왼쪽에서 오른쪽오른쪽에서 왼쪽으로 두 가지 종류가 있습니다. 왼쪽에서 오른쪽 바인딩의 예는 빼기 연산자 ( - )입니다. 표현식

    a - b - c - d
    

    3 개의 동일한 우선 순위 뺄셈을 가졌지 만 동일한 결과를 제공합니다.

    ((a - b) - c) - d
    

    은 가장 왼쪽에 있기 때문에 - 그 두 개의 피연산자에 먼저 결합한다.

    오른쪽에서 왼쪽으로 연관성의 예는 역 참조입니다 * 및 사후 증가 ++ 사업자. 둘 다 동일한 우선 순위를 가지므로 다음과 같은 표현식에서 사용되는 경우

    * ptr ++
    

    , 이것은

    * (ptr ++)
    

    가장 오른쪽, 단항 연산자 ( ++ )가 먼저 단일 피연산자에 바인딩되기 때문입니다.

관계 연산자

관계 연산자는 두 피연산자 간의 특정 관계가 true인지 확인합니다. 결과는 1 ( true ) 또는 0 ( false )을 나타냅니다. 이 결과는 종종 ( if , while , for 를 통해) 제어 흐름에 영향을주기 위해 사용되지만 변수에 저장 될 수도 있습니다.

같음 "=="

제공된 피연산자가 같은지 여부를 확인합니다.

1 == 0;         /* evaluates to 0. */
1 == 1;         /* evaluates to 1. */

int x = 5;
int y = 5;
int *xptr = &x, *yptr = &y;
xptr == yptr;   /* evaluates to 0, the operands hold different location addresses. */
*xptr == *yptr; /* evaluates to 1, the operands point at locations that hold the same value. */

주의 :이 연산자를 대입 연산자 ( = )와 혼동해서는 안됩니다!

같지 않음 "! ="

제공된 피연산자가 같지 않은지 확인합니다.

1 != 0;         /* evaluates to 1. */
1 != 1;         /* evaluates to 0. */

int x = 5;
int y = 5;
int *xptr = &x, *yptr = &y;
xptr != yptr;   /* evaluates to 1, the operands hold different location addresses. */
*xptr != *yptr; /* evaluates to 0, the operands point at locations that hold the same value. */

이 연산자는 equals ( == ) 연산자와 반대 결과를 효과적으로 반환합니다.

"아닙니다!"

객체가 0 인지 확인하십시오.

! 다음과 같이 변수에 직접 사용할 수도 있습니다.

!someVal

이 효과는 다음과 같습니다.

someVal == 0

보다 큼> "

왼쪽 피연산자가 오른쪽 피연산자보다 큰 값인지 확인합니다.

5 > 4      /* evaluates to 1. */
4 > 5      /* evaluates to 0. */
4 > 4      /* evaluates to 0. */

"<"보다 작음

왼쪽 피연산자가 오른쪽 피연산자보다 작은 값을 가지고 있는지 확인합니다.

5 < 4      /* evaluates to 0. */
4 < 5      /* evaluates to 1. */
4 < 4      /* evaluates to 0. */

크거나 같음 "> ="

왼쪽 피연산자가 오른쪽 피연산자보다 크거나 같은 값인지 확인합니다.

5 >= 4      /* evaluates to 1. */
4 >= 5      /* evaluates to 0. */
4 >= 4      /* evaluates to 1. */

작거나 같음 "<="

왼쪽 피연산자가 오른쪽 피연산자보다 작거나 같은지 확인합니다.

5 <= 4      /* evaluates to 0. */
4 <= 5      /* evaluates to 1. */
4 <= 4      /* evaluates to 1. */

배정 연산자

오른쪽 피연산자의 값을 왼쪽 피연산자가 명명 한 저장 위치에 할당하고 값을 반환합니다.

int x = 5;      /* Variable x holds the value 5. Returns 5. */ 
char y = 'c';   /* Variable y holds the value 99. Returns 99 
                 * (as the character 'c' is represented in the ASCII table with 99).
                 */
float z = 1.5;  /* variable z holds the value 1.5. Returns 1.5. */
char const* s = "foo"; /* Variable s holds the address of the first character of the string 'foo'. */

여러 산술 연산에는 복합 대입 연산자가 있습니다.

a += b  /* equal to: a = a + b */
a -= b  /* equal to: a = a - b */
a *= b  /* equal to: a = a * b */
a /= b  /* equal to: a = a / b */
a %= b  /* equal to: a = a % b */
a &= b  /* equal to: a = a & b */
a |= b  /* equal to: a = a | b */
a ^= b  /* equal to: a = a ^ b */
a <<= b /* equal to: a = a << b */
a >>= b /* equal to: a = a >> b */

이러한 화합물 할당의 한 가지 중요한 특징은 왼쪽에있는 표현 ( a )은 한 번만 평가된다는 것입니다. 예 : p 가 포인터 인 경우

*p += 27;

dereferences p 는 한 번만 수행되지만 다음은 두 번 반복됩니다.

*p = *p + 27;

또한 a = b 와 같은 할당 결과는 rvalue 로 알려져 있습니다. 따라서 할당에는 실제로 다른 변수에 할당 할 수있는 값이 있습니다. 이를 통해 하나의 명령문에 여러 변수를 설정하는 할당 체인이 가능합니다.

rvalue 는 다른 표현식 또는 함수 호출의 결과에 대한 일부 코드를 보호하는 if 문 (또는 루프 또는 switch 문)의 제어 표현식에 사용될 수 있습니다. 예 :

char *buffer;
if ((buffer = malloc(1024)) != NULL)
{
    /* do something with buffer */
    free(buffer);
}
else
{
    /* report allocation failure */
}

이 때문에 신비한 버그로 이어질 수있는 일반적인 오타가 발생하지 않도록주의해야합니다.

int a = 2;
/* ... */
if (a = 1)
    /* Delete all files on my hard drive */

이것은, 비참한 결과가있을 것이다 a = 1 항상로 평가됩니다 1 따라서의 제어 표정을하고 if 문은 항상 (이 일반적인 함정에 대한 자세한 내용을 사실 일 것입니다 여기 ). 저자는 다음과 같이 항등 연산자 ( == )를 사용하려고했습니다.

int a = 2;
/* ... */
if (a == 1)
    /* Delete all files on my hard drive */

연산자 연관성

int a, b = 1, c = 2;
a = b = c;

이는 할당 c 위해 b 반환하는 b 할당하는 것보다, . a 이것은 모든 할당 연산자가 올바른 연관성을 가지고 있기 때문에 발생합니다. 즉 표현식의 가장 오른쪽 연산이 먼저 평가되고 오른쪽에서 왼쪽으로 진행된다는 의미입니다.

산술 연산자

기본 산술

관련 수학 연산을 사용하여 왼쪽 피연산자를 오른쪽 피연산자에 적용한 결과 값을 반환합니다. 정류의 일반적인 수학적 규칙이 적용됩니다 (즉, 덧셈과 곱셈은 교환 적, 뺄셈, 나누기 및 모듈러스는 아닙니다).

더하기 연산자

더하기 연산자 ( + )는 두 개의 피연산자를 함께 더하는 데 사용됩니다. 예:

#include <stdio.h>

int main(void)
{
    int a = 5;
    int b = 7;

    int c = a + b; /* c now holds the value 12 */

    printf("%d + %d = %d",a,b,c); /* will output "5 + 7 = 12" */

    return 0;
}

뺄셈 연산자

빼기 연산자 ( - )는 첫 번째 피연산자에서 두 번째 피연산자를 뺍니다. 예:

#include <stdio.h>

int main(void)
{
    int a = 10;
    int b = 7;

    int c = a - b; /* c now holds the value 3 */

    printf("%d - %d = %d",a,b,c); /* will output "10 - 7 = 3" */

    return 0;
}

곱셈 연산자

곱셈 연산자 ( * )는 두 피연산자를 곱하는 데 사용됩니다. 예:

#include <stdio.h>

int main(void)
{    
    int a = 5;
    int b = 7;

    int c = a * b; /* c now holds the value 35 */

    printf("%d * %d = %d",a,b,c); /* will output "5 * 7 = 35" */

    return 0;
}

* 역 참조 연산자와 혼동하지 마십시오.

사업자

나누기 연산자 ( / )는 첫 번째 피연산자를 두 번째 피연산자로 나눕니다. 나누기의 두 피연산자가 정수이면 정수 값을 반환하고 나머지는 버립니다 (나머지를 계산하고 나머지를 얻기 위해 모듈러스 연산자 % 사용).

피연산자 중 하나가 부동 소수점 값이면 결과는 분수의 근사치입니다.

예:

#include <stdio.h>

int main (void)
{
    int a = 19 / 2 ; /* a holds value 9   */
    int b = 18 / 2 ; /* b holds value 9   */
    int c = 255 / 2; /* c holds value 127 */
    int d = 44 / 4 ; /* d holds value 11  */
    double e = 19 / 2.0 ; /* e holds value 9.5   */
    double f = 18.0 / 2 ; /* f holds value 9.0   */
    double g = 255 / 2.0; /* g holds value 127.5 */
    double h = 45.0 / 4 ; /* h holds value 11.25 */

    printf("19 / 2 = %d\n", a);    /* Will output "19 / 2 = 9"    */
    printf("18 / 2 = %d\n", b);    /* Will output "18 / 2 = 9"    */
    printf("255 / 2 = %d\n", c);   /* Will output "255 / 2 = 127" */
    printf("44 / 4 = %d\n", d);    /* Will output "44 / 4 = 11"   */
    printf("19 / 2.0 = %g\n", e);  /* Will output "19 / 2.0 = 9.5"    */
    printf("18.0 / 2 = %g\n", f);  /* Will output "18.0 / 2 = 9"      */
    printf("255 / 2.0 = %g\n", g); /* Will output "255 / 2.0 = 127.5" */
    printf("45.0 / 4 = %g\n", h);  /* Will output "45.0 / 4 = 11.25"  */

    return 0;
}

모듈러스 연산자

모듈러스 연산자 ( % )는 정수 피연산자만을받으며 첫 번째 피연산자가 두 번째 피연산자로 나뉜 후의 나머지를 계산하는 데 사용됩니다. 예:

#include <stdio.h>

int main (void) {
    int a = 25 % 2;    /* a holds value 1  */
    int b = 24 % 2;    /* b holds value 0  */
    int c = 155 % 5;   /* c holds value 0  */
    int d = 49 % 25;   /* d holds value 24 */

    printf("25 % 2 = %d\n", a);     /* Will output "25 % 2 = 1"    */
    printf("24 % 2 = %d\n", b);     /* Will output "24 % 2 = 0"    */
    printf("155 % 5 = %d\n", c);    /* Will output "155 % 5 = 0"   */
    printf("49 % 25 = %d\n", d);    /* Will output "49 % 25 = 24"  */

    return 0;
}

증가 / 감소 연산자

증가 연산자 ( a++ )와 감소 연산자 ( a-- )는 대입 연산자없이 변수의 값을 변경한다는 점에서 다릅니다. 변수 앞이나 뒤에 증감 연산자를 사용할 수 있습니다. 연산자를 배치하면 변수의 값을 변수에 할당하기 전이나 후에 값의 증가 / 감소 시간을 변경합니다. 예:

#include <stdio.h>

int main(void)
{
    int a = 1;
    int b = 4;
    int c = 1;
    int d = 4;

    a++;
    printf("a = %d\n",a);    /* Will output "a = 2" */
    b--;
    printf("b = %d\n",b);    /* Will output "b = 3" */

    if (++c > 1) { /* c is incremented by 1 before being compared in the condition */
        printf("This will print\n");    /* This is printed */
    } else {
        printf("This will never print\n");    /* This is not printed */
    }

    if (d-- < 4) {  /* d is decremented after being compared */
        printf("This will never print\n");    /* This is not printed */
    } else {
        printf("This will print\n");    /* This is printed */
    }
}

cd 대한 예제에서 c 이 두 연산자는 접두사 표기법과 접미사 표기법의 두 가지 형식을가집니다. 두 변수 모두 변수를 증가 ( ++ ) 또는 감소 ( -- ) 할 때와 동일한 효과를 갖지만 반환 값에 따라 다릅니다. 접두어 연산은 먼저 연산을 수행 한 다음 값을 반환하지만 후위 연산은 먼저 반환 된 다음 작업을 수행하십시오.

이러한 잠재적으로 반 직관적 인 동작 때문에 표현식에서 증가 / 감소 연산자를 사용하는 것은 논란의 여지가 있습니다.

논리 연산자

논리적 AND

두 피연산자의 논리 부울 AND-ing을 수행합니다. 피연산자 두 개가 모두 0이 아닌 경우 1을 반환합니다. 논리 AND 연산자는 int 유형입니다.

0 && 0  /* Returns 0. */
0 && 1  /* Returns 0. */
2 && 0  /* Returns 0. */
2 && 3  /* Returns 1. */

논리 OR

피연산자 중 하나가 0이 아닌 경우 1을 반환하는 두 피연산자의 논리 부울 ORing을 수행합니다. 논리 OR 연산자의 유형은 int 입니다.

0 || 0  /* Returns 0. */
0 || 1  /* Returns 1.  */
2 || 0  /* Returns 1.  */
2 || 3  /* Returns 1.  */

논리적 NOT

논리적 부정을 수행합니다. 논리 NOT 연산자는 int 유형입니다. NOT 연산자는 적어도 하나의 비트가 1과 같은지 검사하고, 그렇지 않으면 0을 반환합니다. 그렇지 않으면 1을 반환합니다.

!1 /* Returns 0. */
!5 /* Returns 0. */
!0 /* Returns 1. */

단락 회로 평가

&&|| 모두에 공통적 인 몇 가지 중요한 속성이 있습니다. :

  • 오른손 피연산자 (RHS)가 완전히 평가되기 전에 왼쪽 피연산자 (LHS)가 완전히 평가되고,
  • 왼쪽 피연산자와 오른쪽 피연산자의 평가 사이에 시퀀스 포인트가 있으며,
  • 그리고 가장 중요한 것은 왼쪽 피연산자의 결과가 전체 결과를 결정할 경우 오른쪽 피연산자는 전혀 평가되지 않는다는 것입니다.

이는 다음을 의미합니다.

  • LHS가 '참'(0이 아닌)으로 평가되면, || ( 'true OR anything'의 결과가 'true'이기 때문에) 평가되지 않으며,
  • LHS가 '거짓'(제로)으로 평가되면 && 의 RHS는 평가되지 않습니다 ( '거짓 AND 아무것도'의 결과가 '거짓'이므로).

이것은 다음과 같은 코드를 작성할 수 있기 때문에 중요합니다.

const char *name_for_value(int value)
{
    static const char *names[] = { "zero", "one", "two", "three", };
    enum { NUM_NAMES = sizeof(names) / sizeof(names[0]) };
    return (value >= 0 && value < NUM_NAMES) ? names[value] : "infinity";
}

음수 값이 함수에 전달되면 value >= 0value < NUM_NAMES 은 거짓으로 평가되고 value < NUM_NAMESvalue < NUM_NAMES 은 계산되지 않습니다.

증가 / 감소

증가와 감소 연산자는 접두사와 접미사의 형태로 존재한다.

int a = 1;
int b = 1;
int tmp = 0;

tmp = ++a;        /* increments a by one, and returns new value; a == 2, tmp == 2  */
tmp = a++;        /* increments a by one, but returns old value; a == 3, tmp == 2 */
tmp = --b;        /* decrements b by one, and returns new value; b == 0, tmp == 0 */
tmp = b--;        /* decrements b by one, but returns old value; b == -1, tmp == 0 */

산술 연산은 시퀀스 포인트를 도입하지 않으므로 ++ 또는 -- 연산자를 사용하는 특정 표현식은 정의되지 않은 동작을 초래할 수 있습니다 .

조건부 연산자 / 삼항 연산자

첫 번째 피연산자를 계산하고 결과 값이 0이 아니면 두 번째 피연산자를 계산합니다. 그렇지 않으면 다음 예제와 같이 세 번째 피연산자를 계산합니다.

a = b ? c : d;

다음과 같습니다.

if (b)
    a = c;
else 
    a = d;

이 의사 코드는 그것을 나타냅니다 : condition ? value_if_true : value_if_false . 각 값은 평가 된 표현식의 결과 일 수 있습니다.

int x = 5;
int y = 42;
printf("%i, %i\n", 1 ? x : y, 0 ? x : y); /* Outputs "5, 42" */

조건부 연산자는 중첩 될 수 있습니다. 예를 들어 다음 코드는 세 개의 숫자 중 더 큰 숫자를 결정합니다.

big= a > b ? (a > c ? a : c)
           : (b > c ? b : c);

다음 예제는 정수를 짝수 개의 파일에 짝수 정수를 다른 파일에 씁니다.

#include<stdio.h>

int main()
{
    FILE *even, *odds;
    int n = 10;
    size_t k = 0;

    even = fopen("even.txt", "w");
    odds = fopen("odds.txt", "w");

    for(k = 1; k < n + 1; k++)
    {
        k%2==0 ? fprintf(even, "\t%5d\n", k)
               : fprintf(odds, "\t%5d\n", k);
    }
    fclose(even);
    fclose(odds);

    return 0;
}

조건부 연산자는 오른쪽에서 왼쪽으로 연결됩니다. 다음을 고려하세요:

exp1 ? exp2 : exp3 ? exp4 : exp5

협회가 오른쪽에서 왼쪽으로 진행됨에 따라 위의 표현식은 다음과 같이 평가됩니다.

exp1 ? exp2 : ( exp3 ? exp4 : exp5 )

쉼표 연산자

왼쪽 피연산자를 계산하고 결과 값을 버린 다음 오른쪽 피연산자와 결과를 평가하여 오른쪽 피연산자의 값을 산출합니다.

int x = 42, y = 42;
printf("%i\n", (x *= 2, y)); /* Outputs "42". */

쉼표 연산자는 피연산자 사이에 시퀀스 포인트 를 도입합니다.

기능에 사용되는 쉼표로 분리 된 인수가 오히려이 쉼표 연산자는 다른 분리라고, 쉼표 연산자 아님을 호출합니다. 따라서 쉼표 연산자 의 속성을 갖지 않습니다.

위의 printf() 호출에는 쉼표 연산자구분 기호 가 모두 들어 있습니다.

printf("%i\n", (x *= 2, y)); /* Outputs "42". */
/*           ^        ^ this is a comma operator */
/*           this is a separator */

쉼표 연산자는 초기화 섹션과 for 루프의 업데이트 섹션에서 자주 사용됩니다. 예 :

for(k = 1; k < 10; printf("\%d\\n", k), k += 2);   /*outputs the odd numbers below 9/*

/* outputs sum to first 9 natural numbers */
for(sumk = 1, k = 1; k < 10; k++, sumk += k)
    printf("\%5d\%5d\\n", k, sumk);

캐스트 오퍼레이터

지정된 식을 평가 한 결과 값에서 지정된 형식 으로 명시 적 변환을 수행합니다.

int x = 3;
int y = 4;
printf("%f\n", (double)x / y); /* Outputs "0.750000". */

여기에서 x 의 값은 double 로 변환되고, division은 y 의 값을 double 승격시키고 나눗셈의 결과는 doubleprintf 에 전달되어 인쇄됩니다.

sizeof 연산자

타입을 피연산자로 사용

주어진 유형의 객체의 size_t 유형의 바이트로 평가합니다. 유형 주변에 괄호가 필요합니다.

printf("%zu\n", sizeof(int)); /* Valid, outputs the size of an int object, which is platform-dependent. */
printf("%zu\n", sizeof int); /* Invalid, types as arguments need to be surrounded by parentheses! */

피연산자로 표현식 사용

주어진 표현식 유형의 객체를 size_t 유형의 바이트로 평가합니다. 표현 자체는 평가되지 않습니다. 괄호는 필요하지 않습니다. 그러나 주어진 표현식은 단항 적이어야하기 때문에 항상 사용하는 것이 가장 좋습니다.

char ch = 'a';
printf("%zu\n", sizeof(ch)); /* Valid, will output the size of a char object, which is always 1 for all platforms. */
printf("%zu\n", sizeof ch);  /* Valid, will output the size of a char object, which is always 1 for all platforms. */

포인터 산술

포인터 추가

포인터와 스칼라 유형 N 주어지면 메모리의 가리키는 대상을 직접 계승하는 가리킨 형식의 N 번째 요소에 대한 포인터로 계산됩니다.

int arr[] = {1, 2, 3, 4, 5};
printf("*(arr + 3) = %i\n", *(arr + 3)); /* Outputs "4", arr's fourth element. */

포인터가 피연산자 값 또는 스칼라 값으로 사용되는지는 중요하지 않습니다. 이것은 3 + arr 과 같은 것이 유효 함을 의미합니다. arr[k] 가 배열의 k+1 멤버이면 arr+karr[k] 대한 포인터입니다. 즉, arr 또는 arr+0arr[0] 에 대한 포인터이고 arr+1arr[2] 대한 포인터입니다. 일반적으로 *(arr+k)arr[k] .

통상의 산술과는 달리, int 에 대한 포인터에 1 을 추가하면 현재 주소 값에 4 바이트가 추가됩니다. 배열 이름은 상수 포인터이므로 + 는 배열 이름을 사용하여 포인터 표기법을 통해 배열 멤버에 액세스하는 데 사용할 수있는 유일한 연산자입니다. 그러나 배열에 대한 포인터를 정의하면 배열에서 데이터를보다 유연하게 처리 할 수 ​​있습니다. 예를 들어 다음과 같이 배열의 멤버를 인쇄 할 수 있습니다.

#include<stdio.h>
static const size_t N = 5
    
int main()
{
    size_t k = 0;
    int arr[] = {1, 2, 3, 4, 5};
    for(k = 0; k < N; k++)
    {
        printf("\n\t%d", *(arr + k));
    }
    return 0;
}

배열에 대한 포인터를 정의하면 위의 프로그램은 다음과 같습니다.

#include<stdio.h>
static const size_t N = 5
    
int main()
{
    size_t k = 0;
    int arr[] = {1, 2, 3, 4, 5};
    int *ptr = arr; /* or int *ptr = &arr[0]; */
    for(k = 0; k < N; k++)
    {
        printf("\n\t%d", ptr[k]);
        /* or   printf("\n\t%d", *(ptr + k)); */
        /* or   printf("\n\t%d", *ptr++); */
    }
    return 0;
}

배열 arr 의 멤버는 연산자 +++ 사용하여 액세스 할 수 있습니다. 포인터 ptr 과 함께 사용할 수있는 다른 연산자는 --- 입니다.

포인터 뺄셈

동일한 유형에 대한 두 포인터가 주어지면 첫 번째 포인터의 값을 얻기 위해 두 번째 포인터에 추가해야하는 스칼라 값을 보유하는 ptrdiff_t 유형의 객체로 평가됩니다.

int arr[] = {1, 2, 3, 4, 5};
int *p = &arr[2];
int *q = &arr[3];
ptrdiff_t diff = q - p;

printf("q - p = %ti\n", diff); /* Outputs "1". */
printf("*(p + (q - p)) = %d\n", *(p + diff)); /* Outputs "4". */

액세스 연산자

멤버 액세스 연산자 (도트 . 그리고 화살표 -> ) (A)의 부재에 액세스하는 데 사용되는 struct .

객체의 멤버

액세스 된 객체의 멤버 인 객체를 나타내는 lvalue를 평가합니다.

struct MyStruct
{
    int x;
    int y;
};

struct MyStruct myObject;
myObject.x = 42;
myObject.y = 123;

printf(".x = %i, .y = %i\n", myObject.x, myObject.y); /* Outputs ".x = 42, .y = 123". */

지적 객체의 멤버

역 참조를위한 구문 설탕 다음에 멤버 액세스. 사실 x->y 형식의 표현식은 (*x).y 대한 속기이지만 - 특히 구조체 포인터가 중첩 된 경우 화살표 연산자가 훨씬 명확합니다.

struct MyStruct
{
    int x;
    int y;
};

struct MyStruct myObject;
struct MyStruct *p = &myObject;

p->x = 42;
p->y = 123;

printf(".x = %i, .y = %i\n", p->x, p->y); /* Outputs ".x = 42, .y = 123". */
printf(".x = %i, .y = %i\n", myObject.x, myObject.y); /* Also outputs ".x = 42, .y = 123". */

주소

단항 & 연산자는 연산자의 주소입니다. 주어진 표현식을 평가합니다. 여기서 결과 객체는 lvalue 여야합니다. 그런 다음 유형이 결과 객체 유형에 대한 포인터 인 객체로 평가하고 결과 객체의 주소를 포함합니다.

int x = 3;
int *p = &x;
printf("%p = %p\n", (void *)&x, (void *)p); /* Outputs "A = A", for some implementation-defined A. */

비표준

단항 * 연산자는 포인터를 역 참조합니다. 주어진 표현식을 평가 한 결과로 생성 된 포인터의 역 참조로 인해 lvalue로 평가됩니다.

int x = 42;
int *p = &x;
printf("x = %d, *p = %d\n", x, *p); /* Outputs "x = 42, *p = 42". */

*p = 123;
printf("x = %d, *p = %d\n", x, *p); /* Outputs "x = 123, *p = 123". */

색인 생성

인덱싱은 포인터 추가에 대한 구문 설탕과 역 참조입니다. 효과적으로 a[i] 형식의 표현식은 *(a + i) 와 동일하지만 명시적인 첨자 표기가 선호됩니다.

int arr[] = { 1, 2, 3, 4, 5 };
printf("arr[2] = %i\n", arr[2]); /* Outputs "arr[2] = 3". */

색인 생성의 호환성

정수에 대한 포인터를 추가하는 것은 교환 가능 연산입니다 (즉, 피연산자의 순서는 결과를 변경하지 않습니다). 그래서 pointer + integer == integer + pointer .

이것의 결과는 arr[3]3[arr] 가 동일하다는 것입니다.

printf("3[arr] = %i\n", 3[arr]); /* Outputs "3[arr] = 4". */

표현식의 사용법 arr[3] 대신 3[arr] 은 코드 가독성에 영향을주기 때문에 일반적으로 권장하지 않습니다. 그것은 난독 화 된 프로그래밍 콘테스트에서 인기가있는 경향이 있습니다.

함수 호출 연산자

첫 번째 피연산자는 함수 포인터 여야합니다 (함수 지시자는 함수에 대한 포인터로 변환되기 때문에 받아 들여질 수 있음). 호출 할 함수를 식별하고, 다른 모든 피연산자가 있으면 함수 호출의 인수로 통칭됩니다 . 해당 인수를 사용하여 적절한 함수를 호출 한 결과로 반환되는 값을 계산합니다.

int myFunction(int x, int y)
{
    return x * 2 + y;
}

int (*fn)(int, int) = &myFunction;
int x = 42;
int y = 123;

printf("(*fn)(%i, %i) = %i\n", x, y, (*fn)(x, y)); /* Outputs "fn(42, 123) = 207". */
printf("fn(%i, %i) = %i\n", x, y, fn(x, y)); /* Another form: you don't need to dereference explicitly */

비트 연산자

비트 연산자는 변수에 비트 수준 연산을 수행하는 데 사용할 수 있습니다.
다음은 C에서 지원되는 6 개의 비트 연산자 목록입니다.

상징 운영자
& 비트 AND
| 비트 포함 OR
^ 비트 배타적 논리합 (XOR)
~ ~ 비트가 아닌 (1의 보수)
<< 논리적 좌 쉬프트
>> 논리 오른쪽 시프트

다음 프로그램은 모든 비트 연산자의 사용법을 보여줍니다.

#include <stdio.h>

int main(void)
{
   unsigned int a = 29;    /* 29 = 0001 1101 */  
   unsigned int b = 48;    /* 48 = 0011 0000 */
   int c = 0;           

   c = a & b;              /* 32 = 0001 0000 */ 
   printf("%d & %d = %d\n", a, b, c );

   c = a | b;              /* 61 = 0011 1101 */
   printf("%d | %d = %d\n", a, b, c );

   c = a ^ b;              /* 45 = 0010 1101 */
   printf("%d ^ %d = %d\n", a, b, c );

   c = ~a;                 /* -30 = 1110 0010 */
   printf("~%d = %d\n", a, c );

   c = a << 2;             /* 116 = 0111 0100 */
   printf("%d << 2 = %d\n", a, c );

   c = a >> 2;             /* 7 = 0000 0111 */
   printf("%d >> 2 = %d\n", a, c );

   return 0;
}

이러한 비트 표현의 부호 비트가 특별한 의미를 가지기 때문에 부호있는 유형의 비트 연산은 피해야합니다. 시프트 연산자에는 다음과 같은 특정 제한 사항이 적용됩니다.

  • 1 비트를 부호있는 비트로 왼쪽 시프 팅하는 것은 잘못된 것이며 정의되지 않은 동작으로 연결됩니다.

  • 음의 값 (부호 비트 1)을 오른쪽으로 시프트하는 것은 구현 정의이므로 이식 할 수 없습니다.

  • 시프트 연산자의 오른쪽 피연산자의 값이 음수이거나 승격 된 왼쪽 피연산자의 너비보다 크거나 같은 경우 비헤이비어가 정의되지 않습니다.

마스킹 :

마스킹은 논리 비트 연산을 사용하여 변수에서 원하는 비트를 추출 (또는 원하는 비트를 변환)하는 프로세스를 나타냅니다. 마스킹을 수행하는 데 사용되는 피연산자 (상수 또는 변수)를 마스크 라고합니다.

마스킹은 다양한 방법으로 사용됩니다.

  • 정수 변수의 비트 패턴을 결정합니다.
  • 주어진 비트 패턴의 일부를 새로운 변수에 복사하는 반면, 새로운 변수의 나머지는 0으로 채 웁니다 (비트 AND를 사용)
  • 주어진 비트 패턴의 일부를 새로운 변수에 복사하는 반면, 새로운 변수의 나머지는 1로 채 웁니다 (비트 OR을 사용).
  • 주어진 비트 패턴의 일부를 새로운 변수에 복사하는 반면, 원래의 비트 패턴의 나머지는 새로운 변수 내에서 반전됩니다 (비트 배타적 OR 사용).

다음 함수는 마스크를 사용하여 변수의 비트 패턴을 표시합니다.

#include <limits.h>
void bit_pattern(int u)
{
    int i, x, word;
    unsigned mask = 1;
    word = CHAR_BIT * sizeof(int);
    mask = mask << (word - 1);    /* shift 1 to the leftmost position */
    for(i = 1; i <= word; i++)
    {
        x = (u & mask) ? 1 : 0;  /* identify the bit */
        printf("%d", x);         /* print bit value */
        mask >>= 1;              /* shift mask to the right by 1 bit */
    }
}

_Alignof

C11

지정된 형식에 대한 정렬 요구 사항을 쿼리합니다. 정렬 요구 사항은 유형의 두 객체가 할당 될 수있는 바이트 수를 나타내는 2의 양의 정수입니다. C에서 정렬 요구 사항은 size_t 에서 측정됩니다.

유형 이름은 불완전 유형이나 함수 유형이 아닐 수도 있습니다. 배열이 형식으로 사용되는 경우 배열 요소의 형식이 사용됩니다.

이 연산자는 종종 <stdalign.h> 의 편의 매크로 alignof 를 통해 액세스됩니다.

int main(void)
{
    printf("Alignment of char = %zu\n", alignof(char));
    printf("Alignment of max_align_t = %zu\n", alignof(max_align_t));
    printf("alignof(float[10]) = %zu\n", alignof(float[10]));
    printf("alignof(struct{char c; int n;}) = %zu\n",
            alignof(struct {char c; int n;}));    
}

가능한 출력 :

Alignment of char = 1
Alignment of max_align_t = 16
alignof(float[10]) = 4
alignof(struct{char c; int n;}) = 4

http://en.cppreference.com/w/c/language/_Alignof

논리 연산자의 단락 회로 동작

단락 회로는 가능한 경우 (if / while / ...) 조건의 부분을 건너 뛰는 기능입니다. 두 개의 피연산자에 대한 논리 연산의 경우 첫 번째 피연산자가 평가되고 (참 또는 거짓으로) 평가가있는 경우 (예 : &&를 사용할 때 첫 번째 피연산자가 false이면 첫 번째 피연산자는 ||을 사용하면 참입니다) 두 번째 피연산자는 다음과 같습니다. 평가되지 않은.

예:

#include <stdio.h>
 
int main(void) {
  int a = 20;
  int b = -5;
 
  /* here 'b == -5' is not evaluated,
     since a 'a != 20' is false. */
  if (a != 20 && b == -5) {
    printf("I won't be printed!\n");
  }
   
  return 0;
}

직접 확인해보십시오.

#include <stdio.h>
 
int print(int i) {
  printf("print function %d\n", i);
  return i;
}
 
int main(void) {
  int a = 20;
 
  /* here 'print(a)' is not called,
     since a 'a != 20' is false. */
  if (a != 20 && print(a)) {
    printf("I won't be printed!\n");
  }

  /* here 'print(a)' is called,
     since a 'a == 20' is true. */
  if (a == 20 && print(a)) {
    printf("I will be printed!\n");
  }

  return 0;
}

산출:

$ ./a.out
print function 20
I will be printed!

짧은 계산은 (계산적으로) 비용이 많이 드는 용어를 평가하지 않으려는 경우 중요합니다. 또한,이 경우와 같이 프로그램의 흐름에 크게 영향을 줄 수 있습니다. 왜이 프로그램이 "forked!" 4 번?



Modified text is an extract of the original Stack Overflow Documentation
아래 라이선스 CC BY-SA 3.0
와 제휴하지 않음 Stack Overflow