Why did C use the -> operator instead of reusing the . operator? Unicorn Meta Zoo #1: Why...

As an international instructor, should I openly talk about my accent?

English or Hindi translation of Vyasa Smriti

How to find the stem of any word?

Can I criticise the more senior developers around me for not writing clean code?

How does the mezzoloth's teleportation work?

Where was the County of Thurn und Taxis located?

How important is it that $TERM is correct?

Scheduling based problem

Can you stand up from being prone using Skirmisher outside of your turn?

Why does Arg'[1. + I] return -0.5?

What's the difference between using dependency injection with a container and using a service locator?

What makes accurate emulation of old systems a difficult task?

Co-worker works way more than he should

Map material from china not allowed to leave the country

Prove that the countable union of countable sets is also countable

Is it acceptable to use working hours to read general interest books?

Can a stored procedure reference the database in which it is stored?

Retract an already submitted recommendation letter (written for an undergrad student)

What to do with someone that cheated their way through university and a PhD program?

How do I prove this combinatorial identity

Is Electric Central Heating worth it if using Solar Panels?

Crossed out red box fitting tightly around image

Is this homebrew arcane communication device abusable?

The weakest link



Why did C use the -> operator instead of reusing the . operator?



Unicorn Meta Zoo #1: Why another podcast?
Announcing the arrival of Valued Associate #679: Cesar ManaraWhat was the first C compiler for the IBM PC?What was the first C compiler for the Mac?Which tools were used to create the C language?Why were the / (min) and the / (max) operators abandoned in the C language?Why (historically) include the number of arguments (argc) as a parameter of main?The history of the NULL pointerWhy did C have the return type before functions?Why do C to Z80 compilers produce poor code?Why was UNIX never backported to the PDP-7?What was the name of the object-oriented C language I used in the 1980s on the Mac












41















In the C programming language, the syntax to access the member of a structure is




structure.member




However, a member of a structure referenced by a pointer is written as




pointer->member




There's really no need for two different operators. The compiler knows the type of the left-hand value; if it is a structure, the first meaning is evident. If it is a pointer, the second meaning is evident. Furthermore, . is far easier to type than ->. Not only does -> have more characters to type, on many keyboards one character is unshifted and the other character is shifted, requiring some finger acrobatics. Indeed, many languages based on C allow or use . in place of ->.



Why did C use two operators when one would have sufficed?



(My guess would be because C evolved from the typeless B language.)










share|improve this question


















  • 8





    You can still write (*structure).member, if you like that more. (I don't, very probably K&R didn't, either. It is a bit awkward to handle because of C operator precedence, and that might answer your question)

    – tofro
    yesterday








  • 11





    Just out of curiosity: In your hypothetical language where a.b could be interpreted as (*a).b if a is a pointer-to-struct, would it also be automatically be interpreted as (**a).b if a is a pointer-to-pointer-to-struct? Just to point out a possible consequence…

    – wrtlprnft
    23 hours ago






  • 19





    People complain a lot about pointers being confusing. Imagine adding to that confusion by not knowing if a variable was a pointer or not when reading code.

    – JPhi1618
    22 hours ago






  • 2





    Just to note: Wirth's language Oberon does almost exactly this, allowing the Pascal-like ^ to be omitted in the expressions p^.x and p^[i], and using types to disambiguate.

    – Mike Spivey
    21 hours ago






  • 1





    Following on from @MikeSpivey's comment, for the last decade or so Objective-C, a strict C superset that keeps [almost] all objects on the heap and therefore refers to them exclusively via pointer — indeed they used to be accessible directly as structs, but that was a while ago now — has used . on an object pointer to mean "call the getter or setter as appropriate".

    – Tommy
    14 hours ago
















41















In the C programming language, the syntax to access the member of a structure is




structure.member




However, a member of a structure referenced by a pointer is written as




pointer->member




There's really no need for two different operators. The compiler knows the type of the left-hand value; if it is a structure, the first meaning is evident. If it is a pointer, the second meaning is evident. Furthermore, . is far easier to type than ->. Not only does -> have more characters to type, on many keyboards one character is unshifted and the other character is shifted, requiring some finger acrobatics. Indeed, many languages based on C allow or use . in place of ->.



Why did C use two operators when one would have sufficed?



(My guess would be because C evolved from the typeless B language.)










share|improve this question


















  • 8





    You can still write (*structure).member, if you like that more. (I don't, very probably K&R didn't, either. It is a bit awkward to handle because of C operator precedence, and that might answer your question)

    – tofro
    yesterday








  • 11





    Just out of curiosity: In your hypothetical language where a.b could be interpreted as (*a).b if a is a pointer-to-struct, would it also be automatically be interpreted as (**a).b if a is a pointer-to-pointer-to-struct? Just to point out a possible consequence…

    – wrtlprnft
    23 hours ago






  • 19





    People complain a lot about pointers being confusing. Imagine adding to that confusion by not knowing if a variable was a pointer or not when reading code.

    – JPhi1618
    22 hours ago






  • 2





    Just to note: Wirth's language Oberon does almost exactly this, allowing the Pascal-like ^ to be omitted in the expressions p^.x and p^[i], and using types to disambiguate.

    – Mike Spivey
    21 hours ago






  • 1





    Following on from @MikeSpivey's comment, for the last decade or so Objective-C, a strict C superset that keeps [almost] all objects on the heap and therefore refers to them exclusively via pointer — indeed they used to be accessible directly as structs, but that was a while ago now — has used . on an object pointer to mean "call the getter or setter as appropriate".

    – Tommy
    14 hours ago














41












41








41


3






In the C programming language, the syntax to access the member of a structure is




structure.member




However, a member of a structure referenced by a pointer is written as




pointer->member




There's really no need for two different operators. The compiler knows the type of the left-hand value; if it is a structure, the first meaning is evident. If it is a pointer, the second meaning is evident. Furthermore, . is far easier to type than ->. Not only does -> have more characters to type, on many keyboards one character is unshifted and the other character is shifted, requiring some finger acrobatics. Indeed, many languages based on C allow or use . in place of ->.



Why did C use two operators when one would have sufficed?



(My guess would be because C evolved from the typeless B language.)










share|improve this question














In the C programming language, the syntax to access the member of a structure is




structure.member




However, a member of a structure referenced by a pointer is written as




pointer->member




There's really no need for two different operators. The compiler knows the type of the left-hand value; if it is a structure, the first meaning is evident. If it is a pointer, the second meaning is evident. Furthermore, . is far easier to type than ->. Not only does -> have more characters to type, on many keyboards one character is unshifted and the other character is shifted, requiring some finger acrobatics. Indeed, many languages based on C allow or use . in place of ->.



Why did C use two operators when one would have sufficed?



(My guess would be because C evolved from the typeless B language.)







c






share|improve this question













share|improve this question











share|improve this question




share|improve this question










asked yesterday









Dr SheldonDr Sheldon

2,17621136




