第二十讲 联合结构

       关于结构,我们说得比较多,但是想要说透却不容易,所以,今天我们还是继续说struct,struct之于C就好比class之于C++一样,所以他的重要性就不言而喻了,因为在实际的开发中,仅C/C++自身提供的这些数据类型根本无法满足,怎么办呢?我们就要得自己定义新类型,在c语言里面,想要自定义自己的类型,只能通过struct,不过还好,在C++里面我们不但可以通过struct来定义新类型,我们还有更好使的,那就是class,不过关于class留待C++的部分再说,今天我们要说的是联合类型的结构,怎么创建一个联合类型?今天我们要说的关键字不是struct,而是union,如下:
——————————-
union BookPhone{
        int   number;
        int   telphone;
        char   name[20];


};


——————————–
union的意义和struct的一样,他们都可以自定义一个类型,不过这个union类型和struct类型却有些不同,我们不妨来看看怎么使用他。
———————————-
#include <stdio.h>#include <stdlib.h>union BookPhone{
int number;

int telphone;

char name[20];

};


int main()
{
BookPhone A;

gets(A.name);

printf(“%sn”,A.name);

A.number = 1;

printf(“%s,%dn”,A.name,A.numer);


       A.telphone = 159111111;

printf(“%s,%d,%dn”,A.name,A.number,A.telphone);


       system(“PAUSE”);

return 0;

}
————————————-
我们输入名字为“Zhang San”,然后看我们看到接下来的输出如下面所示:
—————————————
Zhang San
乱码,1
乱码,1591111111,1591111111
————————————–
我们看到名字变成了乱码,接下来我们还看到number变成了电话号码,为什么会这样呢?这就是我们今天要说的重点,不过却不多。
首先,我们初始化name,name使用了10个字节,所以一开始能够正常显示。
接下来,我们又给int类型的number赋值1,使用了两个字节,值得注意的是这个时候刚才我们输入的“Zhang San”被擦出了,所以这个时候我们想在输出name就会变成乱码了,这时候我们用strlen去查看这时候,得到的长度是1。
最后,我们再把电话号码设为159111111,这时候再查看number和name,发现name的长度又变成了9,number却和telphone一样了。
通过上面这个例子,大家是不是看出了些端倪来了呢?union的意思是不是也明白了呢?一句话,联合联合就是联合使用内存,这是一种节省资源的方式,不过现在很多人都觉得这不重要,内存已经白菜价了,所以基本没什么人关心union类型了。
关于union今天就说到这里了,以后我们也可能很少会触碰到这个类型,不过了解一些总是好的,关于struct我们也就说到这里了,想要真正的熟悉还要多练多写,接下来我们该说资源管理了。

第二十一讲 资源管理

       为什么说C/C++是不安全的呢?因为他们都直接在内存上操作,稍有不慎就会漏洞百出,不论是数组越界还是内存泄漏都是一些极为棘手的问题,稍微短小的代码还好排错,要是几万行的代码怎么处理?所以资源管理不管是C还是C++都极为重要,要做一个合格的程序员,就要能够管理内存资源。说到内存管理,今天不得不说的两个函数就是上次我们说到的malloc()和free()了。
———————————–
double * ptr;


ptr = (double*)malloc(50*sizeof(double));


————————————-
这段代码请求 30 个 double 类型值的空间, 并且把 ptr 指向该空间的所在位置。
注意 ptr 是作为指向一个 double 类型值的指针声明的,而不是指向 50个 double 类型值的数据块的指针。
记住: 数组的名字是它第一个元素的地址, 因此,如果我们令 ptr指向一个内存块的第一个元素,就可以像使用数组名一样使用它。也就是说,可以使用表达式 ptr[0] 来访问内存块的第一个元素,ptr[1] 来访问第二个元素, 依此类推。正如前面所学,可以在指针符号中使用数组名,也可以在数组符号中使用指针。
说到这里,我们好像发现声明一个数组的方式似乎有两种了,哪两种呢?我们现在来理一理:
1. 声明一个数组,声明时用常量表达式指定数组维数,然后可以用数组名访问数组元素。
2. 声明一个指针, 调用 malloc(),然后使用该指针来访问数组元素。
用第二种方式,既是用malloc可以做一些用普通的数组声明做不到的事:创建一个动态数组 (dynamicarray),即一个在程序运行时才分配内存并可在程序运行时选择大小的数组. 例如, 假定 n 是一个整数变量. 在 C99 之前, 不能这样做:
double array[n];

  
如果 n 是一个变量, c99 之前不允许这样做,就算是现在的VS2012也不支持这么写,不过GCC支持。
然而, 即使在 c99 之前的编译器中,如果想要申请一个变量数组,我们可以这样做:
ptr = (double *) malloc (n * sizeof(double));

 
是不是发现这种方式很有用吗?动态创建的好处是对资源的利用,下面是一个更为普通的使用方法:
————————————-
T* ptr;
      ptr = (T*)malloc(sizeof(T)*n);


————————————-
T表示类型,在malloc面前的这个T可有可无,在C里面是这样的,不过在C++里就必须要有,所以为了和C++兼容,我们还是把他带上。
当然,在结束使用之后,记得使用free()将资源释放。free()参数就是要释放的内存,如果我们使用完ptr后,我们就用free将他释放掉:free(ptr)即可。
malloc的参数就是我们想要的内存大小,为了不至于内存泄漏,为了不至于数组越界,所以我们就得更好的利用这个函数去动态分配内存。
和malloc一样,calloc也是动态分配内存的函数,该函数和malloc的用法有些不一样,如果我们用calloc来修改上面的定义就得这样写了:
———————————-
double* ptr;
ptr = (double*)calloc(50,sizeof(double));
————————————-
其中端倪就留给大家去探索了,calloc同样可以用free来释放内存。
关于C/C++的五种储存类型就留待大家去探索吧,我就不打算说了。明天我们说文本文件的操作吧。

第十九讲 复合文字结构

    今天的内容不多,所以在讲今天的内容之前,先回答一个同学的提问,有个同学想要写个十进制数转换任何进制,我想大家如同细心的话应该记得我们在递归函数那一讲里面就用递归函数实现了一个十进制转换二进制的小程序,所以我们可以根据那个函数稍作修改便可得到这位同学想要的函数:
—————————–
void  to_anyscale (unsigned long n,int m)
{
int r;

r = n % m;

if (n >= m)
to_anyscale(n/m,m);

if(r<10)
putchar(‘0’+r);

else
putchar(r-10+’A’);

}
————————————
为了验证这个函数的可以性,我们写个main()函数来驱动这个函数试试:
—————————————
#include <stdio.h>#include <stdlib.h>
void  to_anyscale (unsigned long n,int m)
{
int r;

r = n % m;

if (n >= m)
to_anyscale(n/m,m);

if(r<10)
putchar(‘0’+r);

else
putchar(r-10+’A’);

}
int main()
{
unsigned long src;

int scale;

printf(“请输入需要转换的十进制数:n”);

while(scanf(“%d”,&

src))
{
printf(“请输入需要转换的进制数:n”);

scanf(“%d”,&

scale);

to_anyscale(src,scale);

printf(“若要继续,请输入另一个数,若要退出,请输入q或者Q:n”);

}

return 0;

}
—————————————
糟糕,不知道怎么回事,从春节开始微信公众平台似乎好像不能贴图了,也不知道是我浏览器出了问题还是怎么回事,改天用win7的笔记本试试。不过可以告诉大家,这个程序是可行的,大家可以尝试。
复合文字签发不仅适用于数组, 也适用于结构。可以使用复合文字创建一个被用来作为函数参数或被赋值给另一个结构的结构。语法是把类型名写在圆括号中,后跟一个用花括号括起来的初始化项目列表。例如, 下面是一个 struct bookphone 类型的复合文字:
—————————————–
     (struct bookphone)
{“Zhang San”, ”15911111111“ };

—————————————–
这是C99新添加的类型,所以有些编译器可能无法通过,就比如我现在所用的VS2012,他竟然也不支持这么写,但是Ubuntu的GCC却支持这种写法,只是我不太喜欢在Linux下编程,不过没关系,如果大家使用的是VS系列发现不能这么写的话,可以换种写法,在小括号和花括号间用”=“,如下面这种形式:
—————————————-
       struct BookPhone A={“Zhang San”,”15911111111″ };


—————————————-
 struct BookPhone{
char name[20];

char PhoneNumber[12];

};


—————————————
关于struct的声明,C99里面还添加了一个新功能,那就是伸缩型的数组类型。我们可以用伸缩型数组类型来重新定义我们上面的BookPhone结构。
——————————
 struct BookPhone{
char name[20];

char PhoneNumber[];

};


———————————
从上面的定义我们可以得出一个结论:
1. 伸缩型数组成员必须是最后一个数组成员.
2. 结构中必须至少有一个其他成员.
3. 伸缩型数组就像普通数组一样被声明, 除了它的方括号内是空的,其他和数组没有两样。
对于伸缩型的数组的使用我们可以像上面一样用复合字符结构来使用他,但是如果我们像下面这样使用的话,会是什么情况呢?
—————————————
struct BookPhone{
char name[20];

char PhoneNumber[];

};

int main()
{
struct BookPhone A;

printf(“请输入名字:n”);

gets(A.name);

printf(“请输入电话号码:n”);

gets(A.PhoneNumber);

printf(“%s : %sn”,A.name,A.PhoneNumber);

system(“PAUSE”);

return 0;

}
———————————–
因为我们用了一块来历不明的内存,所以会导致内存泄漏,那么怎么才能安全使用他呢?
要想安全使用这种伸缩型的结构,在声明对象的时候就要对他进去内存分配?怎么分配?当然用malloc了。
————————————–
      struct BookPhone* A = (BookPhone*)malloc(sizeof(BookPhone)+sizeof(char)*12);


—————————————–
malloc是C/C++标准库函数,不过也只是在C里面使,C++的话有更好用的new和delete操作符,不过由于我们现在还没说C++的特性,所以我们这里还是用malloc,malloc和new一样都是动态申请内存的,但是malloc是函数,他要配合free一起用,如果用malloc申请了一块内存,用完之后记得用free释放这块内存,否则就会出现内存泄漏。
从上面的例子,我们应该看得出malloc的使用方法,他需要一个size数据来做参数,这个参数就是需要申请的内存大小,malloc前面括号的类型对于C来说可有可无,但是为了和C++兼容,所以我们这里添加了,因为对于c++来说,这里是必须,否则他不知道会有不明确定义。
当然,在分配好这块内存后,在return之前记得将这块内存释放掉:
free(A);

说到这里,结构也算是说得差不多了吧,不过可能有人会问,那么结构里面可以使用我们自定义的类型吗(结构),当然是可以的,那么可以使用成员函数吗?我的回答是可以又不可以,可以是因为在C++里的结构确实是可以,但是在C里面却不可以,这就是C/C++的区别,那么C里面的结构和C++里面的class有什么区别呢?从设计初衷来说没有多少区别,都是让程序员自定义类型,但是c++的class是面向对象抽象设计的,派生和继承,代码的重用,使得开发过程中减少大量的工作量。当然这些以后再说,关于结构嵌套结构我们会在接下来我们实现一个简单链表中得到实现。
明天的内容:联合类型

第十七章 关于字符串的拷贝

       今天除夕,是2013最后的一天的,也是我们公众课在今年的最后一讲,春节期间不再推送,过完春节后继续推送。所以在开讲今天内容之前,我先祝大家新年快乐,马上发财。
说到这马上发财啊,今天做了一件很糗的事,早上在QQ里面有个朋友发了一条祝福信息,我就想今天大过年的,也学学人家短信拜下年,于是就直接把那段信息给复制过来了,然后找了百十来人群发出去,后来收到大家的反馈:不要这么假吧,一点诚意都没,名字也不改的就拿出来发了,我一看不对劲连忙回去看短信,有那么三秒钟真的是石化了,差点没憋出一口鲜血来,不过最后全都化成两个——尼玛。
好了,该扯的也扯了,下面我们进入今天的正题,今天因为是除夕,所以不打算说新内容,昨天因为时间关系匆匆把代码敲上也没有验证是否能够通过,想来有些草率了,今天把我们昨天留下的代码拷贝验证了下,还真是草率了,编译没问题,但是debug运行时报错了,看了下,像是操作了一块无效内存,大家还记得昨天的代码?哦,不要回复16回去看了,因为我今天已经修改好了,下面我就把我们昨天还没修改过的代码再贴出来吧,也顺便加深大家的印象:
—————————
char* copy(char* dest, const char* src)
{
if((dest = NULL) &

&

(src = NULL))
{
printf(“无效参数。”);

return NULL;

}
char* get = dest;

memcopy(dest,src,strlen(src)+1);

return get;


}
 

