C++基础问题总结

C++基础所遇问题总结

#变量初始化
这个可谓是我在整个过程中犯的最多的错误了,所以将其写到了第一条,声明一个变量以后如若要使用其参与运算一定要对其进行初始化
(尤其是for循环语句的表达式内,如for(int i=0;i<20;i++))。
否则会出现错误:使用了未初始化的变量;

#关于i++与++i
i++:先赋值后加;(不可做左值)
++i:先加后赋值;(可做左值)
这样说起来可能比较抽象,可以简单的理解为,i++是先把手中的事做了,自己再+1,而++i是先自己+1之后,再去完成该做的事。举个简单的例子:

a=30; b=a++;c=++a; 经过运算后a,b,c的值各为多少?

根据我们刚才说的,首先a=30;往下走,b=a++,此时a先做手中的事(赋值给b),也就是此时a还为30,并未自增,故b的值也就是30,此时赋值完成后,a就变成了31(a手中的事做完了,应该自增了),再往下,遇到c=++a,那么a先自增为32,在做手中的事(赋值给c),即c为32,所以最终的结果为a=32,b=30,c=30;

#关于continue和break
首先:continue只能在循环体中使用,不可出现在其他地方;
其次,continue是结束当前循环,然后返回到表达式再判断是否继续下一次循环;而break是直接跳出循环(一般case结构中每个判别式都需使用break,否则会将每个case的结果都输出);

#关于C++中的除法运算
除法运算的结果根据除数与被除数决定 如果其中有一个为 浮点数 那么结果就为浮点数 如果都是整数 那么就是整除(丢失小数部分只保留整数部分)。
如:整数/整数 36/10=3 而不是3.6
而 36.0/10=3.6(虽然10是整型,但是因为被除数36.0是浮点型,int型会自动转为浮点型) 所以当你要的结果是浮点数时,一定不要忘记将被除数置为浮点数。

#关于case选项
表达式必须附有常量值,如果选择结构里与选项进行比较的是int型,则直接写case 1等,如若是字符型或其他类型,则需要加单引号,比如 case’A’;

#关于逻辑判别式的表达
一个判别式里的结果是bool类型,也就意味着结果只能是true(1)或者false(0),所以当我们要判断一个数是否在一个范围内这种类型的问题时(如本次考试的分数是否在0到60之间),不能简单地臆想为if(0<score<60),因为程序在判别括号内的内容时是从左往右,也就是只要你的score大于0,那么括号内的内容始终为真(true),也就不会再判断是否小于60,也就是<60这个式子未生效,正确的表达应该是if(0<score&&score<60);

#关于随函数rand()
随机函数rand()(在头文件(cstdlib)中)所取到并不是一个真正的随机数(伪随机数),这是因为函数rand需要一个“种子作为初始值”,种子不同,产生的伪随机数也就不同
如果不设置种子,那么rand总是默认为种子为1,连续调用rand你会发现每 次运行这个程序产生的序列是相同的

如何设置种子:调用函数srand(unsigned int seed)其中seed就是种子,可通过输入流让用户给予.
如果不想每次都去设置种子,那么可以调用时间函数time(包含在头文件<time.h>中)返回当前时间值作为种子,如srand(time(0));那么这样就不需要手动设置种子了。

#关于递归
个人认为这个问题理解起来有点困难,首先递归的含义就是不断地调用自身,当递归的表达式只有一个的时候还好说,先来看个代码段:

unsigned int fun1(unsigned int n) {
	int r;
	if(n==0){
		r = 1;
	}
	else {
		r = fun1(n - 1) * n; //等于是个循环,只要fun1()括号内不为0,就会一直调用,调用直到n为0时。
		                    //此时便能算出r的值(为1),再返回到上一层调用的入口(此时的n也返回到上一层调用时的值),逐层出栈,直到返回到调用到的第一个数(这个过程也就是算从从0到那个数的阶乘).
	}
	return r;
}