2,17621136








  • 8





    You can still write (*structure).member, if you like that more. (I don't, very probably K&R didn't, either. It is a bit awkward to handle because of C operator precedence, and that might answer your question)

    – tofro
    yesterday








  • 11





    Just out of curiosity: In your hypothetical language where a.b could be interpreted as (*a).b if a is a pointer-to-struct, would it also be automatically be interpreted as (**a).b if a is a pointer-to-pointer-to-struct? Just to point out a possible consequence…

    – wrtlprnft
    23 hours ago






  • 19





    People complain a lot about pointers being confusing. Imagine adding to that confusion by not knowing if a variable was a pointer or not when reading code.

    – JPhi1618
    22 hours ago






  • 2





    Just to note: Wirth's language Oberon does almost exactly this, allowing the Pascal-like ^ to be omitted in the expressions p^.x and p^[i], and using types to disambiguate.

    – Mike Spivey
    21 hours ago






  • 1





    Following on from @MikeSpivey's comment, for the last decade or so Objective-C, a strict C superset that keeps [almost] all objects on the heap and therefore refers to them exclusively via pointer — indeed they used to be accessible directly as structs, but that was a while ago now — has used . on an object pointer to mean "call the getter or setter as appropriate".

    – Tommy
    14 hours ago














  • 8





    You can still write (*structure).member, if you like that more. (I don't, very probably K&R didn't, either. It is a bit awkward to handle because of C operator precedence, and that might answer your question)

    – tofro
    yesterday








  • 11





    Just out of curiosity: In your hypothetical language where a.b could be interpreted as (*a).b if a is a pointer-to-struct, would it also be automatically be interpreted as (**a).b if a is a pointer-to-pointer-to-struct? Just to point out a possible consequence…

    – wrtlprnft
    23 hours ago






  • 19





    People complain a lot about pointers being confusing. Imagine adding to that confusion by not knowing if a variable was a pointer or not when reading code.

    – JPhi1618
    22 hours ago






  • 2





    Just to note: Wirth's language Oberon does almost exactly this, allowing the Pascal-like ^ to be omitted in the expressions p^.x and p^[i], and using types to disambiguate.

    – Mike Spivey
    21 hours ago






  • 1





    Following on from @MikeSpivey's comment, for the last decade or so Objective-C, a strict C superset that keeps [almost] all objects on the heap and therefore refers to them exclusively via pointer — indeed they used to be accessible directly as structs, but that was a while ago now — has used . on an object pointer to mean "call the getter or setter as appropriate".

    – Tommy
    14 hours ago








8




8





You can still write (*structure).member, if you like that more. (I don't, very probably K&R didn't, either. It is a bit awkward to handle because of C operator precedence, and that might answer your question)

– tofro
yesterday







You can still write (*structure).member, if you like that more. (I don't, very probably K&R didn't, either. It is a bit awkward to handle because of C operator precedence, and that might answer your question)

– tofro
yesterday






11




11





Just out of curiosity: In your hypothetical language where a.b could be interpreted as (*a).b if a is a pointer-to-struct, would it also be automatically be interpreted as (**a).b if a is a pointer-to-pointer-to-struct? Just to point out a possible consequence…

– wrtlprnft
23 hours ago





Just out of curiosity: In your hypothetical language where a.b could be interpreted as (*a).b if a is a pointer-to-struct, would it also be automatically be interpreted as (**a).b if a is a pointer-to-pointer-to-struct? Just to point out a possible consequence…

– wrtlprnft
23 hours ago




19




19





People complain a lot about pointers being confusing. Imagine adding to that confusion by not knowing if a variable was a pointer or not when reading code.

– JPhi1618
22 hours ago





People complain a lot about pointers being confusing. Imagine adding to that confusion by not knowing if a variable was a pointer or not when reading code.

– JPhi1618
22 hours ago




2




2





Just to note: Wirth's language Oberon does almost exactly this, allowing the Pascal-like ^ to be omitted in the expressions p^.x and p^[i], and using types to disambiguate.

– Mike Spivey
21 hours ago





Just to note: Wirth's language Oberon does almost exactly this, allowing the Pascal-like ^ to be omitted in the expressions p^.x and p^[i], and using types to disambiguate.

– Mike Spivey
21 hours ago




1




1





Following on from @MikeSpivey's comment, for the last decade or so Objective-C, a strict C superset that keeps [almost] all objects on the heap and therefore refers to them exclusively via pointer — indeed they used to be accessible directly as structs, but that was a while ago now — has used . on an object pointer to mean "call the getter or setter as appropriate".

– Tommy
14 hours ago





Following on from @MikeSpivey's comment, for the last decade or so Objective-C, a strict C superset that keeps [almost] all objects on the heap and therefore refers to them exclusively via pointer — indeed they used to be accessible directly as structs, but that was a while ago now — has used . on an object pointer to mean "call the getter or setter as appropriate".

– Tommy
14 hours ago










4 Answers
4






active

oldest

votes


















39














In the embryonic form of C described in the 1974 C Reference Manual, there was no requirement that the left operand of . actually be a structure, nor that the left operand of -> actually be a pointer. The -> operator meant "interpret the value of the left operand as a pointer, add the offset associated with the indicated structure member name, and dereference the resulting pointer as an object of the appropriate type. The . operator effectively took the address of the left operand and then applied ->.



Thus, given:



struct q { int x, y; };
int a[2];


the expressions a[0].y and a[0]->y would be interpreted in a fashion equivalent to ((struct q*)&a[0])->y and ((struct q*)a[0])->y, respectively.



If the compiler had examined the type of the left operand to the . operator, it could have used that to select between the two behaviors for it. It was probably easier, however, to have two operators whose behaviors didn't depend upon the left operand's type.






share|improve this answer



















  • 6





    To the point. As well as the last part about being 'easier'. C wasn'T designed to be a language as comfortable as possible, but to be translated as linear as possible. Resolving contextual information adds complexity and ambiguity. Nothing one wants t have when the task is to write an OS as close to the machine as possible while having the luxury of structured programming support.

    – Raffzahn
    23 hours ago






  • 3





    @Raffzahn: Even in 1974 C, the contextual information had be kept to process the + operator, so the cost of using the already existing information to disambiguate . and -> would have been minimal if there was no need to use them with other types as well.

    – supercat
    17 hours ago











  • Would this also mean that structure member names had to be globally unique?

    – IllusiveBrian
    6 hours ago











  • @IllusiveBrian exactly. See e.g. the names of fields in struct tm (described e.g. in ctime(3)): they are like tm_sec, tm_min, tm_hour etc.. Similarly struct stat (see stat(2)), which has st_dev, st_ino etc.. These are redundant in modern C, but have to be preserved for backwards compatibility

    – Ruslan
    3 hours ago













  • @Raffzahn: In simple compilers, the primary cost of having a compiler use various information to select what kind of operation an operator will perform is generally making sure the compiler will have the information in the context where it is needed. A compiler that has scanned an expression as far as p=thing will need to know whether type of thing is a pointer or array, and the target/element type, if the next token is a +. Consequently, no special effort should be required to ensure that it has that information if the next token happens to be a . or -> instead.

    – supercat
    3 hours ago



















25














Despite your assertion, there would in fact be situations where it would be ambigious.



First off, early C compilers were very simple. This was in fact the main appeal of the language, as compilers for it were very easy to create and could run on very small systems, like early 16/32 microprocessors.



Adding a bunch of code for hitting all the niche cases of type inference would have drastically added to the amount of code required to make a C compiler. In fact, I've argued as a (half) joke that K&R C had type inference, but it always inferred int. If you didn't tell C what type an object was, it assumed int (which could cause some really gnarly bugs, let me tell you...)



