"C 트랩과 결함"----제2장 구문 트랩

2.1 함수 선언 이해하기

2.1.1 함수 선언을 이해하는 방법

먼저 문장을 보면 다음과 같습니다.

(*(void(*)())0)();

이러한 표현식을 구성하는 데는 단 하나의 간단한 규칙이 있습니다. 사용되는 방식으로 선언하십시오.

모든 C 변수의 선언은 두 부분으로 구성됩니다. 유형 및 식과 유사한 선언자 집합입니다. 선언자는 표면적으로 표현식과 유사하며 해당 평가는 선언에 제공된 유형의 결과를 반환해야 합니다.

  1. 간단한 변수 선언:

    float f;
    //含义:当对f进行求值时,表达式f的类型为浮点数类型。因为声明符与表达式类似,所以也可以在声明符中任意使用括号:
    float ((f));
    //含义:((f))的类型是浮点类型,由此可以推知,f也是浮点类型。
    
  2. 함수 및 포인터 유형 선언

    float ff();
    //含义:表达式ff()的求值结果是一个浮点数,也就是说,ff是一个返回值为浮点类型的函数。
    float *pf;
    //含义:*pf是一个浮点数,也就是说,pf是一个指向浮点数的指针。
    

결론: 선언에서 변수 이름을 제거하고 선언 끝에 세미콜론을 제거한 다음 나머지를 괄호로 "캡슐화"하여 캐스터를 가져옵니다.

참고: 아래에 함수 포인터가 있습니다.

void (*fp)();

fp는 함수 포인터이므로 *fp는 포인터가 가리키는 함수이므로 (*fp)() 함수를 호출하는 방법입니다. ANSI C 표준에서는 프로그래머가 위의 표현을 fp()로 축약할 수 있습니다. 그러나 이 표기법은 약칭일 뿐입니다.

식 (*fp)()에서 함수 연산자()가 단항 연산자 *보다 우선 순위가 높기 때문에 *fp 주위의 괄호는 매우 중요합니다. *fp 주위에 괄호가 없으면 *fp()는 실제로 ANSI C가 *((*fp)())의 약어로 사용하는 *(fp())와 정확히 같은 의미를 갖습니다. (*fp와 fp는 등가, 즉 위의 정리가 적용됨)

2.1.2 예제로 문장 이해하기

2.1.2.1 예 1

(*(void(*)())0)();
//将0进行强制类型转换为(void (*)()),即函数指针类型,然后对其进行解引用,解引用之后就找到了那个函数,然后再进行函数调用
//此处就是把0当作是一个地址,本质上就是一个函数的调用

2.1.2.2 예 2

void (*signal(int,void(*)(int)))(int);
//首先signal先和圆括号先结合,形成一个函数,这个函数有两个参数,参数1是一个整数,参数2是一个函数指针,该函数指针指向的函数的参数为int类型,返回类型为void类型,signal函数的返回类型为void(*)(int),即一个函数指针类型,该函数指针指向的函数参数类型为int,返回类型为void。

2.2 연산자 우선순위

연산자 우선 순위 요약

이미지-20220302211047449

이미지-20220302211107657

이미지-20220302211134778

2.2.1 일반적인 실수

2.2.1.1 오류 예 1

if(flags & FLAG!=0 )

if 조건문의 판단조건으로 flag와 FLAG&의 결과를 사용하고자 하지만 우선순위의 문제로 인해 위의 표현을 다음과 같이 조합하여 표현의 의미가 우리와 상반된다.

if(flags & (FLAG!=0))

2.2.1.2 오류 2

r = h<<4 + low;//r的值等于h左移4位后的值与变量low的和

그러나 실제로 다음과 같이 결합됩니다.

r = hi<< (4 + low);

표현된 의미가 우리가 표현하고자 하는 것과 반대입니다.

2.2.1.3 오류 예 3

while(c = getc(in)!=EOF)
	putc(c,out);

위의 문장에서 우리는 한 파일을 다른 파일로 복사하려고 하지만 실제로는 다음과 같이 결합됩니다.

while(c = (getc(in)!=EOF))