char* memcopy(char* dest,const char* src,unsigned
int len)
{
 if((dest =NULL) &

&

(src = NULL))
        {
printf(“无效参数。”);

return NULL;

}
char* get = dest;

if((get+len) <= src || get >(src+len))
{
while(len–)
{
*get = *src;

get++;

src++;

}
}
else
{
get = get + len -1;

src = src + len – 1;

while(len–)
{
*get = *src;

get–;

src–;

}
}

return dest;

}
————————————————
这是我们昨天的程序,因为昨天没时间验证,今天验证下来运行时崩溃,大家现在应该看出问题出现在那里了吧,就是用黑体表示的那两句里面,说到这里,不得不说,编程真的不要细心啊,这个错误我已经连犯两次,有一次是在做一个上传下载文件的客户端和服务器上,在connect套接字的时候就是这样错将“=”当“==”使,所以导致一直无法连接服务器。那么为什么我们上面的这个会报错呢?因为我们重新初始化了指针,所以导致运行时出现无效内存。
还有,这里的逻辑判断也不对,我们的原意是要检查参数的有效性,如果有一个为空就直接返回退出,这样的话用&

&

就极不适合了,为什么会出现这个判断符呢?因为当时没考虑,就直接从下面把他复制上去的。虽然我们一般不会传无效数组指针进去,不会碰到无效退出的情况,但是什么时候自己传了个无效的指针进去不是糟糕了吗?所以上面的黑体语句应该修改为如下:
  if((dest == NULL) || (src == NULL))
再调试的话发现完全没问题了,就算有重叠内存的拷贝也不会崩溃了。下面我们来看看内存拷贝是怎么实现的。
首先,我们先要判断有没有重叠内存的可能,这个该怎么判断呢?还记得我吗上一讲的那个恒等式吗?
  array[i] == *(ptr+i);


     &

array[i] == ptr + i;


通过这两个恒等式,我们可以轻易的判断出是否有内存重叠的情况:
dest是要的目标地址,dest就是我们所需要用到的一块内存的首地址,知道首地址,我们还知道那个len(这块内存的长度),所以我们可以轻松的得到这块内存的范围:[dest,dest+len],只要我们这块内存的最小值小于等于在我们源地址的首地址,或者在源地址的最大范围外就可以肯定他们没有重叠部分,就算有重叠部分,这样也不会印象到我们一般的从低到高的拷贝,如下:
if(get<= src || get >(src+len))
当满足该条件,我们就不用担心内存重叠的问题,直接从低字节向高字节依次拷贝即可,直到将源地址里面的信息拷贝给目标内存。
——————————-
    while(len–)
     { 
             *get =  *src;


             get++;


             src++;


      
}

———————————-
这就是整个拷贝过程,当然,如果大家喜欢用数组操作的话,可以如下循环来完成复制过来:
——————————–
for(int i=0;

i<len;

i++)
    {
              get[i] = src[i];


       }
——————————-
注意一点,如果使用数组模式,不可以使用++或–,如果使用指针的话,可以这样操作。
如果不满足上面的if条件,那么就说有内存重叠的地方,这时我们就要考虑另一种拷贝方法了,我们不能从低位向高位顺序拷贝,我们要从高位向低位进行拷贝,为什么呢?就用昨天我们举的例子吧:
  copy(array1+1,array1);
从上面的原型我们可以看出,这是要把array1里面的信息拷贝到array1+1里面去,因为他们共用了一块内存:
     ——————
         ——————
像上面的这两条线一样,array1的内存块是上面那一段,array1+1是下面这一段,第一个自己可以顺利复制进array1+1里面来,就简单点说,array1这块内存里面储存的是abc,那么a顺利复制到array1+1的首地址里,那么array1+1的首地址却是array1的第二个值储存地址,也就是b的地址,这一来,b便变成了a,于是array1的最终结果是aaaarray1+1的结果也是aaa,现在明白为什么要从高位到低位的拷贝了吧。
关于这个字符串的拷贝函数就说到这里了,他到底算不算是系统string库里面的strcpy()的伪码我不知道,有空的同学可以多加验证,尤其是在效率方面,因为现在我们就差效率没有验证了,其他方面和strcpy()没有两样,甚至内存重叠拷贝的情况竟然比strcpy()的要好,我不知道这是好事还是坏事,我想微软不会没有考虑我所考虑的这些,至于效率,我也懒得去验证,留给大家去探索吧。最后祝大家合家欢乐,马上发财,马到成功。

第十五讲 数组

        在第十四讲里面的有个转换二进制的程序有点失误,因为平时敲惯回车换行(rn),所以不自觉的在r前面添加了个反斜杠,相信大家都看出来了,不过现在已经更改过来,有想要重新回顾的可以直接回复14查看,不过很抱歉,如果有错字少子我可能还是没有更改过来,作为中国人,大家都懂的,多字少字错字甚至是错乱的顺序一般都不会影响阅读和理解的,好了,今天我们来说说C/C++里面的数组。
说到数组,相信大家都不会陌生,从一开始我们的hello world开始,数组对我们来说已经是屡见不鲜了。
数组在编程中有着不可或缺的重要性,在C/C++里面他的重要性不比指针低,那么,这么重要的东西,数组到底是什么东西呢?
数组,就是线性存储的一系列相同类型的值,例如:
int  m_array1[20];


       float m_array2[20];


       char m_array3[20];


       ……
在 m_array1[20] 里面的值都是int类型,而 m_array2[20] 的都是float,如果有不是float呢?直接强制转换成float,同样, m_array3[20]里面都是char类型,而且这些数据在内存里面是储存在一系列连续的内存块里面。
大家是不是想问,既然数组储存在一块内存里面那么怎么表示他们的首地址呢?哦,这似乎又和指针关系扯在一起了,所以对于这个问题,明天再来给大家解答。
数组的初始化通常是在花括号中完成的,例如:
   1. int  m_array1[20] ={0 };


       2.float m_array2[20] ={1.2,1.5,16.2…… };


还有一种初始化,如下:
  3.
int m_array1[20] ={1,2,3 };

关于上面我们说的三种初始化方法,第一种是用0初始化所有的,第二种是逐个初始化,第三种明确初始化一部分余下的全部初始化为0.
那么为什么要说初始化?因为数组和指针不同,数组在声明后就一定要初始化,而对于指针却不然,同样对于数组,一旦声明后,他的大家也就固定了,对于指针……又想说却不然了,但真的是却不然,所以对于数组的初始化很重要。
还有一种对于数组的声明方式,那就是没有明确指明这个数组到底有多大,他的大小有初始化他的内容来决定,想必大家一定还记得我们下面的这个声明:
   char name[] = “Hello World”;