Secondly, since K&R C was weakly(barely) typed, the information in many cases flat out wasn't available. The destination type of a pointer assignment can be an int, or visa versa, and K&R C has no problem with that. The compiler simply cannot infer a dereference. The coder is assumed to know what she's doing.



Also realize that in C pointers and arrays are essentially syntactic sugar for each other. This means now your . operator would have to automagically work on arrays too. For instance, if member happened to be a char array, now structure.member would return with the first character. And again, both chars and pointer are assignable into ints, so context doesn't help you.



This being said, you aren't the first to notice this issue. In fact, Ada was designed that dereferencing a pointer object is always assumed when a dot is used. In those cases where you want the actual pointer, you have to use .all. The ambiguity (pointer vs. pointed to object) is still there, but resolved by moving the extra syntax to the weirder case.






share|improve this answer





















  • 4





    I love it. "K&R C had type inference, but it always inferred int". Brilliant.

    – Peter A. Schneider
    6 hours ago








  • 3





    "The coder is assumed to know what she's doing" is the C philosophy in a nutshell. That's simultaneously C's greatest strength and greatest weakness.

    – John Bode
    4 hours ago



















20














Some of the first C code I saw was like this: 0x8040->output = 'A'; — its purpose was accessing memory mapped I/O locations.  Needless to say it took me a while to figure out what this code was supposed to do, and the hex constant there really threw me.



The original K&R C placed all field names (here output) into the same namespace.  It was an error to have two fields of the same name in different structs at different offsets — but ok to have the same name at the same offset, the idea here being that two different structs could share the same initial fields, giving cheap way of doing "subclassing" to put varying data members at the end of the struct.



A struct could also be anonymous, e.g. no tag name for the struct.  None the less, the members could still be used in . or -> expressions.




The C Programming Language (K&R C) Appendix A, p197,209




[8.5] ... Two structures may share a common initial sequence of members; that is, the same member may appear in two different structures if it has the same type in both and if all previous members are the same in both.  (Actually, the compiler checks only that a name in two different structures has the same type and offset in both, but if the preceding members differ the construction is nonportable.)



...



[14.1] ... §7.1 says that in a direct or indirect structure reference (with . or ->) the name on the right must be a member of the structure named or pointed to by the expression on the left.  To allow an escape from the typing rules, this restriction is not firmly enforced by the compiler.  In fact, any lvalue is allowed before ., and the lvalue is then assumed to have the form of the structure of which the name on the right is a member.  Also, the expression before a -> is required only to be a pointer or an integer.  If an integer, it is taken to be the absolute address, in machine storage units, of the appropriate structure.





Since the K&R language & compiler didn't care what the type of the left hand side of . and -> was, the only way it had to tell the difference by having the two operators.



The ANSI C line of standards simply followed suit in syntax, even as these old rules were abandoned.






share|improve this answer



















  • 1





    Aha, so that’s why POSIX struct member names are all prefixed (e. g. stat::st_uid). I thought it was to enable shenanigans like #define st_atime st_atim.tv_sec.

    – Roman Odaisky
    5 hours ago



















17














I think there are two factors that led to standardization of the distinct operator "->" for accessing data members using a pointer.




  1. You assume that the C compiler would recognize the type of the LHS as being a pointer. But programmers could, and often did, override the initial typing (variable declaration) by using a typecast.

  2. In order to make the code more readable and less prone to unintended side-effects, it is useful to distinguish operations using pointers.


A very common feature of idiomatic C code is that a structure passed to a function as a pointer is modified within the function. Thus, the result is returned implicitly, by the side-effect of the structure variable in the calling function having been modified by the callee. This sort of approach violates modern sensibilities about loosely coupled code, but it was a simple and efficient means of dealing with complexity in C code. I would say the programmer was greatly assisted in maintaining the readability of such code by having distinct operations that made it clear whether some (possibly shared) memory pointer was the thing whose target was being modified.






share|improve this answer


























  • Could you give an example of your point #1? The type of the result of a typecast is well-defined... it's the type you are typecasting to. Should that be a pointer to a structure, the compiler has enough information to access its members. Whatever the type was prior to the typecast is irrelevant.

    – Dr Sheldon
    yesterday








  • 1





    @DrSheldon Yes, you are correct. The compiler can check operators used against the type of a typecast too, and interpret a single operator appropriately. But then you are removing one of the compiler checks against programming errors. I think #1 and #2 actually work in concert to push the programmer toward care with pointer references. If the compiler tries to be "too smart" it ends up misinterpreting what the error-prone programmer intended, and (perhaps more important) makes the code harder to read.

    – Brian H
    yesterday













  • I think this is the best answer, C (and thus C++)'s use of -> is a feature not a bug.

    – Jack Aidley
    5 hours ago












Your Answer








StackExchange.ready(function() {
var channelOptions = {
tags: "".split(" "),
id: "648"
};
initTagRenderer("".split(" "), "".split(" "), channelOptions);

StackExchange.using("externalEditor", function() {
// Have to fire editor after snippets, if snippets enabled
if (StackExchange.settings.snippets.snippetsEnabled) {
StackExchange.using("snippets", function() {
createEditor();
});
}
else {
createEditor();
}
});

function createEditor() {
StackExchange.prepareEditor({
heartbeatType: 'answer',
autoActivateHeartbeat: false,
convertImagesToLinks: false,
noModals: true,
showLowRepImageUploadWarning: true,
reputationToPostImages: null,
bindNavPrevention: true,
postfix: "",
imageUploader: {
brandingHtml: "Powered by u003ca class="icon-imgur-white" href="https://imgur.com/"u003eu003c/au003e",
contentPolicyHtml: "User contributions licensed under u003ca href="https://creativecommons.org/licenses/by-sa/3.0/"u003ecc by-sa 3.0 with attribution requiredu003c/au003e u003ca href="https://stackoverflow.com/legal/content-policy"u003e(content policy)u003c/au003e",
allowUrls: true
},
noCode: true, onDemand: true,
discardSelector: ".discard-answer"
,immediatelyShowMarkdownHelp:true
});


}
});














draft saved

draft discarded


















StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fretrocomputing.stackexchange.com%2fquestions%2f10812%2fwhy-did-c-use-the-operator-instead-of-reusing-the-operator%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown

























4 Answers
4






active

oldest

votes








4 Answers
4






active

oldest

votes









active

oldest

votes






active

oldest

votes









39














In the embryonic form of C described in the 1974 C Reference Manual, there was no requirement that the left operand of . actually be a structure, nor that the left operand of -> actually be a pointer. The -> operator meant "interpret the value of the left operand as a pointer, add the offset associated with the indicated structure member name, and dereference the resulting pointer as an object of the appropriate type. The . operator effectively took the address of the left operand and then applied ->.



Thus, given:



struct q { int x, y; };
int a[2];


the expressions a[0].y and a[0]->y would be interpreted in a fashion equivalent to ((struct q*)&a[0])->y and ((struct q*)a[0])->y, respectively.



If the compiler had examined the type of the left operand to the . operator, it could have used that to select between the two behaviors for it. It was probably easier, however, to have two operators whose behaviors didn't depend upon the left operand's type.






