从 for while 开始的C语言学习(复习)之路PART 1 基础篇
PART 2 近期出解。/笑哭
总体来说 for 循环 和 while 循环 都是循环,可是就是些循环 却被我玩坏了。[玩坏过的勿喷]
第一个: for + scanf :
例如:
for(int i;scanf("%d",&i),i;){ }
int i;
while(scanf("%d",&i),i){ }
while(scanf("%d",&i)!=EOF){ }
while(~scanf("%d",&i)) { }
一般会在各种书里会见到他们的影子。这里只是做出一点直观的解释。
1.它们都有一个共同的先决条件 -- 变量必须要先定义,然后才能使用。
for(int i;scanf("%d",&i),i;){ }
就像这个样例的for循环 在第一个;之前定义了一个变量,然后输入这个变量,判断这个变量是不是等于 0 ,如果等于 0 就会退出。 而有些新手可能就会写成下面这样,然后问为什么输入 0 后 循环不会退出??
for(int i;scanf("%d",&i);){ }
这就要讨论一下 scanf 它的返回值了 scanf到底是返回什么的 我们 可以使用 一个整型的变量来接收它看看。
int main(){
int i,j;
int c =scanf("%d%d",&i,&j);
cout<<c;
return 0;
}
当发生正常输入,那么输出结果是 2 。为什么呢?那是因为 scanf返回的是 你输入的元素个数 因为 scanf里面有两个%d 【什么,和你想的不一样,你是不是想因为有两个变量在scanf里 所以是 2??】那好,我们再做一个实验
int main(){
int i,j;
//int c =scanf("%d%d",&i,&j);
int c =scanf("%d",&i,&j);
当发生正常输入的时候 输出 1. 然后我们接着做下面这个实验
int main(){
int i,j;
//int c =scanf("%d%d",&i,&j);
int c =scanf("%d%d%d",&i,&j);
cout<<c;
return 0;
}
这回正常输入后 c 的值 是 3. 所以真正控制 scanf返回值的 我觉得应该是里边的%d .
这就会让人想到一个符号 EOF 它的值是-1 它的用处是判断文件是不是输入结束。一般都是这两种写法
scanf("%d",&i)!=EOF
~scanf("%d",&i);
这两种写法,那么想玩的同学可能要问 这个要怎么才能使用啊!我好想让 scanf 返回值是-1啊! 在这里,因为笔者使用的是windows 系统 所以 仅仅介绍一下 windows 的操作。
在运行程序scanf等待用户输入的时候 按 ctrl + z 两个键,此时你会看见 程序框显示 ^z 这时按回车就会发现 scanf的返回值是-1了。
[又跑题了] 回归正题 for( 1 ; 2 ; 3) 1 只在循环一开始执行 2 只在下一循环开始之前执行 3 每次循环结束以后执行。为了方便理解 我写了三个程序。
for(;int i=1;){
scanf("%d",&i);
cout<<" ";
break;
}
for(int i;;){
scanf("%d",&i);
cout<<i<<" ";
break;
}
// 以下编译错误
for(;;int i=0){
scanf("%d",&i);
cout<<i<<" ";break;
}
知道为什么会编译错误了吗?这就是我之前提到的 for( 1 ; 2 ; 3) 1 只在循环一开始执行 2 只在下一循环开始之前执行 3 每次循环结束以后执行。所以 定义在 使用的后面 所以编译出错。而第一段代码必须给i赋初值,否则同样编译不通过,原因应该是中间那个是判断循环是否继续执行的条件,所以只接受 0 和 非0 这种数据吧! (bool值也可以使用 %d 的方法进行输出,也会发现是 0 和 1.)
所以 我有一种奇怪的写法 来看一看
while(int i=1){
scanf("%d",&i);
}
这样的写法正是等同于 for(;int i;){} 这种写法。 所以 我们得出了一个什么样子的结论呢??
任意的while()可以使用for() 进行替换。那 while(scanf("%d",&i)!=EOF) 这个能不能进行替换呢?? 试一下就知道了
for(int i;scanf("%d",&i)!=EOF;){
;
}
或者是这样
for(int i;~scanf("%d",&i);){
;
}
我发现 这两条也是可以通过编译器进行编译的。所以可以暂说for 可以 替代 while .
那它们之间 到底是谁效率高呢???
网上都有讨论 据说 九十年代有过测试 证明过 while 的效率优于 for 不过 更多的,我们现在的计算机配置,似乎已经看不到差别了吧!!!
所以 那么冒泡排序的 while 的写法 是不是 可以试试看呢~~~ 我们要善于观察 for 和 while 的 差异性
这个是用 for 写的 冒泡排序
int main(){
int a[10]={1,3,2,5,4,7,6,9,8,0};
for(int i=1;i<10;i++)
for(int j=0;j<10-i;j++)
if(a[j]>a[j+1])
swap(a[j],a[j+1]);
for(int i=0;i<10;i++)
cout<<a[i]<<" ";
}
这个是用 while 写的 冒泡排序
int main(){
int a[10]={1,3,2,5,4,7,6,9,8,0};
int i=1,j;
while(i<10){
j = 0;
while(j<10-i){
if(a[j]>a[j+1])
swap(a[j],a[j+1]);
j++;
}
i++;
}
for(int i=0;i<10;i++)
cout<<a[i]<<" ";
}
就发现了for 和 while 的 残留问题 for 循环 可以轻松定义局部的变量 避免了 空间的浪费 而while 循环却会残余变量,那是不是就无解了呢?? 不是的 !! 还记得 { } 花括号的用法吗??我们把 排序所需要的代码用 花括号括起来
int main(){
int a[10]={1,3,2,5,4,7,6,9,8,0};
{ // 我是一个可爱的花括号
int i=1,j;
while(i<10){
j = 0;
while(j<10-i){
if(a[j]>a[j+1])
swap(a[j],a[j+1]);
j++;
}
i++;
}
} // 我也是一个可爱的花括号,你看见我了吗== 喂
// cout<<i<<endl; // 你们猜 这条语句 可以编译的过去吗?? 答案:是不能编译过去的。所以局部变量的概念掌握了吗??
for(int i=0;i<10;i++)
cout<<a[i]<<" ";
}
所以 并不是像我之前所说的那样 会有这种 多余的 东西存在。所以仁者见仁,智者见智,使用方略其实是非常宽广的嘛!!
根据我们的学习我们会发现 其实并不像老师所说 for 循环是有循环次数才使用,while是未知循环次数才使用的。
下面讲一下 循环 for 的指针和字符串的 使用方法。
再讲这个之前 我们讲一下 指针的 使用方法。
指针 分为 很多种 什么 int* char* string* (c++) 迭代器指针啊等等等。 第一节课的时候老师说 指针保存的是指向元素的地址。随着学习的逐渐深入 发现 并不是完全是这样的。为什么呢??我们举一个例子
char i = 'c';
int t = (int)i;
这种写法大家都是认为可以这样的吧!!因此无论怎么变 指针变量 可以理解是一种指向它本身基类型的类型,总之就是类型,是类型就可以用强制类型转换进行赋值。因此我们不难说明以下代码是正确的
int* i = (int*)1;
接着我们 还知道了 已知数组中间元素的地址 ,那么用它来减去首地址就会得出这个元素在数组的位置。这个就说明了指针可以进行加减运算 ,然后我们可以猜想它能不能进行乘除运算呢?好!上代码
int* i = (int*)1;
int* j = (int*)2;
cout<<i*j<<endl; // 我是错误的代码 你发现我了吗??
所以是不能进行乘除运算的!!!!
那么指针是 怎么进行加减运算的呢??它又拿什么进行运算的呢??例如: j - i
int* i = (int*)1;
int* j = (int*)2;
cout<<j - i<<endl;
答案是多少呢??反正不是 1 而是 0 !!!!为什么是 0 呢?因为 int 类型是 占用 4 个字节的空间 即 [1,4] 闭区间哦!!
所以两个相同类型的指针相减 减得是 所占用的那个空间 1 和 2 都是第一个空间 所以 1-1==0 正确!
我们又来看下下面这个程序
int main(){
int* p =NULL;
int k = (int)p;
cout<<k;
}
k的值是多少 ?答案是 0 。说明了什么 int* p =NULL 就等价于 int *p = (int *)0; 呀!!不信?
我们看看下面这个程序
int* p =NULL;
int* k = (int *)0;
if(p == k)
cout<<"我对了"; // 是输出的我 看见我了吗??喂!
else
cout<<"继续努力";
我们还知道 '\0'代表的是 0 那么我们可以演示一下这个程序: (逐渐进入今天的正题)
char* p =NULL;
if(p == '\0') // 或者 if(p == 0)
cout<<"我对了"; // 是我是我!!
else
cout<<"继续努力"; // ………………
现在 我们 看 正题 ,哎对了 正题是什么来着??
循环 for 的指针和字符串的 使用方法。(肯定我掌握的不如那些牛叉的大佬了,所以想要学更多的东西还得靠手 不是靠喷!)
我们都知道 字符串 是由若干个字符在和'\0'在一段连续的空间中组成的。间断的空间是无法构建字符串的。比如用链表来存。
我们来看下面一个代码:
char *s = "nihao ma";
现在问一下 一共有多少个有效字符 一共有8 个有效字符。
再问一下 一共有多少个字符 因为是直接赋值 所以我们有三种方法 知道这个结果:
1. 使用strlen函数的返回值+1 即 int i = sizeof(s); 返回值是 9、
2.一眼看出来,你不会不懂数数吧??/笑哭
3.因为这个是一个直接赋值的字符串 所以 可以直接调用 sizeof() 来查 ,所以 int i = sizeof(s); 返回值是 9
但是注意 sizeof 用于查看这个数组剩余的空间的 我们再扯一下 sizeof()这个函数:
char a[6];
cout<<sizeof(p);
那都不用想啊 肯定返回的是 6 啊??为什么??因为char 类型一个占用1 个空间 一共有 6 个 因此 1*6 == 6 就是这么来的。
那也就是可以拓展为
int a[6];
cout<<sizeof(p);
返回的是 4*6 == 24 答案正确
那 给定一个字符串 我们怎样遍历呢??
是这样??
for(int i=0,j=strlen(str);i<j;i++)
;
还是这样??
for(int i=0;str[i]!='\0';i++)
;
还是 一种特殊的方法
for(char* s=str;*s!='\0';s++)
;
推荐使用第三种 方法 只是推荐 ,那有人会问我怎么知道我正在处理的元素是第几个元素呢??其实这个非常好处理,只需要
当前指针 - 数组的首部指针 。无论是 malloc定义的 还是 int 直接定义的 都可以通过减法计算出当前处理的元素的位置。
即 s - str == 当前元素的个数。
下回再见。哈哈哈 别怪我下回放难的呀??基础不牢地动山摇./笑哭