C++容易被忽视的基础知识

作为一个java开发者,由于实际需要最近开始接触C++,这是最近接触C++碰到的一些难点和易错点,整理如下,给自己和其他C++的初学者····

通过本文,你可以学到:

1)浮点值的上溢和下溢

2)浮点数舍入误差

3)使用%d显示float值不会把float转化为近似的int值,而是显示垃圾值

4)scanf的读取习惯

5)strlen()函数和sizeof()区别

6)负数转化为unsigned和大于255的值转化为字符相当于取模

7)prinf读取stack里的数字是按%读取的


1.浮点值的上溢和下溢:

如:假设系统最大的float的值为3.4E38,并进行如下操作:

float toobig =3.4E38*100.0f;
print("%e\n",toobig);
这会发生上溢(overflow),pronf()函数显示的值是inf或infinity(或者这个含义的其他名称)
当除以一个很小的数时,如0.1233E-10除以10,结果将失去一位精度,变为0.0123E-10,这种现象称为下溢(underflow).

另外如用asin()返回反正弦值,但输入参数却大于1时返回NaN(Not-a-Number)


2.浮点数舍入误差

将一个数加上1再减去原数,结果为1.如果用浮点计算,可能会有其他结果,如:

#include <stdio.h>
int main(void)
{
	float a, b;
	b = 2.0e20 + 1.0;
	a = b - 2.0e20;
	printf("%f\n", a);
	return 0;
}
结果:

技术分享

造成这种结果的原因是:计算机缺少足够的进行正确运算所需的十进制位数。2.0e20为2后面加20个0.若加1,变化的其实是第21位,若需要正确计算,至少需要存储21位数字,但是float只要6.7位有效数字。以上错误结果会随着平台不同而表现不同。


3.使用%d显示float值不会把float转化为近似的int值,而是显示垃圾值。


4.scanf的读取习惯

/* praise1.c -- uses an assortment of strings */
#include <stdio.h>
#define PRAISE "What a super marvelous name!"
int main(void)
{
    char name[40];

    printf("What's your name?\n");
    scanf("%s", name);
    printf("Hello, %s. %s\n", name, PRAISE);
  
    return 0;
}
结果:

What‘s your name?

Tony KK

Hello,Tony.What a super marvelous name!

造成这种原因是:使用%s的scanf()只会读入第一个单词而不会把整个语句作为字符串读入。C可以使用其他读取函数(如gets())来处理一般的字符串。


5.strlen()函数和sizeof():

sizeof运算符,以字节为单位给出数据的大小,strlen()函数以字符串为单位给出字符串的长度。

#include <stdio.h>
#include <string.h>      /* provides strlen() prototype */
#define PRAISE "What a super marvelous name!"
int main(void)
{
	char name[40];

	printf("What's your name?\n");
	scanf_s("%s", name);
	printf("Hello, %s. %s\n", name, PRAISE);
	printf("Your name of %d letters occupies %d memory cells.\n",
		strlen(name), sizeof name);
	printf("The phrase of praise has %d letters ",
		strlen(PRAISE));
	printf("and occupies %d memory cells.\n", sizeof PRAISE);

	return 0;
}
交互:

What‘s your name?

Morgan Buttercup

Hello,Margan.What a super marvelous name!

Your name of 6 letters occupies 40 memory cells.

The phrase of praise has 28 letters and occupies 29 memory cells.

解释:

对于name数组,sizeof显示的是数组长度,strlen()显示的是已经用了的数组内长度。对于字符串PRAISE,sizeof比strlen()多了1,因为它把结束的\0也计算进来了。


6.负数转化为unsigned和大于255的值转化为字符相当于取模

#include <stdio.h>
#define PAGES 336
#define WORDS 65618
int main(void)
{
    short num = PAGES;
    short mnum = -PAGES;

    printf("num as short and unsigned short:  %hd %hu\n", num,
            num);
    printf("-num as short and unsigned short: %hd %hu\n", mnum,
            mnum);
    printf("num as int and char: %d %c\n", num, num);
    printf("WORDS as int, short, and char: %d %hd %c\n",
            WORDS, WORDS, WORDS);
   return 0;
}
输出:

技术分享

首先,-336并没有像我们期待的转化为336,而是变成65200.原因是short int中 0到32767 表示他们本身,42768到65535表示负数,65535代表-1,65534代表-2,依此类推,65200表示-336。所以不要期待%u能把数字和符号分开。

第二行显示如果你想把一个大于255的值转化为字符,相当于取模:

2进制的336在内存中为0000000101010000,可是字符只能转化小于等于255的数字,否则就只是截取一段转化,此时值就变了。当我们试着打印一个比short int大的数字时也是这样。


7.prinf读取stack里的数字是按%读取的

#include <stdio.h>
int main(void)
{
    float n1 = 3.0;
    double n2 = 3.0;
    long n3 = 2000000000;
    long n4 = 1234567890;

    printf("%.1e %.1e %.1e %.1e\n", n1, n2, n3, n4);
    printf("%ld %ld\n", n3, n4);
    printf("%ld %ld %ld %ld\n", n1, n2, n3, n4);
   
    return 0;
}
结果:

技术分享

计算机根据变量的类型而非转换说明符把这些值放入值栈中。所以,n1在堆栈中占用8个字节(float变为double),同理,n2占8个字节,n3,n4分别占用4个字节。然后控制转移到prinf()函数,prinf()是按照转换说明符去读取的。%ld读取4个字节,也就是n1前半部分,下一个%ld再读取4个字节,n1的后半部分,因此最后结果都错。





郑重声明:本站内容如果来自互联网及其他传播媒体,其版权均属原媒体及文章作者所有。转载目的在于传递更多信息及用于网络分享,并不代表本站赞同其观点和对其真实性负责,也不构成任何其他建议。