share|improve this answer



















  • 6





    To the point. As well as the last part about being 'easier'. C wasn'T designed to be a language as comfortable as possible, but to be translated as linear as possible. Resolving contextual information adds complexity and ambiguity. Nothing one wants t have when the task is to write an OS as close to the machine as possible while having the luxury of structured programming support.

    – Raffzahn
    23 hours ago






  • 3





    @Raffzahn: Even in 1974 C, the contextual information had be kept to process the + operator, so the cost of using the already existing information to disambiguate . and -> would have been minimal if there was no need to use them with other types as well.

    – supercat
    17 hours ago











  • Would this also mean that structure member names had to be globally unique?

    – IllusiveBrian
    6 hours ago











  • @IllusiveBrian exactly. See e.g. the names of fields in struct tm (described e.g. in ctime(3)): they are like tm_sec, tm_min, tm_hour etc.. Similarly struct stat (see stat(2)), which has st_dev, st_ino etc.. These are redundant in modern C, but have to be preserved for backwards compatibility

    – Ruslan
    3 hours ago













  • @Raffzahn: In simple compilers, the primary cost of having a compiler use various information to select what kind of operation an operator will perform is generally making sure the compiler will have the information in the context where it is needed. A compiler that has scanned an expression as far as p=thing will need to know whether type of thing is a pointer or array, and the target/element type, if the next token is a +. Consequently, no special effort should be required to ensure that it has that information if the next token happens to be a . or -> instead.

    – supercat
    3 hours ago
















39














In the embryonic form of C described in the 1974 C Reference Manual, there was no requirement that the left operand of . actually be a structure, nor that the left operand of -> actually be a pointer. The -> operator meant "interpret the value of the left operand as a pointer, add the offset associated with the indicated structure member name, and dereference the resulting pointer as an object of the appropriate type. The . operator effectively took the address of the left operand and then applied ->.



Thus, given:



struct q { int x, y; };
int a[2];


the expressions a[0].y and a[0]->y would be interpreted in a fashion equivalent to ((struct q*)&a[0])->y and ((struct q*)a[0])->y, respectively.



If the compiler had examined the type of the left operand to the . operator, it could have used that to select between the two behaviors for it. It was probably easier, however, to have two operators whose behaviors didn't depend upon the left operand's type.






share|improve this answer



















  • 6





    To the point. As well as the last part about being 'easier'. C wasn'T designed to be a language as comfortable as possible, but to be translated as linear as possible. Resolving contextual information adds complexity and ambiguity. Nothing one wants t have when the task is to write an OS as close to the machine as possible while having the luxury of structured programming support.

    – Raffzahn
    23 hours ago






  • 3





    @Raffzahn: Even in 1974 C, the contextual information had be kept to process the + operator, so the cost of using the already existing information to disambiguate . and -> would have been minimal if there was no need to use them with other types as well.

    – supercat
    17 hours ago











  • Would this also mean that structure member names had to be globally unique?

    – IllusiveBrian
    6 hours ago











  • @IllusiveBrian exactly. See e.g. the names of fields in struct tm (described e.g. in ctime(3)): they are like tm_sec, tm_min, tm_hour etc.. Similarly struct stat (see stat(2)), which has st_dev, st_ino etc.. These are redundant in modern C, but have to be preserved for backwards compatibility

    – Ruslan
    3 hours ago













  • @Raffzahn: In simple compilers, the primary cost of having a compiler use various information to select what kind of operation an operator will perform is generally making sure the compiler will have the information in the context where it is needed. A compiler that has scanned an expression as far as p=thing will need to know whether type of thing is a pointer or array, and the target/element type, if the next token is a +. Consequently, no special effort should be required to ensure that it has that information if the next token happens to be a . or -> instead.

    – supercat
    3 hours ago














39












39








39







In the embryonic form of C described in the 1974 C Reference Manual, there was no requirement that the left operand of . actually be a structure, nor that the left operand of -> actually be a pointer. The -> operator meant "interpret the value of the left operand as a pointer, add the offset associated with the indicated structure member name, and dereference the resulting pointer as an object of the appropriate type. The . operator effectively took the address of the left operand and then applied ->.



Thus, given:



struct q { int x, y; };
int a[2];


the expressions a[0].y and a[0]->y would be interpreted in a fashion equivalent to ((struct q*)&a[0])->y and ((struct q*)a[0])->y, respectively.



If the compiler had examined the type of the left operand to the . operator, it could have used that to select between the two behaviors for it. It was probably easier, however, to have two operators whose behaviors didn't depend upon the left operand's type.






share|improve this answer













In the embryonic form of C described in the 1974 C Reference Manual, there was no requirement that the left operand of . actually be a structure, nor that the left operand of -> actually be a pointer. The -> operator meant "interpret the value of the left operand as a pointer, add the offset associated with the indicated structure member name, and dereference the resulting pointer as an object of the appropriate type. The . operator effectively took the address of the left operand and then applied ->.



Thus, given:



struct q { int x, y; };
int a[2];


the expressions a[0].y and a[0]->y would be interpreted in a fashion equivalent to ((struct q*)&a[0])->y and ((struct q*)a[0])->y, respectively.



If the compiler had examined the type of the left operand to the . operator, it could have used that to select between the two behaviors for it. It was probably easier, however, to have two operators whose behaviors didn't depend upon the left operand's type.







share|improve this answer












share|improve this answer



share|improve this answer










answered yesterday









supercatsupercat

8,6051043




8,6051043








  • 6





    To the point. As well as the last part about being 'easier'. C wasn'T designed to be a language as comfortable as possible, but to be translated as linear as possible. Resolving contextual information adds complexity and ambiguity. Nothing one wants t have when the task is to write an OS as close to the machine as possible while having the luxury of structured programming support.

    – Raffzahn
    23 hours ago






  • 3





    @Raffzahn: Even in 1974 C, the contextual information had be kept to process the + operator, so the cost of using the already existing information to disambiguate . and -> would have been minimal if there was no need to use them with other types as well.

    – supercat
    17 hours ago











  • Would this also mean that structure member names had to be globally unique?

    – IllusiveBrian
    6 hours ago











  • @IllusiveBrian exactly. See e.g. the names of fields in struct tm (described e.g. in ctime(3)): they are like tm_sec, tm_min, tm_hour etc.. Similarly struct stat (see stat(2)), which has st_dev, st_ino etc.. These are redundant in modern C, but have to be preserved for backwards compatibility

    – Ruslan
    3 hours ago













  • @Raffzahn: In simple compilers, the primary cost of having a compiler use various information to select what kind of operation an operator will perform is generally making sure the compiler will have the information in the context where it is needed. A compiler that has scanned an expression as far as p=thing will need to know whether type of thing is a pointer or array, and the target/element type, if the next token is a +. Consequently, no special effort should be required to ensure that it has that information if the next token happens to be a . or -> instead.

    – supercat
    3 hours ago














  • 6





    To the point. As well as the last part about being 'easier'. C wasn'T designed to be a language as comfortable as possible, but to be translated as linear as possible. Resolving contextual information adds complexity and ambiguity. Nothing one wants t have when the task is to write an OS as close to the machine as possible while having the luxury of structured programming support.

    – Raffzahn
    23 hours ago






  • 3





    @Raffzahn: Even in 1974 C, the contextual information had be kept to process the + operator, so the cost of using the already existing information to disambiguate . and -> would have been minimal if there was no need to use them with other types as well.

    – supercat
    17 hours ago











  • Would this also mean that structure member names had to be globally unique?

    – IllusiveBrian
    6 hours ago











  • @IllusiveBrian exactly. See e.g. the names of fields in struct tm (described e.g. in ctime(3)): they are like tm_sec, tm_min, tm_hour etc.. Similarly struct stat (see stat(2)), which has st_dev, st_ino etc.. These are redundant in modern C, but have to be preserved for backwards compatibility

    – Ruslan
    3 hours ago













  • @Raffzahn: In simple compilers, the primary cost of having a compiler use various information to select what kind of operation an operator will perform is generally making sure the compiler will have the information in the context where it is needed. A compiler that has scanned an expression as far as p=thing will need to know whether type of thing is a pointer or array, and the target/element type, if the next token is a +. Consequently, no special effort should be required to ensure that it has that information if the next token happens to be a . or -> instead.

    – supercat
    3 hours ago