这个程序段实现的功能是n的阶乘,终止递归的条件是n==0;那么如何利用递归实现阶乘呢,其实n!=n*(n-1)!,而(n-1)!=(n-1)*(n-2)!,如此反复,直到 n 递减到 0,那么就知道 0!=1,此时递归结束,我们已知 0!=1,那么就以此为起点逐次返回,便可得到1!(1!=1 * 0!=1),以此类推便可算出n!;
再回到程序,比如传入的参数值n=3,那么先就会进到if判断,此时n=3 ,进入到else,else里 fun1 函数调用自身,此时将(n-1)传入fun1(准确的说是fun1里的fun1),那么再进到if,此时n=2,再进入else里,再次调用fun1,此时将n=1传入fun1(也就是fun1的fun1的fun1),再进到函数体,此时n=1,进入到if,仍不成立,再次调用fun1,将此时n=0传入到fun1(也就是fun1的fun1的fun1的fun1),此时n=0, 进到if,满足,给r赋值1,返回r,也就是r现在等于1,此时程序开始回退,回退到上一层的入口(fun1的fun1的fun1),上面说到,当程序位于此节点的时候,此时的n=1,而fun(n-1)已经完成了最深层的递归,也就是0!=1,所以在 fun1的fun1的fun1 这个节点,*r=0!1=1,再返回上一个节点,也就是 fun1里的fun1, 此时的n=2,所以r=1 * 2=2,再返回上一级,也就是第一次调用fun1,此时n=3,r=3 * 2 * 1=6,也就返回到了程序的最初点,返回完了,那么也就算出来最终r=6。

但是当程序有两个或多个表达式的时候,这个过程就变得更复杂了起来,我们来看最经典的汉诺塔问题:
在这里插入图片描述
这个是实现汉诺塔问题的代码块:

#include<iostream>
using namespace std;
void move(char src, char dest) {
	cout << src << "--->" << dest<<endl;
}

void hanoi(int n,char src,char medium,char dest) {
	if (n == 1) {
		move(src, dest);
	}
	else {
		hanoi(n - 1, src, dest, medium);
		move(src, dest);
		hanoi(n - 1, medium, src, dest);
	}
}

void main() {
	int number;
	cout << "请输入要移动的盘子数" << endl;
	cin >> number;
	hanoi(number, 'A', 'B', 'C');
}

在这里面一个函数就调用了自身两次,这个过程就相对要复杂一点,需要注意的是,程序会先彻底深入递归完一次调用后再进行下一次递归调用(而一次深入递归又面临两次子递归,子递归又有子子递归),希望读者有时间能通过调试仔细观察并理解这个过程,记录下参数的变化情况并查看其是否与自己所想一致,这里就不再一 一赘述;

#关于函数的参数传递

参数传递分为值传递和引用传递,值传递的形参不会影响实参,而引用传递的形参却会影响实参(引用传递的形参可以看作是另一个变量的别名),引用传递的形参需要在前面加&,比如:int &a。
那么什么叫做形参不影响实参呢?我们来看下面的代码块:

#include<iostream>
using namespace std;
void swap(int a,int b){
int t=a;
a=b;
b=t;
}
int main(){
int x=5,y=10;
cout<<"x="<<x<<"  y="<<y<<endl;
swap(x,y)
cout<<"x="<<x<<"  y="<<y<<endl;
return 0;
}

其中不难看出swap的功能是一个交换变量值的函数,而最终得到的结果却是:

运行结果:
x=5  y=10
x=5  y=10

那么这是为什么呢?答案就是因为上面所说的,值传递的形参不影响实参,形参a,b之间的值交换并不干涉实际参数x,y的值,所以经过运算后x,y的值仍不改变;那么如果要实现x,y的交换,只需在swap函数中的形参前加上&即可,也就是void swap(int &a,int &b)即可;

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值