표현의 의미가 우리가 달성하고자 하는 목적을 달성하지 못합니다. 여기서 getc(in) 함수의 반환 값은 EOF와 비교한 후 폐기되는 임시 변수일 뿐입니다. 따라서 파일의 결과 복사본에는 이진 1바이트 스트림 집합만 포함됩니다.

2.2.1.4 오류 4

if((t=BTYPE(pt1->aty)==STRTY) || t==UNIONTY)

이 코드 줄의 원래 의도는 먼저 t에 값을 할당한 다음 t가 STRTY 또는 UNIONTY와 같은지 여부를 결정하는 것입니다.

그러나 실제 조합은 다음과 같습니다.

if((((t=BTYPE(pt1->aty))==STRTY) || t)==UNIONTY)

실제 결과는 상당히 다릅니다. BTYPE(pt1->aty)의 값이 STRTY인지 여부에 따라 t의 값은 1 또는 0입니다. t의 값이 0이면 UNIONTY와 더 비교할 수 있습니다. .

2.2.1.5 오류 5

while(c == '\t' || c = ' '|| c == '\n')
	c = getc(f);

이 예는 불법입니다. 할당 연산자는 while 절에서 다른 연산자보다 우선 순위가 낮기 때문에 위의 예를 설명할 수 있습니다.

while((c == '\t' || c) = (' '|| c == '\n'))

물론 이것은 (c == '\t' || c)할당 연산자의 좌변에 나타날 수 없기 때문에 불법입니다.

2.2.2 연산자의 연관 이해

*p++;
//上面的代码应该像下面这样进行理解:
*(p++);//++和*都是单目运算符,具有相同的优先级,但是结合性是自右向左的

2.2.3 관계 연산자의 우선 순위

6개의 관계연산자의 우선순위가 같지 않고 >, >=, <, <=의 우선순위가 두 연산자 ==, !=보다 우선순위가 높기 때문에 다음과 같이 쓰는 경우가 많다.

if(a < b == c < d)
//上面代码等价于
if((a < b) == (c < d))

2.2.4 오른쪽에서 왼쪽으로 우선 순위 요약

단항 연산자, 삼항 연산자, 할당 연산자

2.3 명령문의 끝으로 세미콜론을 기록하십시오.

2.3.1 세미콜론 추가

참고: if 또는 while 절 뒤에 세미콜론을 추가하지 마십시오. 세미콜론을 뒤에 넣으면 계산할 수 없는 결과가 발생하여 코드 오류가 발생하고 종종 찾기가 어렵습니다.

예:

if(x[i] > big);
	big = x[i];

위의 코드는 실제로 다음과 같습니다.

if(x[i] > big) {}
	big = x[i];

즉, 조건이 참인지 아닌지에 관계없이 big = x[i] 문을 실행합니다.

2.3.2 세미콜론 누락

참고: 세미콜론을 누락하면 종종 측정할 수 없는 결과를 초래할 수 있습니다.

예:

if(n<3)
	return 
logrec.date = x[0];

위의 코드는 다음 코드와 동일합니다.

if(n<3)
	return logrec.date = x[0];

이때, n<3 조건을 만족하면 큰 문제가 없을 수도 있지만, n<3 조건을 만족하지 않으면 logrec.date = x[0] 문을 건너뛴다. 이것은 우리가 표현하고자 하는 것과 반대입니다.

다음 코드가 있습니다.

struct logrec
{
	int date;
	int time;
	int code;
}//分号被省略
main()
{
	···
}

세미콜론이 생략되지 않은 경우 main() 함수의 반환 값 형식은 int 형식(컴파일러 기본값)이고 생략 후 반환 형식은 구조체 형식입니다.

2.4 switch 문

참고: C 언어의 case 문 다음에 다음 case 문을 계속 실행하지 않으려면 case 문 끝에 break를 추가하여 현재 case 문을 종료해야 합니다.

2.5 기능 목록

==C 언어 요구 사항: 함수를 호출할 때 함수가 매개변수를 사용하지 않더라도 매개변수 목록이 포함되어야 합니다. == 따라서 f가 함수이면

f();

함수 호출 문이고,

f;

그러나 그것은 아무 것도 하지 않는 진술이다. 이 문은 함수를 호출하지 않고 함수 f의 주소를 계산합니다.

추천

출처blog.csdn.net/m0_57304511/article/details/123449168