6




6





To the point. As well as the last part about being 'easier'. C wasn'T designed to be a language as comfortable as possible, but to be translated as linear as possible. Resolving contextual information adds complexity and ambiguity. Nothing one wants t have when the task is to write an OS as close to the machine as possible while having the luxury of structured programming support.

– Raffzahn
23 hours ago





To the point. As well as the last part about being 'easier'. C wasn'T designed to be a language as comfortable as possible, but to be translated as linear as possible. Resolving contextual information adds complexity and ambiguity. Nothing one wants t have when the task is to write an OS as close to the machine as possible while having the luxury of structured programming support.

– Raffzahn
23 hours ago




3




3





@Raffzahn: Even in 1974 C, the contextual information had be kept to process the + operator, so the cost of using the already existing information to disambiguate . and -> would have been minimal if there was no need to use them with other types as well.

– supercat
17 hours ago





@Raffzahn: Even in 1974 C, the contextual information had be kept to process the + operator, so the cost of using the already existing information to disambiguate . and -> would have been minimal if there was no need to use them with other types as well.

– supercat
17 hours ago













Would this also mean that structure member names had to be globally unique?

– IllusiveBrian
6 hours ago





Would this also mean that structure member names had to be globally unique?

– IllusiveBrian
6 hours ago













@IllusiveBrian exactly. See e.g. the names of fields in struct tm (described e.g. in ctime(3)): they are like tm_sec, tm_min, tm_hour etc.. Similarly struct stat (see stat(2)), which has st_dev, st_ino etc.. These are redundant in modern C, but have to be preserved for backwards compatibility

– Ruslan
3 hours ago







@IllusiveBrian exactly. See e.g. the names of fields in struct tm (described e.g. in ctime(3)): they are like tm_sec, tm_min, tm_hour etc.. Similarly struct stat (see stat(2)), which has st_dev, st_ino etc.. These are redundant in modern C, but have to be preserved for backwards compatibility

– Ruslan
3 hours ago















@Raffzahn: In simple compilers, the primary cost of having a compiler use various information to select what kind of operation an operator will perform is generally making sure the compiler will have the information in the context where it is needed. A compiler that has scanned an expression as far as p=thing will need to know whether type of thing is a pointer or array, and the target/element type, if the next token is a +. Consequently, no special effort should be required to ensure that it has that information if the next token happens to be a . or -> instead.

– supercat
3 hours ago





@Raffzahn: In simple compilers, the primary cost of having a compiler use various information to select what kind of operation an operator will perform is generally making sure the compiler will have the information in the context where it is needed. A compiler that has scanned an expression as far as p=thing will need to know whether type of thing is a pointer or array, and the target/element type, if the next token is a +. Consequently, no special effort should be required to ensure that it has that information if the next token happens to be a . or -> instead.

– supercat
3 hours ago











25














Despite your assertion, there would in fact be situations where it would be ambigious.



First off, early C compilers were very simple. This was in fact the main appeal of the language, as compilers for it were very easy to create and could run on very small systems, like early 16/32 microprocessors.



Adding a bunch of code for hitting all the niche cases of type inference would have drastically added to the amount of code required to make a C compiler. In fact, I've argued as a (half) joke that K&R C had type inference, but it always inferred int. If you didn't tell C what type an object was, it assumed int (which could cause some really gnarly bugs, let me tell you...)



Secondly, since K&R C was weakly(barely) typed, the information in many cases flat out wasn't available. The destination type of a pointer assignment can be an int, or visa versa, and K&R C has no problem with that. The compiler simply cannot infer a dereference. The coder is assumed to know what she's doing.



Also realize that in C pointers and arrays are essentially syntactic sugar for each other. This means now your . operator would have to automagically work on arrays too. For instance, if member happened to be a char array, now structure.member would return with the first character. And again, both chars and pointer are assignable into ints, so context doesn't help you.



This being said, you aren't the first to notice this issue. In fact, Ada was designed that dereferencing a pointer object is always assumed when a dot is used. In those cases where you want the actual pointer, you have to use .all. The ambiguity (pointer vs. pointed to object) is still there, but resolved by moving the extra syntax to the weirder case.






share|improve this answer





















  • 4





    I love it. "K&R C had type inference, but it always inferred int". Brilliant.

    – Peter A. Schneider
    6 hours ago








  • 3





    "The coder is assumed to know what she's doing" is the C philosophy in a nutshell. That's simultaneously C's greatest strength and greatest weakness.

    – John Bode
    4 hours ago
















25














Despite your assertion, there would in fact be situations where it would be ambigious.



First off, early C compilers were very simple. This was in fact the main appeal of the language, as compilers for it were very easy to create and could run on very small systems, like early 16/32 microprocessors.



Adding a bunch of code for hitting all the niche cases of type inference would have drastically added to the amount of code required to make a C compiler. In fact, I've argued as a (half) joke that K&R C had type inference, but it always inferred int. If you didn't tell C what type an object was, it assumed int (which could cause some really gnarly bugs, let me tell you...)



Secondly, since K&R C was weakly(barely) typed, the information in many cases flat out wasn't available. The destination type of a pointer assignment can be an int, or visa versa, and K&R C has no problem with that. The compiler simply cannot infer a dereference. The coder is assumed to know what she's doing.



Also realize that in C pointers and arrays are essentially syntactic sugar for each other. This means now your . operator would have to automagically work on arrays too. For instance, if member happened to be a char array, now structure.member would return with the first character. And again, both chars and pointer are assignable into ints, so context doesn't help you.



This being said, you aren't the first to notice this issue. In fact, Ada was designed that dereferencing a pointer object is always assumed when a dot is used. In those cases where you want the actual pointer, you have to use .all. The ambiguity (pointer vs. pointed to object) is still there, but resolved by moving the extra syntax to the weirder case.






share|improve this answer





















  • 4





    I love it. "K&R C had type inference, but it always inferred int". Brilliant.

    – Peter A. Schneider
    6 hours ago








  • 3





    "The coder is assumed to know what she's doing" is the C philosophy in a nutshell. That's simultaneously C's greatest strength and greatest weakness.

    – John Bode
    4 hours ago