大家想一下,我们name的长度到底是多少?可能有人会认为是10,因为这确实是10个字符,可能有人会说中间还有空格,所以应该是11,当然如果对C/C++有一定了解的同学应该只是,这是12,因为在后面还有’�’。这就是C语言的字符串风格,而且就算C++里面有一个更好用的string,但是在开发程序中,C风格的字符串还是不可或缺的,很多数据还都需要用数组来传递。
那么怎么取得这种不明定义的数组的长度呢?我们这里有一个操作符:sizeof(),这是一个操作符,不是函数,所以希望大家不要把他和strlen()搞混了,strlen()才是真正的函数,他的功能同样是计算数组的长度,但有一点值得注意,数组作为sizeof的参数不退化,但作为strlen()的参数后退化为指针,还有对于明确声明长度的数组分别用sizeof和strlen来计算长度会有不同的结果:
sizeof(name) = 12;


        strlen(name) = 11;


        sizeof(m_array3) = 20;


        strlen(m_array3) = 3;


有兴趣的同学可以网上搜一下关于sizeof和strlen的区别,关于strlen我们留待cstring的时候再来说说,到时候也会把他和sizeof的区别好好理一下。
一个初始化说了这么多,感觉好浪费的说,不过如果大家真的弄清楚了还是值得的,下面我们来说说关于访问数组中的元素的问题。
char name[] = “Hello World”;


当我们声明定义这个数组之后,编译便会默认我们的name长度是12,也就是说我们这个定义相当于:
char name[12] = “Hello World”;


所以我们可以通过下标来对name里面的元素进行访问,但是有一点得注意,关于访问元素的下标是从0开始的,所以name[0]表示第一个元素,也就是H,以此类推,如果大家想要通过打印方法来把这些元素分别一个个显示出来的话,到最后一个元素也就是name[11]时发现什么也没打印,不错,因为这个元素是C字符串数组的结尾符,是’�’,所以什么都不打印。
那么我们是不是可以定义另一个数组,用已知的数组去初始化这个数组呢?像下面这样:
char  name1[] = name[] &

name1[] = name?
这当然不可以,是不是有人会问,如果这样都不可以,我们怎么把一个字符串复制给另一个字符串呢?哦,不好意思,也似乎也不是复制,按C/C++的逻辑,这算是赋值了,当然赋值也包含了复制的功能,不过不管是赋值还是复制,我们都可以实现。
下面我们就为两个字符%

第十三章 函数(2)

        昨天说到参数和返回值,但是只是开了个头,今天我们来继续看看函数的参数,还是从main()开始吧,这个函数我们从开始到现在都没有发现他有带参数,但是我们有些时候可能会碰到像下面这种类型:
int/
void main(int argc,char * argv[])

大家可能会觉得奇怪,这个真是我们接触的mian函数吗?一开始我也和大家一样,当然如果有人接触过UNIX或者Linux系统的话而且也曾在上面用过C/C++编程过的话,那对这个函数就不陌生。在windows系统上一般没人使用这个带参数的原型,所以我这个只使用windows的人第一次碰到这个原型后顿时大惑不解,于是去Google了下,这才知道这是UNIX余留下来的习惯,尽管如此,我们试着看下这两个参数到底实现些什么,我们编写像下面这样的程序:
—————————-
#include <stdio.h>
#include <stdlib.h>
int mian(int argc,char *argv[])
{
      for(int i=0;

i<argc;

i++)
            printf(“%sn”,argv[i]);


      system(“PAUSE”);


}
—————————
发现他的输出竟是下面的样子:
第十三章 函数(2)
看来在我们这个程序里面,argc的值只是1,argv[0]直向的字符串是我们该程序的路径,这是为什么呢?大家可以自己Google一下吧,毕竟现在我们是在windows下的可视化界面下编程,所以这些参数根本用不上,我也不懂,哈哈……
我们现在继续来看下面这个函数:
—————————-
long power(int n,int m)
{
       long s = 1;


       while(m–)
              s *= n;


       

return s;


}
——————————–
这个函数带有两个参数,n和m,返回一个long类型的值,其中这两个参数和返回值都是这个函数的接口,如果我们要调用这个函数,就必须给他传递两个参数进去,而且接收他返回的值。
不过这个函数有很大的局限性,他只能接受两个int类型的数作为参数,等以后我们学到C++的时候那时候我们用函数模板来重新定义这个函数,那时就完美了。
又扯远了,还是回到我们的函数上来,函数是实现特定功能的代码,就好比我们上面的函数是实现一个幂次方的,还记得我是怎么声明这个函数的吗?
—————————-
#include <stdio.h>
#include <stdlib.h>
long power(int n,int m);

//声明函数原型
int age();

//声明函数原型
int main()
{
       //实现代码段
}
long power(int n,int m)//定义函数
{
         //功能实现代码
}
int age()
{
       //功能实现代码
}
—————————–
想要声明一个函数,我们就应该在main()前面将他声明,事实上这就是给出了一个接口给我们程序调用,这个函数的具体实现还在下面的定义。我们在上面的程序里声明了两个函数,一个带参数一个不带参数,所以第一个参数的接口是两个参数和返回值,第二没参数的函数的接口就是他的返回值。

第十四讲 递归函数

       上两讲我们说到函数,不过说实在的,对于非专业讲师来说,这个主题真的没啥好讲的,所以我讲没劲,大家也看得没劲,不过还好,我们今天进入下一个话题,今天要说的是递归函数,递归函数可以归为函数一类,同时也归为算法一类,他也是数据结构的一类,虽然我们现在不讨论数据结构,但这里既然说到函数,就拿出来说一下吧,顺便大家也好对这个递归函数有所理解。
大家还记得我们第十一讲里面有一个关于计算幂次方的简单函数吗?我们当时是使用一个while循环来完成这个简单的算法,这个算法确实是最简单的,同时也是最好用的,等以后我们使用模板函数来模板化的时候我们还是会用这个算法,不过今天既然我们说到递归,我们是不是可以用递归函数来实现这个功能呢?当然可以,在说递归思想之前我们先来看我们对幂次方算法的重新实现,如果大家看完这个实现之后明白递归思想了的话我也就懒得多说了,哈哈,偷懒啊。
———————————-
我们先用循环来实现,顺便让大家回忆一下
int power(const
int n,int m)

{
     
int s=1;


      while(m–)
           s *= n;


      

return s;


}
下面我们用递归来实现
int power(const
int n,int m)

