表达式(十三)
九、判等运算符
判等运算符包括:等于运算符(==)、不等于运算符(!=),其语法格式分别如下所示:
expression == expression
expression != expression
其中expression为表达式。
对于判等运算符,其操作数应满足以下条件之一:
-- 左操作数和右操作数都具有算术类型。
4 != 5
-- 左操作数和右操作数都是指向限定或者非限定版本的兼容类型的指针。
int arr[8]; int *p1 = &arr[1]; int *p2 = &arr[6]; p2 != p1;
-- 左操作数和右操作数中一个是指向对象类型的指针,另一个是指向限定或者非限定版本的void类型的指针。
int a = 3; float b = 3.14f; void *ptr = &a; ptr != &b;
-- 左操作数和右操作数都具有nullptr_t类型。
nullptr_t p1;
nullptr_t p2;
p1 == p2;
-- 左操作数和右操作数中一个具有nullptr_t类型,另一个是空指针常量。
nullptr_t ptr; ptr == nullptr;
-- 左操作数和右操作数中一个是指针,另一个是空指针常量(null pointer constant)或者具有nullptr_t类型。
int *ptr = NULL; ptr = malloc(3*sizeof(int)); ptr == NULL;
如果一个操作数是十进制浮点类型(decimal floating type),另一个操作数不能是标准浮点类型(standard floating type)、复数类型或者虚数类型。
// 以下判等表达式非法; // 1.23DD是_Decimal64类型,属于十进制浮点类型; // 1.23是double类型,属于标准浮点类型。 1.23DD == 1.23
判等运算符类似于关系运算符,但判等运算符的优先级低于关系运算符。如果判等运算符指定的关系为真,运算结果为1,如果判等运算符指定的关系为假,运算结果为0。判等运算符运算结果的类型是int类型。
5 != 4; //表达式的值为1。 1 < 2 == 3 < 4; //表达式的值为1。
由于关系运算符的优先级高于判等运算符,先运算1 < 2和3 < 4,结果都是1,等于运算符的左操作数和右操作数相等,因此表达式1 < 2 == 3 < 4的值为1。
如果判等运算符的两个操作数都具有算术类型,操作数会执行常用算术转换(usual arithmetic conversions)。+0和-0相等。如果操作数是复数类型,当且仅当复数的实部(real parts)和虚部(imaginary parts)都相等时,复数值才相等。
double complex x = 0.5 + 1.0I; float complex y = 0.5f + 1.0fI; printf("%d\n", 65=='A'); //输出1。 printf("%d\n", x==y); //输出1。
不同类型域的两个算术类型值,只有当其转换成由常用算术转换规则确定的(复数)结果类型时相等才相等。
double complex dc = 0.5 + 0.0I; //dc属于复数类型域。 double d = 0.5; //d属于实数类型域。 d == dc; //表达式d == dc的值为1。
判等运算符的两个操作数如果都是nullptr_t类型,或者一个操作数是nullptr_t类型,另一个是空指针常量,那么这两个操作数相等。
nullptr_t p1; nullptr_t p2; p1 == p2; //表达式p1 == p2的值为1。 p1 == nullptr; //表达式p1 == nullptr的值为1。
判等运算符的两个操作数,如果一个操作数是空指针,另一个操作数是空指针常量或者具有nullptr_t类型,则两个操作数相等。
int *ptr = NULL; nullptr_t p; ptr == p; //表达式ptr == p的值为1。 ptr == nullptr; //表达式ptr == nullptr的值为1。
判等运算符的两个操作数,如果一个操作数是指向对象类型的指针,另一个操作数是指向限定或者非限定版本的void类型的指针,前者将转换成后者类型。
int *p1 = NULL; void *p2 = NULL; p1 == p2; //p1将转换成void *类型。
如果两个指针相等,应满足以下条件之一:
-- 两个指针都是空指针。
int *p1 = NULL; int *p2 = NULL; p1 == p2; //表达式p1 == p2值为1。
-- 两个指针指向相同的对象(包括一个指针指向对象,另一个指针指向第一个子对象的情况。)或者函数。
struct s{ int i; double d; }; ... int a; int *p1 = &a; int *p2 = &a; p1 == p2; //表达式p1 == p2的值为1。 struct s S; struct s *p3 = &S; int *p4 = &S.i; p3 == (struct s *)p4; //表达式p3 == (struct s *)p4的值为1。
-- 两个指针都是指向同一数组最后一个元素之后的那个元素。
int arr[5]; int *p1 = &arr[5]; int *p2 = arr + 5; p1 == p2; //表达式p1 == p2的值为1。
-- 两个指针中一个指向第一个数组最后一个元素之后的那个元素,另一个指针指向第二个数组的第一个元素,并且在地址空间上第二个数组紧跟在第一个数组之后。
struct{ int a[3]; int b[3]; } S; int *p1 = &S.a[3]; int *p2 = &S.b[0]; p1 == p2; //表达式p1 == p2的值为1。
两个对象在内存中相邻,常见的三种情况是:1、同一数组的相邻元素;2、同一结构的相邻成员,并且它们之间没有填充;3、尽管两个对象没有关联,但实现将两个对象放在一起。
int arr[3][5]; &arr[0][5] == &arr[1][0]; //表达式&arr[0][5] == &arr[1][0]的值为1。 struct { int i; double d1; double d2; } S; &S.i+1 != (int *)&S.d1; //表达式&S.i+1 != (int *)&S.d1的值为1。 &S.d1+1 == &S.d2; //表达式&S.d1+1 == &S.d2的值为1。
数组元素arr[0][4]是第一行的最后一个元素,数组元素arr[1][0]是第二行的第一个元素,因此数组元素arr[0][4]和数组元素arr[1][0]是相邻元素,数组元素arr[1][0]是数组元素arr[0][4]后的那个元素。表达式arr[0][5]表示第一行的最后一个元素arr[0][4]后的那个元素,因此表达式&arr[0][5]和表达式&arr[1][0]的值相等。
S.i和S.d1是相邻结构成员,但它们之间存在填充,所以在内存中是不相邻的;S.d1和S.d2是相邻结构成员,它们之间不存在填充,所以在内存中是相邻的。指向非数组元素的对象指针的行为与指向长度为1的数组的第一个元素的指针的行为相同。&S.d1+1指向S.d1后的那个元素,所以&S.d1+1和&S.d2指向相同位置。&S.i+1指向S.i后的那个元素,由于S.i和S.d1之间存在填充,所以&S.i+1指向的是填充,而不是S.d1。