这里记录的是看书学习中的一些以前不知道的小知识点
来自P61页
我们知道cout会对数字进行自动修饰,他会省掉为0的小数位,如果我们需要保留特定位数的话可以使用格式化方法,但是如果想要尽可能的原样输出,就需要setf方法,注意是setf 不是self
使用方法:
#include"iostream"
int main()
{
using namespace std;
cout.setf(ios_base::fixed,ios_base::floatfield);//在这里
double g = 1;
cout << g;
return 0 ;
}
如果不加那句的话,会输出1,加了会输出1.000000
之前我们在C中使用类似int(value)这种方式转换,但是在C++中有一个更好地方法是:static_cat< typename >(value),前者有过多的可能性而比较危险,后者更加严格一些
double g = 1;
g = g/3;
cout << static_cast<int>(g);
要注意的是,sizeof()获取的是对象的长度其单位是字节,strlen()获得的是字符串的长度,而不是数组本身的长度
cin就像scanf以空格,tab,\n作为输入的结束,所以我们需要使用比较高级的特性来帮助们获取字符串,我倾向于使用get()方法
使用get有许多好处,以前经常做到一些需要依靠换行来判断输入结束的情况,另外,在有第二个参数的情况下,get也可以用于判断我们到底是怎么结束的输出,是正常停止还是因为达到了参数设定的界限而停止了。
但是当我们需要读取空行的时候
例如使用一个get接受空行,但是他会设置一个失效位(failbit)这使得你的下一行(有内容的输入,如果仍然输入空行是没有效果的)无法正常读入,但是下下行却可以!
要注意的是,一串用引号引起来的多个字符本身就被视为头指针,cout在输出的时候会(与输出其他类型的指针不同)cout会从头指针输出到\0为止
所以像下面的代码
#include"iostream"
int main()
{
using namespace std;
char x[20] = "wozuiqiang",*y = x;
cout << x << endl << y << endl << *y << endl <<"ok le";
return 0;
}
输出是这样的
wozuiqiang
wozuiqiang
w
ok le
字符串指针在cout中会直接输出字符串,而*y则是输出了x[0]。意味着可以像使用字符串常量那样使用字符串指针,但是,如果要让指针指向常量字符串,则应该用const把它固定下来,不然会有各种风险,尤其是在输入的时候。
不要将信息读入未被初始化的指针,这可能导致无法控制的错误,因为不知道数据被存在了哪里,造成了什么影响。
另外,在给新分配的空间赋值的时候,不可以使用=,这样会使得我们失去访问新内存的机会,应该使用strncpy(),这个函数可以指定第三个参数控制复制的最大长度避免越界。
7. 关于cin.clear()的重要性
#include"iostream"
#include"cstring"
#include"ctime"
int main()
{
using namespace std;
int golf[50];
for (int i = 0;i<5;i++)
while(!(cin >> golf[i]))
{
cout << "please input a number:";
//cin.clear();//请注意这里!!
while(cin.get()!='\n')
;
cout << "rewrite your input,please:";
}
for(int i = 0;i<5;i++)
cout << golf[i]<<" "<<sizeof(golf)/sizeof(golf[0])<<endl;
return 0;
}
请注意程序中的cin.clear(),这个程序是为了避免错误的输入设计的,cin.clear()在这里起着至关重要的作用,为什么呢?因为如果没有它的话,程序就会锁住输入不再就受输入。程序就被锁住废掉了。
虽然不匹配的输入被留在了输入队列中,而cin.get()从输入流中读取每个字符,但是在这里他却无法读取了,说明对于输入流,我们还需要更深的理解。
8. 关于指针
我们在定义指针的时候,总是要指定它的类型(如int * pointer)
但是其实这不代表着指针本身是int类型的,它的意思是指针指向的数据是int类型的,我的理解是,指针是两部分构成的,一部分是地址,一部分是一个长度值,这个长度值告诉编译器应该在这个地址的基础上取多长的范围以及怎么解释数据。正是因为这个长度,我们在让pointer+1的时候(如果他已经被正确地赋值了),不是加了一个字节长,而是加了一个int数据的长度。
9. 指针和const
我们常常想保护实参的数据,可以使用const来保护
如:
int a = 5;
const int *p = &a;
这样实际上是锁住了p,但是这里的微妙之处在于(指针总是很微妙),这个const只是锁住了p,使得不能通过*p修改他的值(他甚至没有锁住p,所以你可以直接改掉p的地址),但是我们仍然可以用a修改他的值。如果你想两者都锁住(一般在函数中这么做有意义),那么应当这么做:
const int a = 5,*p = &a;
这里需要注意的是,不能把const的地址赋给常规指针(如果这样可以的话,你就可以通过指针修改值,这就与原变量的const状态相矛盾了,编译器不会让你这么做)
但是这个原理又告诉我们一件事情,const锁住的不是数据,而是变量,他让编译器不允许这个变量进行修改,但是如果你通过别的方式找到了他的地址直接修改他的值的话,却是可以的
另外,你可以像这样锁住p指针不让他能够改变指向的地址
int * const p = &a;
根据上面的原理,这里实际上是锁住了p这个变量。
注意,当只有1层间接关系的时候可使用这种技术,但是当你创建的是指向指针的指针或者更多层的间接关系的时候,就要另当别论了。
10. 二维数组作为参数
对于二维数组作为参数的情况,要注意函数的原型比如
int data[3][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}}; int total = sum(data,3)
为什么使用行数作为参数而不是列数呢,因为函数的正确原型应当如下面这样
int sum(int (*array)[4],int size);
务必注意,括号不能少,不然的话会被看作是int类型的指针的数组!
这里的指针类型告诉我们,它指向由四个int组成的数组,这也就是传递行数而非列数的原因。
11. 传递结构的地址
因为C++把结构视作一个整体,所以传递的时候需要用&获取地址
声明形参的时候,应声明为指向结构的指针
//假设我们前面有个结构叫polar
int show(const polar * p);//避免在函数中修改结构
为什么要写下这个知识点呢,是因为当我们在函数中需要读取结构的时候,应该使用间接成员运算符(->)而不是成员运算符(.),以前在写栈和队列的习题的时候,曾经试图用.访问元素,但是发现不行,那时候没有深究(木得时间,而且定义的内容是参照别人的代码写的,其实当时只是略微看懂而已),现在发现,那时候在定义的时候就写了一句
typedef struct node *ptrNode;
所以下面使用的其实全部都是指针。所以才必须用->访问