用字符串字面值初始化
char s[4] = "str";//数组长度至少为4,因为字符串常量最后还有看不见的结束符
const char *p = "str";
1)"str"是一个字面值,它的类型将会先被转换为const char[4]。由于这里不存在指针,所以"str"就是顶层const,然后赋值操作,顶层const可以被忽略,所以左边为char s[4]
。
2)"str"是一个字面值,它的类型将会先被转换为const char[4]。但由于左边类型为字符指针,所以类型就会转换为const char *,这个指针指向第一个字符s,之所以有const是因为它是字面值。所以等号右边的值就是底层const,赋值操作时,底层const不能被忽略。所以这句不能是char *p = "str"
,因为将会丢失了底层const。
无符号的0减1
float sum(float a[], unsigned length) {
int i;
float result=0;
for (i = 0; i <= length - 1; i++) {
result += a[i];
}
return result;
}
此题来自CSAPP练习2.25。当参数length为0时,运行这段代码会抛出异常,内存错误。
原因:length为无符号数,展开为二进制是全为0,减去1后(实际效果为加上-1,-1明显是一个有符号数即补码数字,在补码数字中-1的二进制全为1,实际效果就是二进制全为0加上二进制全为1),二进制全为1,全为1的无符号数的值即为Umax(int为4字节,即32位二进制)即为
2
32
2^{32}
232-1即为4294967296-1。
所以for的条件判断实际为i<=4294967295u
。又因为作比较运算时,会将有符号的隐式转换为无符号的再比较,所以这里会把i先转换为一个无符号的副本来比较,虽然这里对程序没什么影响。
strlen返回类型为unsigned
int strlonger(char *s, char * t) {
cout << strlen(s) - strlen(t);
return strlen(s) - strlen(t) > 0;
}
int main(){
char p1[4] = "str";
char p2[5] = "str1";
cout << " "<<strlonger(p1,p2);
}
运行结果为:4294967295 1
此题来自CSAPP练习2.26。当数组s的长度小于t的长度时,本应该返回0即假,但却返回了1即真。
原因:strlen的函数原型为size_t strlen(const char *s)
,而在头文件stdio.h中数据类型size_t
是定义成unsigned int
的。所以返回的是-1的unsigned,即为4294967295。
形参实参与底层const
1)实参为底层const,形参无const。
int strlonger(char *s, char * t) {
return strlen(s) - strlen(t) > 0;
}
int main() {
const char *p1 = "str";
const char *p2 = "str1";
cout << " " << strlonger(p1, p2);
}
结论:这里相当于形参=实参
。本文第一章已经提到,赋值时底层const不能被忽略掉,就会丢失限定符。所以这里出错。
2)实参为无const,形参为底层const。
int strlonger(const char *s, const char * t) {
cout << strlen(s) - strlen(t);
return strlen(s) - strlen(t) > 0;
}
int main() {
char p1[4] = "str";
char p2[5] = "str1";
cout << " " << strlonger(p1, p2);
}
结论:不存在丢失限定符的情况,所以这里没有错误。p1传进去的是char *p1
。但是在strlonger
函数中,由于传进来的两个指针都是底层const,所以无法通过解引用这两个指针来对数组元素赋值了。
无符号数加法
unsigned short a = 65535;
unsigned short b = 1;
auto c= a + b;
cout << (unsigned short)(a + b);
c的类型为int,值为65536。输出为0。
原因:unsigned short
是两个字节,16位二进制。执行a+b后为int型的65536,展开二进制为00000000 00000001 00000000 00000000。按照16位截断之后,所以就是0了。
换个思路,65536取余
2
16
2^{16}
216,所以为0。因为在unsigned short
里最大二进制位的权值为
2
15
2^{15}
215,根本不可能有
2
16
2^{16}
216。
补码数:正溢出、负溢出
使用short int,字节为2,二进制为16位。w=16。
1)正溢出:当a+b之和大于了Tmax即
2
15
−
1
2^{15}-1
215−1即32768-1=32767。
short a = 32767;
short b = 1;
auto c= a + b;
cout << (short)(a + b);
a + b结果为32768大于了Tmax。截断结果即输出为-32768
。直观的计算方法是和减去
2
w
2^w
2w。因为正溢出是因为得到第17位的权值
2
16
2^{16}
216,而这里不应该有第17位的权值,所以就是和减去
2
16
2^{16}
216。
2)负溢出:当a+b之和小于了Tmin即
−
2
16
-2^{16}
−216即-32768。
short a = -32768;
short b = -1;
auto c= a + b;
cout << (short)(a + b);
a + b结果为-32769小于了Tmin。截断结果即输出为32767
。直观的计算方法是和加上
2
w
2^w
2w。因为负溢出是因为得到第17位的权值
−
2
16
-2^{16}
−216,而这里不应该有第17位的权值,所以就是和加上
2
16
2^{16}
216。-32769+65536=32767。
总结:
1)当且仅当a>0,b>0,但和<=0时,此时计算的和发现了正溢出。
2)当且仅当a<0,b<0,但和>=0时,此时计算的和发现了负溢出。