{
     
int s;


      if(m>0)
           s = n*power(n,m-1);


      else 
           s = 1;


      
return s;


}
———————————
两种实现方法,大家看出了端倪了吗?相信聪明的你们已经看出来了,用递归简直是多此一举啊,不错,在这个实现里面,用递归确实不是明知之举,但不可否认递归思想也是一种算法思想,我们来验证一下吧:
第十四讲 递归函数
再我们常用的范围内使用完全没问题,不过这样实现太过麻烦了,现在我们再来看看下面这个实现:
———————————-
void  to_binary (unsigned long n)  
{
int r;

r = n % 2;

if (n >= 2)
to_binary(n/2);

putchar(‘0’+r);

return;

}
—————————————
相信大家已经知道这个函数实现的功能了,这就是将一个十进制数转换为二进制数的实现,所以说到底,这是一个递归的反向计算的思想,很有意思,在要实现反向计算的时候,递归却又比循环好用得多,还是老规矩,我们来验证一下结果:
第十四讲 递归函数
关于递归的好和递归的坏都在这两个例子里得到证明,所以该什么时候使用递归就看大家的想法了,有人说,不管是C还是C++或者是JAVA又或者是C#他们不过都只是工具而已,数据结构才是衡量一个程序员功力深浅的标准,而算法又是一门仁者见仁智者见智的东西,但是在计算机面前似乎又有一个标准,相信大家之所以选择学习C/C++就是因为看上他的效率,所以算法之重要也就不言而喻了,今天我们所说的递归只是其中的一类,后面我们还会说一些基本的算法思想链表和队列,二叉树和容器。
还是回到递归函数上面来吧,现在我们来说说递归的基本原理:
递归,简单点说,就是每次循环都是调用自己,相信每位都对数列不陌生,在数列里面大家是不是经常有a[n] = a[n-1]XX等关系式,其实递归也就是解决这类问题的。
递归函数有六个要点:
第一,每一级的函数调用都有自己的变量(如果不理解,再回头去看看我们上面的两个例子)
       第二,每一次函数调用都会有一次返回。
       第三,递归函数中,位于递归调用前的语句和各级被调函数具有相同的执行顺序。
       第四,递归函数中,位于递归调用后的语句的执行顺序和各个被调用函数的顺序相反。
       第五,虽然每一级递归都有自己的变量,但是函数并不会得到复制。
       第六,递归函数中必须包含可以终止递归调用的语句。
对于第三和第四,如果还有不解的没关系,我们再来看看下面的例子。
———————————-

void test(int n)
{
printf (“第 %d : %dn”,n,n);

 
     if (n <4)
test(n + 1);

printf (“第 %d : %dn”,n,n);

 
}
————————————-
我们我们用个main()来驱动这个函数,看看输出的情况会是什么。
—————————–
int main()
{
      test(1);


      system(“PAUSE”);


     

return 0;


}
——————————
再我们打印结果之前,大家再回头去第三第四条吧,好对我们下面的输出不会觉得怪异:
第十四讲 递归函数
现在对递归是不是有些理解了呢?
斐波纳契列定义这是一个面试考试高频出现的考题,现在我们用递归来实现这个定义:第一个和第二个数字都是 1 ,而后续的每个数字是其两个数字之和。
———————————–
int Fibonacci (int n)
{
if (n >2)

return Fibonacci (n-1) + Fibloacct (n-2);

else

return 1;

}
————————————-
很简单,我就不截图了,这是一个将递归的优点发挥到极限的例子,最后,总结一下递归的优缺点然后就结束今天的内容吧:
优点在于为某些编程问题提供了最简单的解决方法,而缺点是一些递归算法会很快耗尽计算机的内存资源。同时,使用递归的程序难于阅读和维护。还是上面那句话,怎么使用就看大家怎么看了。

第十二讲 函数(1)

      关于昨天的问题,不知道大家是不是已经吃透了,不过就算是吃透了,我想大家一定会觉得那样做太过麻烦了,今天在说尼科彻斯定理之前,我们再来看看另一个比较简单的方法,昨天我们用的C语言来实现,今天我们用C++来实现吧,虽然说现在还没正式开讲C++,但是让那些还没接触过C++的同学可以对比一下两者的区别。
今天,我们不要那么麻烦,不用返回那些奇奇怪怪的东西了,我们直接使用STL里的vector容器来实现,但是今天我们不说vector,留在C++部分再讲。
————————————-
#include <iostream>#include <vector>using namespace std;

int main()
{
vector<int>age;

int n,m,a[10]={0 },s[10]={0 };

long sum1,sum2;

for(int i=0;

i<25;

i++)
{
sum1 = i*i*i;

sum2 = i*i*i*i;

if(sum1 >1000 &

&

sum1 <10000 &

&

sum2 >100000 &

&

sum2 <1000000)
{
age.push_back(i);

}
}
int size = age.size();

n = age[0];

m = age[size-1];

do
{
sum1 = n*n*n;

for(int i=3;

i>=0;

i–)
{
a[i] = sum1%10;

sum1 = sum1/10;

}
sum2 = n*n*n*n;

for(int i=9;

i>3;

i–)
{
a[i] = sum2%10;

sum2 = sum2/10;

}
for(int i=0;

i<=9;

i++)
{
s[a[i]]++;

}
for(int i=0;

i<=9;

i++)
{
if(s[i] == 1)
{
if(i == 9)
cout<<“这位同学的年龄是:”<<n<<endl;

}
else
break;

}
n++;

}while(n!=m);

cin.get();

return 0;

}
————————————-
今天的设计思路是将同时满足sum1和sum2的数值存放在age容器里面,然后分别取出第一个数和最后一个数来做do while循环,在do while循环和昨天实现的一样,所以没什么好说的,现在我们来看看输出和昨天的有没有区别:
第十二讲 函数(1)
结果和昨天的一样,大家是不是发觉C/C++是无所不能的啊,解决的方案还有很多,大家有兴趣的话可以自己去探索,现在回到我们今天的主题——函数。
说到函数,现在想必大家都比较熟悉了,但是为什么大家都熟悉的函数我们现在还要搬出来讲呢?因为在C/C++里,函数是完成特定任务的程序代码的自包含单元,所以,所以无论我们想要实现什么功能,都需要一个个的函数来实现。就好比昨天我们留下的尼科彻斯定理的证明就可以用一个函数来实现,下面我们先分析一下要实现这个问题的关键。
解决这个问题的关键,首先我们要确定这一连串的奇数的最大值的范围,我们声明一个int对象sum来储存一个数的立方,sum的一半n如果是奇数的话,那么 n+n+2的值就大于sum了,所以,我们可以得出一个结论,  这一连串中的奇数的最大值不会大于n,那么n是偶数的情况怎么办呢?我们应该+1还是-1?我们选择+1,那么另一个奇数就是n-1,n+n+1-1刚好等于sum,所以当n为偶数的时候,最大奇数的取值范围不会超过n+1,在确定范围后,接下来的是就好办了,我们直接从最大值开始用while来循环穷举:
———————————-
#include <stdio.h>#include <stdlib.h>int main()
{
int i, j, k ,a, n, m, sum,flag=1;

printf(“请输入一个数:n”);

scanf(“%d”, &

a);

sum = a*a*a;

n = sum / 2;

if (n % 2 == 0)
n = n + 1;

while (flag==1&

&

n >= 1)
{
m = 0;

k = 0;

while (1)
{
m += (n – 2 * k);

k++;

if (m == sum)
{
printf(“%d*%d*%d=%d=”, a, a, a, sum);

for (j = 0;

 j <k – 1;

j++)
printf(“%d+”, n – j * 2);

printf(“%dn”, n – (k – 1) *2);

flag=0;

break;

}
if (m >sum)
break;

}
n -= 2;

}
system(“PAUSE”);

return 0;

}
——————————————–
这个函数不难看懂,我们现在来看这个函数的输出:
第十二讲 函数(1)
大家可以多验证一下,那么现在我们来分析一下这个函数的结构:
#include <stdio.h>
 #include <stdlib.h>
