表达式(十一)
六、乘法类运算符
乘法类运算符包括:乘法运算符(*)、除法运算符(/)、取余运算符(%),其语法格式分别如下所示:
expression * expression
expression / expression
expression % expression
其中expression是表达式。乘法运算符(*)、除法运算符(/)的操作数应是算术类型(arithmetic type);取余运算符(%)的操作数应是整数类型。乘法运算符(*)、除法运算符(/)、取余运算符(%)的操作数会执行常用算术转换(usual arithmetic conversions)。
对于乘法运算符和除法运算符,如果一个操作数是十进制浮点类型(decimal floating type),另一个操作数不能是标准浮点类型(standard floating type)、复数类型或者虚数类型。
// 以下乘法表达式非法; // 1.23DD是_Decimal64类型,属于十进制浮点类型; // 1.23是double类型,属于标准浮点类型。 1.23DD * 1.23
乘法运算符(*)的运算结果是操作数的乘积。
4*5; //表达式的值是20,类型是int类型。
除法运算符(/)的运算结果是第一个操作数除以第二个操作数的商;取余运算符(%)的运算结果是第一个操作数除以第二个操作数的余数。对于除法运算符(/)和取余运算符(%),如果第二个操作数是0,其行为是未定义的。
8/4; //表达式的值是2,类型是int类型。 9%4; //表达式的值是1,类型是int类型。
如果除法运算符(/)的操作数都是整数,运算结果是代数商(algebraic quotient),结果中的分数部分会被丢弃,这通常称为向0取整(truncation toward zero)。
11/4; //值为2。 -11/4; //值为-2。
如果表达式A/B的商是个可表示值,则表达式(A/B)*B+A%B应等于A,否则A/B和A%B的行为是未定义的。
七、加法类运算符
加法类运算符包括:加法运算符(+)、减法运算符(-),其语法格式分别如下所示:
expression + expression
expression - expression
其中expression为表达式。
对于加法运算符(+),其操作数类型应满足以下条件之一:
-- 左操作数和右操作数都具有算术类型。
4 + 5
-- 左操作数和右操作数中一个是指向完整对象类型的指针,另一个是整数类型。
int arr[3]; int *ptr = arr + 1;
如果一个操作数是十进制浮点类型(decimal floating type),另一个操作数不能是标准浮点类型(standard floating type)、复数类型或者虚数类型。
// 以下加法表达式非法; // 1.23DD是_Decimal64类型,属于十进制浮点类型; // 1.23是double类型,属于标准浮点类型。 1.23DD + 1.23
如果加法运算符(+)的两个操作数都是算术类型,操作数会执行常用算术转换(usual arithmetic conversions)。
加法运算符(+)的运算结果是操作数之和;如果操作数都是算术类型,结果类型是操作数执行常用算术转换后对应的类型;如果一个操作数是指针,结果类型是指针对应的类型。
unsigned char uc = 'A'; unsigned char uch = 'B'; uc + uch; //表达式uc+uch的类型是int类型。 &uc + 1; //表达式&uc+1的类型是unsigned char *类型。
对于减法运算符(-),其操作数类型应满足以下条件之一:
-- 左操作数和右操作数都具有算术类型。
5 - 4
-- 左操作数和右操作数都是指向限定或者非限定版本的兼容的完整对象类型的指针。
int arr[8]; int *ptr = &arr[5]; ptr - arr;
-- 左操作数是指向完整对象类型的指针,右操作数具有整数类型。
int arr[8]; int *ptr = &arr[5]; ptr - 3;
如果一个操作数是十进制浮点类型(decimal floating type),另一个操作数不能是标准浮点类型(standard floating type)、复数类型或者虚数类型。
// 以下减法表达式非法; // 1.23DD是_Decimal64类型,属于十进制浮点类型; // 1.23是double类型,属于标准浮点类型。 1.23DD - 1.23
如果减法运算符(-)的两个操作数都是算术类型,操作数会执行常用算术转换(usual arithmetic conversions)。
减法运算符(-)的运算结果是第一个操作数减去第二个操作数所得的差值;如果操作数都是算术类型,结果类型是操作数执行常用算术转换后对应的类型;如果只有一个操作数是指针类型,结果类型是指针对应的类型;如果两个操作数都是指针类型,结果类型是ptrdiff_t类型。
unsigned char arr[] = "ABC"; unsigned char *ptr = &arr[1]; arr[1] - arr[0]; //表达式arr[1] - arr[0]的类型是int类型。 ptr - 1; //表达式ptr - 1的类型是unsigned char *类型。 ptr - arr; //表达式ptr - arr的类型是ptrdiff_t类型。
加法运算符(+)或者减法运算符(-)的两个操作数,如果一个是指向数组元素的指针ptr,另一个是整数N,假设数组足够大,运算结果指向数组元素ptr+N或者ptr-N,该数组元素和原数组元素的下标之差等于整数N或者-N。
int arr[10]; int *ptr = &arr[5]; ptr - 3; //指向第3个数组元素。 ptr + 3; //指向第9个数组元素。
如果指针ptr指向数组的最后一个元素,表达式ptr+1指向数组最后一个元素之后的那个元素。如果指针ptr指向数组最后一个元素之后的那个元素,表达式ptr-1指向数组的最后一个元素。
int arr[5]; int *ptr = &arr[4]; int *temp = ptr + 1; //指针temp指向数组最后一个元素之后的那个元素。 ptr = temp - 1; //指针ptr指向数组的最后一个元素。
如果减法运算符(-)的两个操作数都是指针,并且指向同一数组的元素或者指向数组最后一个元素之后的那个元素,则运算结果不会溢出;否则其行为是未定义的。当两个指针相减时,结果是两个数组元素的下标之差。如果指针指向数组最后一个元素之后的那个元素,该指针不能用作间接运算符(*)的操作数。
int arr[5] = {0,1,2,3,4}; int *ptr = &arr[5]; ptr - arr; //值为5,类型为ptrdiff_t类型。 *ptr; //非法。
指针运算时可以先转换成字符指针,然后再加上或者减去整数表达式和指针原先指向对象的大小的乘积,最后将得到的指针转换回原来的类型。对于指针相减,字符指针之间的差值应除以原来指向对象的大小。从这个角度讲,实现只需要在数组末尾提供一个额外字节,就能满足“超过最后一个元素”的要求。
int arr[5] = {0,1,2,3,4}; signed char *ptr = (signed char *)arr; ptr += 2*sizeof(int); (int *)ptr; //指针ptr指向数组第3个元素。 signed char *p1 = (signed char *)&arr[1]; signed char *p2 = (signed char *)&arr[3]; printf("%td\n", (p2-p1)/sizeof(int)); //输出2。
指向非数组元素指针的行为与指向长度为1的数组的第一个元素的指针行为相同,数组元素类型与指针指向对象的类型相同。
int i; int *ptr = &i; ptr += 1;