programing

C가->와.을 구분하는 이유는 무엇입니까?

nasanasas 2021. 1. 6. 08:26
반응형

C가->와.을 구분하는 이유는 무엇입니까?


좋아요, 이것은 심각한 결과는 아니지만 한동안 저를 괴롭 혔습니다. 연산자 ->.연산자를 구별하는 이유가 있습니까?

물론 현재 규칙은 .구조체에 대해 작동하고 구조체 ->에 대한 포인터 (또는 공용체)에 대해 작동한다는 것입니다. 그러나 실제로 작동하는 방법은 다음과 같습니다. 하자 s요소를을주게됩니다 구조체 수 x및하자 ps같은 형태의 구조체에 대한 포인터.

당신이 쓰면

s->x

컴파일러는 다음과 같은 방식으로 경고를 내 보냅니다.

당신은 sx를 의미했습니다. 다시 입력하고 다시 컴파일하십시오.

당신이 쓰면

ps.x

컴파일러는 다음과 같은 방식으로 경고를 내 보냅니다.

당신은 ps-> x를 의미했습니다. 다시 입력하고 다시 컴파일하십시오.

컴파일러는 모두의 유형을 알고 있기 때문에 sps컴파일시를, 그것이 올바른 운영자가 될 내용을 해석하는 데 필요한 모든 정보가 있습니다. 올바른 수정에 대한 모호성이 없다는 점에서 세미콜론 누락과 같은 다른 경고와 같지 않다고 생각합니다.

그래서 여기 C1x 표준위원회에 대한 가상의 제안이 있습니다 (ISO가 보수적 인 행보를 보이고 있기 때문에 결코 고려되지 않을 것입니다) :

lhs.rhs 표현식이 주어지면 lhs가 구조체 또는 공용체 유형이면 표현식은 rhs라는 이름의 lhs 요소를 참조합니다. lhs가 포인터-구조체 또는 -union 유형이면 (* lhs) .rhs로 해석됩니다.

이것은 확실히 우리를 항상 절약 할 수 있고 사람들이 C를 배우는 것을 더 쉽게 만들어 줄 것입니다. [그리고 나는 학습자들이 ->혼란 스럽거나 짜증나 것을 발견 할만큼 권위있게 말할만큼 충분히 C를 가르쳤습니다 .]

C가 비슷한 일을하는 전례도 있습니다. 예를 구현 이유로, 함수 선언은 항상 포인터-에 기능으로 캐스팅, 그래서된다 f(x,y)(*f)(x,y)여부와 상관없이 모두 작동 f함수 나 함수에 대한 포인터로 선언했다.

그래서, 제 질문 :이 제안에 무엇이 문제입니까? ps.x사이에 치명적 모호성이있는 예를 생각할 수 있습니까 s.x? 아니면 필수 구분을 유지하는 것이 왜 유용합니까?


글쎄, 만약 당신이 정말로 C 언어의 사양에 그런 종류의 기능을 도입하고 싶다면, 나머지 언어와 "혼합"되도록하기 위해 논리적으로해야 할 일은 "decay to pointer"의 개념을 확장하는 것입니다. "를 구조화합니다. 함수와 함수 포인터로 직접 예제를 만들었습니다. 그렇게 작동하는 이유는 C의 함수 유형이 sizeof및 단항 &연산자를 제외한 모든 컨텍스트에서 포인터 유형으로 감소하기 때문 입니다. (배열, BTW도 마찬가지입니다.)

따라서 제안한 것과 유사한 것을 구현하기 위해 C의 다른 모든 "감쇠"(즉, 배열에서 -pointer decay 및 function-to-pointer decay) 작동 : 유형의 struct 객체가 T표현식에서 사용되면 그 유형은 즉시 유형으로 감소 T*합니다 (구조체 객체의 시작에 대한 포인터-피연산자 sizeof또는 단항 인 경우 제외) &. 구조체에 대해 이러한 붕괴 규칙이 도입되면 구조체에 ->대한 포인터가 있는지 또는 왼쪽에 구조체 자체가 있는지에 관계없이 연산자를 사용 하여 구조체 요소에 액세스 할 수 있습니다 . .이 경우 오퍼레이터 는 완전히 불필요해질 것입니다.->그리고 ->.

위의 내용이 C 언어의 정신으로 구현 된 경우이 기능이 어떻게 생겼는지 다시 한 번 말씀 드리겠습니다.

그러나 나는 (Charles가 말한 것에 동의하면서) 구조체에 대한 포인터로 작동하는 코드와 구조체 자체로 작동하는 코드 사이의 시각적 차이를 잃는 것은 정확히 바람직하지 않다고 말하고 싶습니다.