这是我们每次写函数的时候都会在最开头添加的两句代码,大家想必都很熟悉了,但是我想对于初学者来说这个问题可能让大家觉得有些疑惑,这到底是干嘛呢?
这是函数的预处理,也就是包含相关的头文件,何为头文件?就是一个以“.h”结尾的文件,这个文件里面包含有相关的函数接口,我们要使用的标准IO都是在stdio.h这个文件里声明的,所以,我们只要包含了这个头文件就可以在接下来的代码里面使用里面的相关接口。所以如果我们需要包含什么相关文件,需要使用#include预处理命令。
除了#include命令之外,我们还会看到其他命令,如同下面这些:
#define PI 3.1415926
这是一个宏定义,就是把PI定义为3.14.5926,只要在函数开头添加了这个预处理命令,在接下来的代码中,凡是遇到PI他就表示3.1415926,所以,如果想要定义一个常量,可以使用#define 命令,记住这个命令的用法是后面只能用一个数字:
———————————
#define PI 3.1415926;//错误的方式
#define PI  3.1415929  3.1415927//错误
#define PI  2 * 3.1415926//正确的方式
#define PI  3.1415926 //正确
———————————–
当然,如果只是定义一个常量的话,我们以后会用const来定义,const的效率要比#define的高。
在函数开头使用#XXX的一般都是程序的预处理命令,大家可以多了解一些,这里就不多说了。
接下来是主线程的入口:main(),main()是控制台函数的入口,我们之所以能够看到那个黑乎乎的窗口,就是通过这个main()呼唤出来的,还记得在第一讲的时候我们说过吗?当函数执行到main()下面的左花括号({)的时候那个黑乎乎的窗口就出现了。
在SDK程序里,应用程序的窗口的入口函数是WinMain(),同时也是MFC程序的窗口入口函数,不过我们暂时不说这个函数,留待讲SDK程序的时候再说。
那么大家是不是还记得在第一讲里面我们在main()的前面使用的是void,而后来我们都用int,而细心的同学可能也注意到了,我们用void的程序没有return,而用int的都有一句
return 0
;的语句,这是为什么呢?
在c99以前,其实我们是可以不用在main()前面添加什么关键词的,因为那时候的默认情况就是void,有void修饰的函数是不需要返回的,所以我们那时候就没有使用return语句。
在标准里面,main可以定义为int/void,所以大家会看到这两种情况,但大家通常见到的都是int,带一个默认返回值,返回值一般是用来检验错误的,但是对于main来说几乎没这个必要,所以void也成了他的标准。
一个修饰词说了这么多,有点过分了,我们接着说下一点,定义一个函数,除了函数名和类型外,还需要参数和代码段的实现。
说道参数,还记得第十一讲里面我们自定义的power(int n,int m)函数吗?括号里面的n,和m便是这个函数的两个参数,但是我们又经常遇到一些没带参数的函数,没带参数的函数可以这么定义,fun(void),但是void是可以省略的,所以我们经常会看到这样的类型fun()。
每一个函数都有他的用处,那么我们怎么拿出来用呢?我们怎么把他引入主线程main()中来呢?这就关乎着函数的接口了。
说到函数的接口,大家应该不会默认,相信大家已经想到了,函数的接口就是参数和返回值。今天说得好像不少了,微信学习讲究的是轻松越快,每天花几分钟的时间就好即可,所以关于参数和返回值留待明天再说。

第十一讲 do while

      今天的内容主要是说昨天留给大家思考的问题,这个问题说难不难,就是有些麻烦,相信有同学已经得出答案了,不管如何,还是以前那句话,我们从头理一理吧。
从原题目入手,年龄的三次方是4位数,那么我们可以得出一个范围来,我们可以声明一个long类型变量来储存这个值
long  sum1;
sum1的取值范围我们可以明确的知道大于1000并且小于10000。
(sum1>1000 &

&

sum1 <10000)
接下来我们可以再从第二个条件知道,年龄的4次方是6位数,于是我们再声明一个long类型变量sum2来储存这个值,并且得到他的取值范围是在100000和1000000之间:
long sum2;
     (sum2>100000 &

&

sum2<1000000);


说到这里,顺便说一下,算是补漏吧,千万不要写出下面类型的判断语句出来,这是C/C++不认同的:
(100000<sum2<1000000)(错误的代码);


既然说到sum1和sum2,那么大家是不是想问,sum1和sum2的值从何而来?在C/C++里面的math.h库里有个power(n,m)函数,该函数返回n的m次方值,不过今天,我们不想直接使用这个库,我们自己写一个计算幂次方的函数来调用就好,反正是公众课,所以我想大家不会嫌代码多的是吧,下面我们就简单的先把幂次方的函数定义出来:
——————————–
long power(int n,int m)
{
long s=1;

while(m–)
{
s *= n;

}

return s;

}
——————————-
既然power已经定义好了,那么我们的sum1和sum2就顺理成章的计算出来了:
sum1 = power(i,3);


     sum2 = power(i,4);


i是一个int变量,用来遍历年龄的,上面的代码如果不是因为教学的原因,可以不用写那个幂次方函数,直接使用i*i*i和i*i*i*i即可。
但是大家是不是发现一个问题,同学满足sum1和sum2的值不少,而且我们又看不见,难道说我们还要先把这个值打印出来再来研究这问题吗?其实本来有种思想就很简单的,就是自己预先演算一边,把同时满sum1和sum2的值计算出来,然后来用do while循环计算出年龄,这种方法要自己一个数字一个数字的去计算,不过可以根据我们大概的判断一下范围来围绕计算也不会是太难的,但是这不是我们今天要说的重点,毕竟我也很懒不想去想这个范围到底在哪里。既然这样,那我们把同时满足sum1和sum2的值的个数和最大值分别用一个函数计算出来,然后把他们传回main()中就可以了,下面我们分别实现返回个数和最大的两个函数:
—————————————-
//返回同时满足sum1和sum2的年龄数
int age()
{
int age[100];

int count = 0;

long sum1,sum2;

for(int i=0;

i<100;

i++)
{
sum1 = power(i,3);

sum2 = power(i,4);

if(sum1 >1000 &

&

sum1 <10000 &

&

sum2 >100000 &

&

sum2 <1000000)
{
age[i] = i;

count++;

}
}

return count;

}
————————————-
————————————-
//返回满足sum1和sum2的最大值
int agemax()
{
int age[25]={0 },max = 0;

long sum1,sum2;

for(int i=0;

i<25;

i++)
{
sum1 = power(i,3);

sum2 = power(i,4);

if(sum1 >1000 &

&

sum1 <10000 &

&

sum2 >100000 &

&

sum2 <1000000)
{
age[i] = i;

}
if(max<age[i])
{
max = age[i];

}
}

return max;

}
————————————
两个函数差不多,如果我们直接使用打印输出的话只需要一个函数就好,但是我们现在是用他们的返回值来作为参数,所以我们把他们写成两个函数,分别返回不同的值。
我们现在知道最大值和个数,再加上这些数据都是连续的整数,所以我们通过最大值+1再减去个数便得到最小满足sum1和sum2的值。
这些都准备好了,现在我们可以回来设计我们的计算部分了,这里我们会使用do while来循环计算,wo while是我们今天的主角,所以现在来说说一下这个循环的用法。
———————————-
do
{
do something;
}while(条件);
———————————–
从上面这个声明中,想必大家都清楚了,do while是先执行一遍程序然后再来判断满足否,如果不满足直接退出,如果满足继续循环,所以在三个循环函数中,可以根据具体的情况选择不同的循环来循环。
大家是不是还意识一点,在三个循环函数中,只有do while循环在while后面加分号,记住,这是一定要加的。
循环就是循环,没有什么花样,下面我们就用这个循环来计算昨天留下的问题,不过再给出最终代码之前,还是有先说一些必要的要点。
我们虽然得到一个范围,但是我们还有一个条件没有满足,那就是sum1和sum2里面没有重复的数字,这一点怎么来得出呢?首先我们要把sum1和sum2里面的所有数字取出来,怎么取?大家还记得前几讲说过的取余“%”运算符吗?现在我们就要用它和取整来对sum1和sum2取余,然后我们把每位都储存在一个数组里面,所以我们要在开头先声明一个数组,为了和早些时候的sum1和sum2区分开,我们这里声明为sum3和sum4操作如下:
——————————–
int  a[10] ={0 }//分辨用0来初始化
……
//对sum3
for(i=3;

i>=0;

i–)
{
       a[i] = sum3%10;
       sum /=10 ;

// sum = sum/10;
}
//对sum4
for(i=9;

i>3;

i–)
{
       a[i] = sum4%10;


       sum4 /= 10;


}
————————————
这样,我们便得到一个包含10个元素的数组,其中这10个元素要满足各不相同的条件,也就是他们分别是:0,1,2,3,4,5,6,7,8,9。那么怎么判断呢?我们可以再声明一个数组int s[10]={0 },同样用0去初始化,然后用a[10]的元素去作为的下标,统计这些数字出现的次数,方法如下:
————————————-
//统计数字出现的次数
for(i=0;

i<=9;

i++)
{
     s[a[i]]++;


}
————————–
接下来我们判断一下有没有重复的数字,方法如下:
——————————
//判断有没有重复数字然后顺便打印出结果
for(i=0;

i<=9;

i++)
{
     if(s[i] == 1)//如果有重复,就不会满足这个条件,然后会跳转到下面的break。
    {
         if(i == 9)//这个判断没多大意义,就是为了只打印一遍就好,否则会打印出10遍来。
            printf(” 同学的年龄是:%dn”,c);

     
      }
     else
        break;


}
————————————
说到这里,想必大家应该都清楚了,下面是完整的程序清单,当然大家可以尝试一下简单的那个方法,就是自己预先算出能够满足sum1和sum2的值,然后再做循环,那样就不需要另外的三个程序。
—————————————-
//完整清单
#include <stdio.h>#include <stdlib.h>long power(int n,int m);

int age();

int agemax();

int main()
{
int n,m,c;

long a[10]={0 },s[10]={0 },i,sum3,sum4;

n = age();

m = agemax();

c = m-n+1;

do
{
sum3 = power(c,3);

for(i=3;

i>=0;

i–)
{
a[i] = sum3%10;

sum3 = sum3/10;

}
sum4 = power(c,4);

for(i=9;

i>3;

i–)
{
a[i] = sum4%10;

sum4 = sum4/10;

}
for(i=0;

i<=9;

i++)
{
s[a[i]]++;

}
for(i=0;

i<=9;

i++)
{
if(s[i] == 1)
{
if(i == 9)
printf(“这位同学的年龄是:%dn”,c);

}
else
break;

}
c++;

}while(c<=m);

system(“PAUSE”);

return 0;

}
//实现幂次方
long power(int n,int m)
{
long s=1;

while(m–)
{
s *= n;

}

return s;

}
//求满足sum1和sum2的个数
int age()
{
int age[25];

int count = 0;

long sum1,sum2;

for(int i=0;

i<25;

i++)
{
sum1 = power(i,3);

sum2 = power(i,4);

if(sum1 >1000 &

&

sum1 <10000 &

&

sum2 >100000 &

&

sum2 <1000000)
{
age[i] = i;

count++;

}
}

return count;

}
//求出满足sum1和sum2的最大值
int agemax()
{
int age[25]={0 },max = 0;

long sum1,sum2;

for(int i=0;

i<25;

i++)
{
sum1 = power(i,3);

sum2 = power(i,4);

if(sum1 >1000 &

&

sum1 <10000 &

&

sum2 >100000 &

&

sum2 <1000000)
{
age[i] = i;

}
if(max<age[i])
{
max = age[i];

}
}

return max;

}
———————————–
现在我们来看下输出:
第十一讲 do while
哦,原来臭小子是18岁啊,如果有持怀疑精神的可以自己推算一下吧,把这个程序理解后我相信大家不但学会了do while还学会了很多东西,好吧,今天就到这里,大家有兴趣证明一下尼科彻斯吗?
====================
尼科彻斯定理:
任何一个整数的立方都可以写成一连串的连续奇数之和,大家可以编程证明这个定理,知识点不会超出我们现在所讲范围。
====================

第十讲 switch()

         昨天留下的问题不知道大家思考得怎么样,不管大家思考得怎么样,我想大家心中已经有了答案,不过我还是要说说,这个问题最适合探讨for()和if()的用法了,这个问题的解决方案就是用for来遍历,用if来判断,是或非,在一遍遍的循环比较中最终得出答案,下面我们来看看这个实现清单:
—————————————–
#include <stdio.h>
#include <stdlib.h>
int main()
{
    int A,B,C,D,E;


    for(A=1;

A<6;

A++)
   {
        for(B=1;

B<6;

B++)
       {
             if(A!=B)
             {
                 for(C=1;

C<6;

C++)
                {
                     if(C!=B &

&

C!=A)
                    {
                         for(D=1;

D<6;

D++)
                         {
                              if(D!=C&

&

D!=B&

&

D!=A)
                             {
                                  for(E=1;

E<6;

E++)
                                 {
                                      if(E!=D&

&

E!=C&

&

E!=B&

&

E!=A)
                                       {
                                            if((C!=1)&

&

(D-E==2)&

&

(E!=2)&

&

(A!=1)&

&

(A!=5)&

&

(B-C==1))
                                             {
                                                printf(“A:%d”,A);


                                                printf(“B:%d”,B);


                                                printf(“C:%d”,C);


                                                printf(“D:%d”,D);


                                                printf(“E:%d”,E);


                                             
}

                                      
}

                                  
}

                              
}

                          
}

                     
}

                  }
             
}

        
}

    
}

     system(“PAUSE”);


    

return 0;


}
——————————————-
这循环虽然嵌套得有些复杂,但是不难看懂,这个循环简单,就是遍历一遍名次后再做比较然后继续下一个,直到最终比对出答案,现在我们来看看运行结果:
第十讲 switch()
是不是很有意思?其实很多有趣问题我们都可以通过简单的代码来轻松实现,不过,这些问题留待大家去探索,现在我们先来说说switch函数吧,还记得昨天我们讲的if()语言说的那个菜单问题吗?现在我们不妨来个更简单明了的现实:
—————————————-
#include <stdio.h>#include <stdlib.h>int main()
{
char ch;


   
int n;

printf(“回复D或者d查看目录:n”);

while(scanf(“%c”,&

ch))
{
switch(ch)
{
case ‘D’:
case ‘d’:
printf(“第一讲 Hello World.tn”);

printf(“第二讲 printf()(1).tn”);

printf(“第三讲 printf()(2).tn”);

printf(“…………tn”);

printf(“回复相应的数字查看相应章节.tn”);

goto d;

default:
printf(“无效字符,请重新输入:n”);

}
}
d:    while(scanf(“%d”,&

n))
{
switch (n)
{
case 1:
printf(“第一讲 Hello World.tn”);

break;

case 2:
printf(“第二讲 printf().tn”);

break;

case 3:
printf(“第三讲 printf()(2).tn”);

break;

default:
printf(“敬请期待……n”);

break;

}
}
system(“PAUSE”);

return 0;

}
———————————————
这个实现中,我们用了2个while和两个switch来制作这个菜单,第一个while嵌套switch用来查看目录,相信细心的同学应该知道这个目录就是我们这个公众课的目录,第二个while嵌套switch用来查看目录,但由于现在我们主要是讲这个switch的用法,所以我们就只是简单的把章节名拿出来show一下就好,等到以后我们就可以输入相应的章节序号可以直接打开章节文本和内容或者直接从网络上获取。
大家可能看到一个奇怪的“d:”标志,这里顺便也就给大家说一下goto语句,goto是一个让人讨厌又让人喜欢的语句,讨厌之处是本身容易出错,但是好处就是可以直接从一个深度循环里面直接跳转出来,由于while循环有个共性,只要你给的条件满足他就会一直循环下去,俗称死循环,像我们上面的这种写法就更容难退出了,这是我们所不希望看到的,因为如果这样的话我们就会被这个while给套牢了,我们想要的是当我们进入目录之后就可以进入查看章节的章程,当然如果我们没有进入目录自然会有重新尝试的机会,所以,我们应该在正确进入目录后直接跳出目录然后进行查看章节的环节,所以我们在打印完章节后我们使用goto语句直接退出循环,进入下一个环节,goto在这里就起到了关键作用,goto后面的d是一个标识符,标明要跳转d标记的地方,想要标记某个位置就直接在该行代码前加上该标识符和冒号即可,当程序运行到goto语句时直接跳转到标记处。
说完goto,现在还有个陌生的东西要给大家说,这同时也是今天的重点,switch语句,大家是不是都看到了,在两个循环里面,switch似乎都是和case一起使用,所以我们可以简单的总结一下switch的用法:
————————————
switch(what)//判断what是什么
{
 case  what =what1://当what是what1的时候
         do something;
         break;
 case   what = other what://是其他情况的时候
         do something;
         break;
case  ……:
       ……
default://表示自己不希望what = 的值
      break;
————————————
从上面我们总结出来的用法中,大家似乎又接触到了一个新东西:break;既然今天连碰到goto和break,我们不妨再把另一个关键字拿出来一起说了吧,这个关键字当然说的是continue,这三个关键字都有跳转的意思,我们现在来看看三个关键字的区别,以便大家不要用错了:
break 命令可以与三种循环形式中的任何一种以及 switch 语句一起使用。它导致程序控制跳过包含它的循环或 switch 语句的剩余部分,继续执行紧跟在循环或 switch 后的下一条命令。
continue 命令可以与三种循环形式中的任何一种一起使用,但不能和 switch 语句一起使用。它导致程序控制跳过循环中的剩余语句。对于 while 或 for 循环,开始下一个循环周期。对于 do while 循环(明天再说这个循环),对退出条件进行判断,如果必要,开始一个循环周期。
goto 语句导致程序控制跳转到由指定标签定位的语句。冒号用来将被标记的语句同它的标签相分隔。标签名遵循变量的命名规则。被标记的语句可以出现在 goto 之前或之后。
现在陌生的东西我们都弄清楚了,我们就来看下这个程序的效果吧:
第十讲 switch()
今天 就说到这里了,大家可以尝试一下其他的吧,现在还是老规矩,留个问题大家思考一下吧:
—————————————
        一个同学很喜欢数学,有人问他年龄时他却说:“ 我的年龄平方是三位数,立方是四位数,四次方是六位数,三次方和四次方正好占遍了0,1,2,3,4,5,6,7,8,9这10个数字。”现在大家能够猜到这位同学年龄吗?
—————————————–