25












25








25







Despite your assertion, there would in fact be situations where it would be ambigious.



First off, early C compilers were very simple. This was in fact the main appeal of the language, as compilers for it were very easy to create and could run on very small systems, like early 16/32 microprocessors.



Adding a bunch of code for hitting all the niche cases of type inference would have drastically added to the amount of code required to make a C compiler. In fact, I've argued as a (half) joke that K&R C had type inference, but it always inferred int. If you didn't tell C what type an object was, it assumed int (which could cause some really gnarly bugs, let me tell you...)



Secondly, since K&R C was weakly(barely) typed, the information in many cases flat out wasn't available. The destination type of a pointer assignment can be an int, or visa versa, and K&R C has no problem with that. The compiler simply cannot infer a dereference. The coder is assumed to know what she's doing.



Also realize that in C pointers and arrays are essentially syntactic sugar for each other. This means now your . operator would have to automagically work on arrays too. For instance, if member happened to be a char array, now structure.member would return with the first character. And again, both chars and pointer are assignable into ints, so context doesn't help you.



This being said, you aren't the first to notice this issue. In fact, Ada was designed that dereferencing a pointer object is always assumed when a dot is used. In those cases where you want the actual pointer, you have to use .all. The ambiguity (pointer vs. pointed to object) is still there, but resolved by moving the extra syntax to the weirder case.






share|improve this answer















Despite your assertion, there would in fact be situations where it would be ambigious.



First off, early C compilers were very simple. This was in fact the main appeal of the language, as compilers for it were very easy to create and could run on very small systems, like early 16/32 microprocessors.



Adding a bunch of code for hitting all the niche cases of type inference would have drastically added to the amount of code required to make a C compiler. In fact, I've argued as a (half) joke that K&R C had type inference, but it always inferred int. If you didn't tell C what type an object was, it assumed int (which could cause some really gnarly bugs, let me tell you...)



Secondly, since K&R C was weakly(barely) typed, the information in many cases flat out wasn't available. The destination type of a pointer assignment can be an int, or visa versa, and K&R C has no problem with that. The compiler simply cannot infer a dereference. The coder is assumed to know what she's doing.



Also realize that in C pointers and arrays are essentially syntactic sugar for each other. This means now your . operator would have to automagically work on arrays too. For instance, if member happened to be a char array, now structure.member would return with the first character. And again, both chars and pointer are assignable into ints, so context doesn't help you.



This being said, you aren't the first to notice this issue. In fact, Ada was designed that dereferencing a pointer object is always assumed when a dot is used. In those cases where you want the actual pointer, you have to use .all. The ambiguity (pointer vs. pointed to object) is still there, but resolved by moving the extra syntax to the weirder case.







share|improve this answer














share|improve this answer



share|improve this answer








edited 22 hours ago

























answered 22 hours ago









T.E.D.T.E.D.

86936




86936








  • 4





    I love it. "K&R C had type inference, but it always inferred int". Brilliant.

    – Peter A. Schneider
    6 hours ago








  • 3





    "The coder is assumed to know what she's doing" is the C philosophy in a nutshell. That's simultaneously C's greatest strength and greatest weakness.

    – John Bode
    4 hours ago














  • 4





    I love it. "K&R C had type inference, but it always inferred int". Brilliant.

    – Peter A. Schneider
    6 hours ago








  • 3





    "The coder is assumed to know what she's doing" is the C philosophy in a nutshell. That's simultaneously C's greatest strength and greatest weakness.

    – John Bode
    4 hours ago








4




4





I love it. "K&R C had type inference, but it always inferred int". Brilliant.

– Peter A. Schneider
6 hours ago







I love it. "K&R C had type inference, but it always inferred int". Brilliant.

– Peter A. Schneider
6 hours ago






3




3





"The coder is assumed to know what she's doing" is the C philosophy in a nutshell. That's simultaneously C's greatest strength and greatest weakness.

– John Bode
4 hours ago





"The coder is assumed to know what she's doing" is the C philosophy in a nutshell. That's simultaneously C's greatest strength and greatest weakness.

– John Bode
4 hours ago











20














Some of the first C code I saw was like this: 0x8040->output = 'A'; — its purpose was accessing memory mapped I/O locations.  Needless to say it took me a while to figure out what this code was supposed to do, and the hex constant there really threw me.



The original K&R C placed all field names (here output) into the same namespace.  It was an error to have two fields of the same name in different structs at different offsets — but ok to have the same name at the same offset, the idea here being that two different structs could share the same initial fields, giving cheap way of doing "subclassing" to put varying data members at the end of the struct.



A struct could also be anonymous, e.g. no tag name for the struct.  None the less, the members could still be used in . or -> expressions.




The C Programming Language (K&R C) Appendix A, p197,209




[8.5] ... Two structures may share a common initial sequence of members; that is, the same member may appear in two different structures if it has the same type in both and if all previous members are the same in both.  (Actually, the compiler checks only that a name in two different structures has the same type and offset in both, but if the preceding members differ the construction is nonportable.)



...



[14.1] ... §7.1 says that in a direct or indirect structure reference (with . or ->) the name on the right must be a member of the structure named or pointed to by the expression on the left.  To allow an escape from the typing rules, this restriction is not firmly enforced by the compiler.  In fact, any lvalue is allowed before ., and the lvalue is then assumed to have the form of the structure of which the name on the right is a member.  Also, the expression before a -> is required only to be a pointer or an integer.  If an integer, it is taken to be the absolute address, in machine storage units, of the appropriate structure.





Since the K&R language & compiler didn't care what the type of the left hand side of . and -> was, the only way it had to tell the difference by having the two operators.



The ANSI C line of standards simply followed suit in syntax, even as these old rules were abandoned.






share|improve this answer



















  • 1





    Aha, so that’s why POSIX struct member names are all prefixed (e. g. stat::st_uid). I thought it was to enable shenanigans like #define st_atime st_atim.tv_sec.

    – Roman Odaisky
    5 hours ago
















20














Some of the first C code I saw was like this: 0x8040->output = 'A'; — its purpose was accessing memory mapped I/O locations.  Needless to say it took me a while to figure out what this code was supposed to do, and the hex constant there really threw me.



The original K&R C placed all field names (here output) into the same namespace.  It was an error to have two fields of the same name in different structs at different offsets — but ok to have the same name at the same offset, the idea here being that two different structs could share the same initial fields, giving cheap way of doing "subclassing" to put varying data members at the end of the struct.



A struct could also be anonymous, e.g. no tag name for the struct.  None the less, the members could still be used in . or -> expressions.




The C Programming Language (K&R C) Appendix A, p197,209




[8.5] ... Two structures may share a common initial sequence of members; that is, the same member may appear in two different structures if it has the same type in both and if all previous members are the same in both.  (Actually, the compiler checks only that a name in two different structures has the same type and offset in both, but if the preceding members differ the construction is nonportable.)



...



[14.1] ... §7.1 says that in a direct or indirect structure reference (with . or ->) the name on the right must be a member of the structure named or pointed to by the expression on the left.  To allow an escape from the typing rules, this restriction is not firmly enforced by the compiler.  In fact, any lvalue is allowed before ., and the lvalue is then assumed to have the form of the structure of which the name on the right is a member.  Also, the expression before a -> is required only to be a pointer or an integer.  If an integer, it is taken to be the absolute address, in machine storage units, of the appropriate structure.