추신 : 구조체에 대한 그러한 붕괴 규칙의 명백한 부정적인 결과는 "배열은 단지 일정한 포인터"라고 이타 적으로 믿는 현재의 초보자 군대 외에 우리는 "구조 객체는 단지 상수 포인터 일뿐"이라고 이타 적으로 믿고있는 신참 군대를 갖게 될 것입니다. ". 그리고 Chris Torek의 배열 FAQ는 구조체를 다루기 위해 약 1.5-2 배 더 커야합니다. :)


나는 당신이 말한 것에 대해 미친 것이 없다고 생각합니다. .구조체에 대한 포인터를 사용하면 작동합니다.

그러나 구조체와 구조체에 대한 포인터가 다르게 취급된다는 사실이 마음에 듭니다.

비용이 많이 드는 작업과 단서에 대한 컨텍스트를 제공합니다.

이 스 니펫을 고려하여 상당히 큰 기능의 중간에 있다고 상상해보십시오.

s.c = 99;
f(s);

assert(s.c == 99);

현재 나는 그것이 s구조체 라고 말할 수 있습니다 . 에 대한 호출을 위해 전체가 복사 될 것임을 알고 있습니다 f. 나는 또한 그 주장 발사 될 수 없다는 것을 알고있다 .

.struct에 대한 포인터와 함께 사용 하는 것이 허용되면 그 중 어느 것도 알 수 없으며 assert가 실행될 수 있으며 (err ) 다른 것으로 f설정 될 수 있습니다 .s.cs->c

또 다른 단점은 C ++와의 호환성을 떨어 뜨린다는 것입니다. C ++는 ->클래스가 '유사'포인터가 될 수 있도록 클래스에 의해 오버로드 될 수 있습니다. 그것이 중요 .하고 ->다르게 행동 하는 것이 중요합니다 . .구조체에 대한 포인터와 함께 사용되는 "새로운"C 코드는 더 이상 C ++ 코드로 허용되지 않을 것입니다.


글쎄요 분명 모호함이 없거나 제안을 할 수 없었습니다. 유일한 문제는 다음과 같은 경우입니다.

p->x = 3;

당신 p은 포인터이지만 허용한다면 :

p.x = 3;

그런 상황에서 당신은 실제로 알지 못합니다. 특히 나중에 포인터를 캐스팅하고 잘못된 수의 간접 레벨을 사용하는 경우 잠재적으로 문제를 일으킬 수 있습니다.


A distinguishing feature of the C programming language (as opposed to its relative C++) is that the cost model is very explicit. The dot is distinguished from the arrow because the arrow requires an additional memory reference, and C is very careful to make the number of memory references evident from the source code.


Well, there could definitely be cases where you have something complex like:

(*item)->elem

(which I have had happen in some programs), and if you wrote something like

item.elem

meaning the above, it could be confusing whether elem is an element of struct item, or an element of a struct that item points to, or an element of a struct that is pointed to be an element in a list that is pointed to by an iterator item, and so on and so forth.

So yeah, it does make things somewhat clearer when using pointers to pointers to structs, &c.


Yes, that's OK, but it is not what C really needs at all

Not only is it OK, but it is the modern style. Java and Go both just use .. Since everything that doesn't fit in a register is at some level a reference, the distinction between thing and pointer to thing is definitely a bit arbitrary, at least until you get to function calls.

The first evolutionary step was to make the dereference operator postfix, something dmr once implied he at some point prefered. Pascal does this, so it has p^.field. The only reason there even is a -> operator is because it's goofy to have to type (*p).field or p[0].field.

So yes, it would work. It would even be better as it works at a higher level of abstraction. One really should be able to make as many changes as possible without requiring downstream code to change, that's in a sense the entire point of higher level languages.

I have argued that using () for function calls and [] for array subscripting is wrong. Why not allow different implementations to export different abstractions?

But there isn't much reason to make the change. C programmers are unlikely to revolt over the lack of a syntactic sugar extension that saves one character in an expression and it would be hard to use anyway because it would not be immediately if ever universally adopted. Remember that when standards committees go rogue they end up preaching to empty rooms. They require the willing cooperation and agreement of the world's compiler developers.

What C really needs isn't ever-so-slightly faster ways to write unsafe code. I don't mind working in C, but project managers don't like having their reliability determined by their worst guy, and it's possible that what C really needs is a safe dialect, something like Cyclone, or perhaps something just like Go.


If anything, the current syntax lets readers of the code know whether or not the code is working with a pointer or the actual object. Someone who does not know the code beforehand understands it better.

ReferenceURL : https://stackoverflow.com/questions/1813865/why-does-c-have-a-distinction-between-and

반응형