C ++编译器是否真的足够聪明以区分乘法和解除引用?

时间:2012-02-12 01:01:31

标签: c++

我有以下代码行:

double *resultOfMultiplication = new double(*num1 * *num2);

编译器如何知道哪些*用于去激励以及哪个*用于乘法?

此外,在这种情况下,一个更重要的问题可能是double一个原语(比如Java)或一个对象?如果它是原始的,我怎么能创建一个new

6 个答案:

答案 0 :(得分:4)

编译器不需要“智能”;语言的语法由语法产品树定义,该树本质上赋予某些运算符在其他运算符的应用上的应用优先级或“优先级”。当表达式可能不明确时(因为,例如,使用的两个运算符由相同的词法标记表示),这是特别方便的。

但这只是lexing和解析。是否有任何特定操作实际上在语义上有效,直到编译后才会确定;特别是,给定两个指针xy,表达式*x *y将无法编译,因为您无法将*x乘以y,而不是因为有{{1}}缺少运算符,否则可能是一个解除引用,然后是另一个解除引用。

我不会深入证明C ++中存在运算符优先级;为此,只需要学习语法结构的基础课程,你就可以很快完成它。

答案 1 :(得分:4)

也许类比会有所帮助:

问:人们如何将“i”上方的点与句子末尾的点分开?他们真的很聪明,他们不会将每一个'我'解释为句子的结尾吗?

答:因为他们在不同的地方!

编译器的'*'也是如此:它们出现在不同的位置。乘法运算符位于两个表达式的中间;解除引用操作符位于表达式前面。这对您来说可能并不明显,但对于编译器来说这是显而易见的。

解析时任何体面的文本都会告诉你编译器是如何做到这一点的。所需的技术大约在40年前开发出来,被认为是编译器中最基本的事物之一。 C ++编译器必须有许多智能部件,但这不是其中之一。

专家注意:我知道因素,左值等。但他们只会在这种情况下混淆。

答案 2 :(得分:3)

这完全是关于语法的。没有后缀*,因此在标识符必须被视为中缀乘以后*

答案 3 :(得分:0)

首先取消引用,因此*num1 * *num2被解析为(*num1) * (*num2),这是明确的。 *resultOfMultiplication未被解析为解除引用,因为它是变量定义。在这样的上下文中,编译器期望数据类型后跟标识符,因此星号是明确的。

原始数据类型仍然是C ++中的对象。如果对基本类型使用new,那么所有发生的事情就是分配用于保存对象的空闲存储中的足够内存并将其地址返回给您。这与“普通”变量(即double t;)不同,后者具有自动或静态存储持续时间。

答案 4 :(得分:0)

由于问题是关于“聪明”,我想补充一点。我的答案将参考C语言,但我认为C ++中的情况是相同的。

在这种情况下,编译器不需要真正智能,因为语言不允许直接在地址上执行乘法,因此指针前面的符号*始终只能表示“取消引用”,而非指针前面的符号*只能表示“乘法”。

我将尝试用一个例子解释这个。

让我们创建一个小程序,然后将其称为test.c。然后,我们在main()函数中创建两个指针firstsecond,并让first的地址为140732806008300,地址为{ {1}} second

当我们尝试求和两个地址时,编译器允许我们在没有先前强制转换的情况下和平地使用140732806008296操作数,因为在C中允许指针的总和:

+

我们在这里做的是直接执行两个指针的总和,得到一个新指针作为结果,为了这个缘故,然后被转换为无符号整数#include <stdio.h> int main () { int *first, *second, fifteen = 15, twenty = 20; first = &fifteen; /* Let the address of `first` be 140732806008300 */ second = &twenty; /* Let the address of `second` be 140732806008296 */ printf("first + second is: %llu\n", (long long unsigned int) first + second); return 0; } 函数。由于这是允许的,我们正确地获取字符串

prinf()

但是如果我们尝试使用乘法操作数first + second is: 703664030041496 乘以两个地址...

*

...我们收到以下错误:

#include <stdio.h>

int main () {

    int *first, *second, fifteen = 15, twenty = 20;

    first = &fifteen;   /* Let the address of `first` be 140732806008300 */
    second = &twenty;   /* Let the address of `second` be 140732806008296 */

    printf("first * second is: %llu\n", (long long unsigned int) first * second);

    return 0;

}

这是因为不允许直接乘法地址。因此,在能够最终使用符号为“乘法操作数”的符号test.c: In function ‘main’: test.c:11:69: error: invalid operands to binary * (have ‘long long unsigned int’ and ‘int *’) printf("first * second is: %llu\n", (long long unsigned int) first * second); 之前,我们必须将指针强制转换为有效的整数

*

...现在我们终于让符号#include <stdio.h> int main () { int *first, *second, fifteen = 15, twenty = 20; first = &fifteen; /* Let the address of `first` be 140732806008300 */ second = &twenty; /* Let the address of `second` be 140732806008296 */ printf("first * second is: %llu\n", (long long unsigned int) ((long unsigned int) first) * ((long unsigned int) second)); return 0; } 站在整数之间(而不是指针之间)。在这种情况下,它只能意味着“乘法”(而不是其他),所以我们最终得到了正确的操作结果:

*

我试着考虑一些例子,其中符号first * second is: 4480243502683625952 的含义不能被人类消除(但也没有成功),但没有成功。

这意味着符号*可以明确地仅表示给定上下文中的一个事物 - 即,在某些情况下我们必须使用括号来改变其含义,但其含义在其上下文中始终是明确的。

答案 5 :(得分:0)

它取决于使用它的上下文,为了简单解决,它会查看左右单词以了解符号是什么。

进一步阅读维基百科页面:Lexer_hack