Since the K&R language & compiler didn't care what the type of the left hand side of . and -> was, the only way it had to tell the difference by having the two operators.



The ANSI C line of standards simply followed suit in syntax, even as these old rules were abandoned.






share|improve this answer



















  • 1





    Aha, so that’s why POSIX struct member names are all prefixed (e. g. stat::st_uid). I thought it was to enable shenanigans like #define st_atime st_atim.tv_sec.

    – Roman Odaisky
    5 hours ago














20












20








20







Some of the first C code I saw was like this: 0x8040->output = 'A'; — its purpose was accessing memory mapped I/O locations.  Needless to say it took me a while to figure out what this code was supposed to do, and the hex constant there really threw me.



The original K&R C placed all field names (here output) into the same namespace.  It was an error to have two fields of the same name in different structs at different offsets — but ok to have the same name at the same offset, the idea here being that two different structs could share the same initial fields, giving cheap way of doing "subclassing" to put varying data members at the end of the struct.



A struct could also be anonymous, e.g. no tag name for the struct.  None the less, the members could still be used in . or -> expressions.




The C Programming Language (K&R C) Appendix A, p197,209




[8.5] ... Two structures may share a common initial sequence of members; that is, the same member may appear in two different structures if it has the same type in both and if all previous members are the same in both.  (Actually, the compiler checks only that a name in two different structures has the same type and offset in both, but if the preceding members differ the construction is nonportable.)



...



[14.1] ... §7.1 says that in a direct or indirect structure reference (with . or ->) the name on the right must be a member of the structure named or pointed to by the expression on the left.  To allow an escape from the typing rules, this restriction is not firmly enforced by the compiler.  In fact, any lvalue is allowed before ., and the lvalue is then assumed to have the form of the structure of which the name on the right is a member.  Also, the expression before a -> is required only to be a pointer or an integer.  If an integer, it is taken to be the absolute address, in machine storage units, of the appropriate structure.





Since the K&R language & compiler didn't care what the type of the left hand side of . and -> was, the only way it had to tell the difference by having the two operators.



The ANSI C line of standards simply followed suit in syntax, even as these old rules were abandoned.






share|improve this answer













Some of the first C code I saw was like this: 0x8040->output = 'A'; — its purpose was accessing memory mapped I/O locations.  Needless to say it took me a while to figure out what this code was supposed to do, and the hex constant there really threw me.



The original K&R C placed all field names (here output) into the same namespace.  It was an error to have two fields of the same name in different structs at different offsets — but ok to have the same name at the same offset, the idea here being that two different structs could share the same initial fields, giving cheap way of doing "subclassing" to put varying data members at the end of the struct.



A struct could also be anonymous, e.g. no tag name for the struct.  None the less, the members could still be used in . or -> expressions.




The C Programming Language (K&R C) Appendix A, p197,209




[8.5] ... Two structures may share a common initial sequence of members; that is, the same member may appear in two different structures if it has the same type in both and if all previous members are the same in both.  (Actually, the compiler checks only that a name in two different structures has the same type and offset in both, but if the preceding members differ the construction is nonportable.)



...



[14.1] ... §7.1 says that in a direct or indirect structure reference (with . or ->) the name on the right must be a member of the structure named or pointed to by the expression on the left.  To allow an escape from the typing rules, this restriction is not firmly enforced by the compiler.  In fact, any lvalue is allowed before ., and the lvalue is then assumed to have the form of the structure of which the name on the right is a member.  Also, the expression before a -> is required only to be a pointer or an integer.  If an integer, it is taken to be the absolute address, in machine storage units, of the appropriate structure.





Since the K&R language & compiler didn't care what the type of the left hand side of . and -> was, the only way it had to tell the difference by having the two operators.



The ANSI C line of standards simply followed suit in syntax, even as these old rules were abandoned.







share|improve this answer












share|improve this answer



share|improve this answer










answered 18 hours ago









Erik EidtErik Eidt

1,337413




1,337413








  • 1





    Aha, so that’s why POSIX struct member names are all prefixed (e. g. stat::st_uid). I thought it was to enable shenanigans like #define st_atime st_atim.tv_sec.

    – Roman Odaisky
    5 hours ago














  • 1





    Aha, so that’s why POSIX struct member names are all prefixed (e. g. stat::st_uid). I thought it was to enable shenanigans like #define st_atime st_atim.tv_sec.

    – Roman Odaisky
    5 hours ago








1




1





Aha, so that’s why POSIX struct member names are all prefixed (e. g. stat::st_uid). I thought it was to enable shenanigans like #define st_atime st_atim.tv_sec.

– Roman Odaisky
5 hours ago





Aha, so that’s why POSIX struct member names are all prefixed (e. g. stat::st_uid). I thought it was to enable shenanigans like #define st_atime st_atim.tv_sec.

– Roman Odaisky
5 hours ago











17














I think there are two factors that led to standardization of the distinct operator "->" for accessing data members using a pointer.




  1. You assume that the C compiler would recognize the type of the LHS as being a pointer. But programmers could, and often did, override the initial typing (variable declaration) by using a typecast.

  2. In order to make the code more readable and less prone to unintended side-effects, it is useful to distinguish operations using pointers.


A very common feature of idiomatic C code is that a structure passed to a function as a pointer is modified within the function. Thus, the result is returned implicitly, by the side-effect of the structure variable in the calling function having been modified by the callee. This sort of approach violates modern sensibilities about loosely coupled code, but it was a simple and efficient means of dealing with complexity in C code. I would say the programmer was greatly assisted in maintaining the readability of such code by having distinct operations that made it clear whether some (possibly shared) memory pointer was the thing whose target was being modified.






share|improve this answer


























  • Could you give an example of your point #1? The type of the result of a typecast is well-defined... it's the type you are typecasting to. Should that be a pointer to a structure, the compiler has enough information to access its members. Whatever the type was prior to the typecast is irrelevant.

    – Dr Sheldon
    yesterday








  • 1





    @DrSheldon Yes, you are correct. The compiler can check operators used against the type of a typecast too, and interpret a single operator appropriately. But then you are removing one of the compiler checks against programming errors. I think #1 and #2 actually work in concert to push the programmer toward care with pointer references. If the compiler tries to be "too smart" it ends up misinterpreting what the error-prone programmer intended, and (perhaps more important) makes the code harder to read.

    – Brian H
    yesterday













  • I think this is the best answer, C (and thus C++)'s use of -> is a feature not a bug.

    – Jack Aidley
    5 hours ago
















17














I think there are two factors that led to standardization of the distinct operator "->" for accessing data members using a pointer.




  1. You assume that the C compiler would recognize the type of the LHS as being a pointer. But programmers could, and often did, override the initial typing (variable declaration) by using a typecast.

  2. In order to make the code more readable and less prone to unintended side-effects, it is useful to distinguish operations using pointers.


A very common feature of idiomatic C code is that a structure passed to a function as a pointer is modified within the function. Thus, the result is returned implicitly, by the side-effect of the structure variable in the calling function having been modified by the callee. This sort of approach violates modern sensibilities about loosely coupled code, but it was a simple and efficient means of dealing with complexity in C code. I would say the programmer was greatly assisted in maintaining the readability of such code by having distinct operations that made it clear whether some (possibly shared) memory pointer was the thing whose target was being modified.






share|improve this answer


























  • Could you give an example of your point #1? The type of the result of a typecast is well-defined... it's the type you are typecasting to. Should that be a pointer to a structure, the compiler has enough information to access its members. Whatever the type was prior to the typecast is irrelevant.

    – Dr Sheldon
    yesterday








  • 1





    @DrSheldon Yes, you are correct. The compiler can check operators used against the type of a typecast too, and interpret a single operator appropriately. But then you are removing one of the compiler checks against programming errors. I think #1 and #2 actually work in concert to push the programmer toward care with pointer references. If the compiler tries to be "too smart" it ends up misinterpreting what the error-prone programmer intended, and (perhaps more important) makes the code harder to read.

    – Brian H
    yesterday













  • I think this is the best answer, C (and thus C++)'s use of -> is a feature not a bug.

    – Jack Aidley
    5 hours ago














17












17








17







I think there are two factors that led to standardization of the distinct operator "->" for accessing data members using a pointer.




  1. You assume that the C compiler would recognize the type of the LHS as being a pointer. But programmers could, and often did, override the initial typing (variable declaration) by using a typecast.

  2. In order to make the code more readable and less prone to unintended side-effects, it is useful to distinguish operations using pointers.


A very common feature of idiomatic C code is that a structure passed to a function as a pointer is modified within the function. Thus, the result is returned implicitly, by the side-effect of the structure variable in the calling function having been modified by the callee. This sort of approach violates modern sensibilities about loosely coupled code, but it was a simple and efficient means of dealing with complexity in C code. I would say the programmer was greatly assisted in maintaining the readability of such code by having distinct operations that made it clear whether some (possibly shared) memory pointer was the thing whose target was being modified.






share|improve this answer















I think there are two factors that led to standardization of the distinct operator "->" for accessing data members using a pointer.




  1. You assume that the C compiler would recognize the type of the LHS as being a pointer. But programmers could, and often did, override the initial typing (variable declaration) by using a typecast.

  2. In order to make the code more readable and less prone to unintended side-effects, it is useful to distinguish operations using pointers.


A very common feature of idiomatic C code is that a structure passed to a function as a pointer is modified within the function. Thus, the result is returned implicitly, by the side-effect of the structure variable in the calling function having been modified by the callee. This sort of approach violates modern sensibilities about loosely coupled code, but it was a simple and efficient means of dealing with complexity in C code. I would say the programmer was greatly assisted in maintaining the readability of such code by having distinct operations that made it clear whether some (possibly shared) memory pointer was the thing whose target was being modified.







share|improve this answer














share|improve this answer



share|improve this answer








edited yesterday

























answered yesterday









Brian HBrian H

18.4k69158




18.4k69158













  • Could you give an example of your point #1? The type of the result of a typecast is well-defined... it's the type you are typecasting to. Should that be a pointer to a structure, the compiler has enough information to access its members. Whatever the type was prior to the typecast is irrelevant.

    – Dr Sheldon
    yesterday








  • 1





    @DrSheldon Yes, you are correct. The compiler can check operators used against the type of a typecast too, and interpret a single operator appropriately. But then you are removing one of the compiler checks against programming errors. I think #1 and #2 actually work in concert to push the programmer toward care with pointer references. If the compiler tries to be "too smart" it ends up misinterpreting what the error-prone programmer intended, and (perhaps more important) makes the code harder to read.

    – Brian H
    yesterday













  • I think this is the best answer, C (and thus C++)'s use of -> is a feature not a bug.

    – Jack Aidley
    5 hours ago



















  • Could you give an example of your point #1? The type of the result of a typecast is well-defined... it's the type you are typecasting to. Should that be a pointer to a structure, the compiler has enough information to access its members. Whatever the type was prior to the typecast is irrelevant.

    – Dr Sheldon
    yesterday








  • 1





    @DrSheldon Yes, you are correct. The compiler can check operators used against the type of a typecast too, and interpret a single operator appropriately. But then you are removing one of the compiler checks against programming errors. I think #1 and #2 actually work in concert to push the programmer toward care with pointer references. If the compiler tries to be "too smart" it ends up misinterpreting what the error-prone programmer intended, and (perhaps more important) makes the code harder to read.

    – Brian H
    yesterday













  • I think this is the best answer, C (and thus C++)'s use of -> is a feature not a bug.

    – Jack Aidley
    5 hours ago

















Could you give an example of your point #1? The type of the result of a typecast is well-defined... it's the type you are typecasting to. Should that be a pointer to a structure, the compiler has enough information to access its members. Whatever the type was prior to the typecast is irrelevant.

– Dr Sheldon
yesterday







Could you give an example of your point #1? The type of the result of a typecast is well-defined... it's the type you are typecasting to. Should that be a pointer to a structure, the compiler has enough information to access its members. Whatever the type was prior to the typecast is irrelevant.

– Dr Sheldon
yesterday






1




1





@DrSheldon Yes, you are correct. The compiler can check operators used against the type of a typecast too, and interpret a single operator appropriately. But then you are removing one of the compiler checks against programming errors. I think #1 and #2 actually work in concert to push the programmer toward care with pointer references. If the compiler tries to be "too smart" it ends up misinterpreting what the error-prone programmer intended, and (perhaps more important) makes the code harder to read.

– Brian H
yesterday







@DrSheldon Yes, you are correct. The compiler can check operators used against the type of a typecast too, and interpret a single operator appropriately. But then you are removing one of the compiler checks against programming errors. I think #1 and #2 actually work in concert to push the programmer toward care with pointer references. If the compiler tries to be "too smart" it ends up misinterpreting what the error-prone programmer intended, and (perhaps more important) makes the code harder to read.

– Brian H
yesterday















I think this is the best answer, C (and thus C++)'s use of -> is a feature not a bug.

– Jack Aidley
5 hours ago





I think this is the best answer, C (and thus C++)'s use of -> is a feature not a bug.

– Jack Aidley
5 hours ago


















draft saved

draft discarded




















































Thanks for contributing an answer to Retrocomputing Stack Exchange!


  • Please be sure to answer the question. Provide details and share your research!

But avoid



  • Asking for help, clarification, or responding to other answers.

  • Making statements based on opinion; back them up with references or personal experience.


To learn more, see our tips on writing great answers.




draft saved


draft discarded














StackExchange.ready(
function () {
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fretrocomputing.stackexchange.com%2fquestions%2f10812%2fwhy-did-c-use-the-operator-instead-of-reusing-the-operator%23new-answer', 'question_page');
}
);

Post as a guest















Required, but never shown





















































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown

































Required, but never shown














Required, but never shown












Required, but never shown







Required, but never shown







Popular posts from this blog

VNC viewer RFB protocol error: bad desktop size 0x0I Cannot Type the Key 'd' (lowercase) in VNC Viewer...

Couldn't open a raw socket. Error: Permission denied (13) (nmap)Is it possible to run networking commands...

Why not use the yoke to control yaw, as well as pitch and roll? Announcing the arrival of...