C语言看到门:从Hello,World!到冒泡排序

说明:
1.本文从我的C语言学习笔记中整理,学习自课程、网络、书籍。
2.本文中的引用:C语言编程相关中的部分语句(由于这篇文章整理自我三年前的笔记,笔记中一些摘录的出处已经难以考证,故无法给出有效链接)
3.本文相当基础,读完后应该连入门都不算。如果你不会C语言,欢迎阅读;如果你不是纯小白,建议不要浪费时间。
吐槽:
1.我想在百度上搜一下引用语句的出处,结果我发现有三个以上标原创的不同文章一模一样。

C语言编程相关

简单介绍以及其他一些有的没的

编程语言的发展大概经历了以下几个阶段:

  • 汇编语言 --> 面向过程编程 --> 面向对象编程
  • 汇编语言是编程语言的拓荒年代,它非常底层,直接和计算机硬件打交道,开发效率低,学习成本高;
  • C语言是面向过程的编程语言,已经脱离了计算机硬件,可以设计中等规模的程序了;
  • Java、C++、Python、C#、PHP 等是面向对象的编程语言,它们在面向过程的基础上又增加了很多概念。
C标准库

标准C语言(ANSI C)共定义了15 个头文件,称为“C标准库”,所有的编译器都必须支持,如何正确并熟练的使用这些标准库,可以反映出一个程序员的水平。

  • 合格程序员:<stdio.h>、<ctype.h>、<stdlib.h>、<string.h>
  • 熟练程序员:<assert.h>、<limits.h>、<stddef.h>、<time.h>
  • 优秀程序员:<float.h>、<math.h>、<error.h>、<locale.h>、<setjmp.h>、<signal.h>、<stdarg.h>
关于学习的平台选择

因为Window系统的市场占比很大,所以我建议,你可以在Windows下学习C语言,完全没问题,如果有一天Windows不能满足你的学习工作需求了,那你再切换到如Linux等其他平台,这个时候你对编程已经很了解了,切换的成本也低了。

简单描述程序的运行过程(以QQ为例)

首先,有一点你要明确,你安装的QQ软件是保存在硬盘中的。

双击QQ图标,操作系统就会知道你要 运行这个软件,它会在硬盘中找到你安装的QQ软件,将数据(安装的软件本质上就是很多数据的集合)复制到内存。QQ不是在硬盘中运行的,而是在内存中运行的。因为内存的读写速度比硬盘快很多。

对于读写速度,内存 > 固态硬盘 > 机械硬盘。机械硬盘是靠电机带动盘片转动来读写数据的,而内存条通过电路来读写数据,电机的转速肯定没有电的传输速度(几乎是光速)快。虽然固态硬盘也是通过电路来读写数据,但是因为与内存的控制方式不一样,速度也不及内存。

例如,打开Word文档,输入一些文字,虽然我们看到的不一样了,但是硬盘中的文档没有改变,新增的文字暂时保存到了内存,Ctrl+S才会保存到硬盘。因为内存断电后会丢失数据,所以如果你编辑完Word文档忘记保存就关机了,那么你将永远无法找回这些内容。

虚拟内存

如果我们运行的程序较多,占用的空间就会超过内存(内存条)容量。例如计算机的内存容量为2G,却运行着10个程序,这10个程序共占用3G的空间,也就意味着需要从硬盘复制 3G 的数据到内存,这显然是不可能的。

操作系统(Operating System,简称 OS)为我们解决了这个问题:当程序运行需要的空间大于内存容量时,会将内存中暂时不用的数据再写回硬盘;需要这些数据时再从硬盘中读取,并将另外一部分不用的数据写入硬盘。这样,硬盘中就会有一部分空间用来存放内存中暂时不用的数据。这一部分空间就叫做虚拟内存(Virtual Memory)。

3G - 2G = 1G,上面的情况需要在硬盘上分配 1G 的虚拟内存。

硬盘的读写速度比内存慢很多,反复交换数据会消耗很多时间,所以如果你的内存太小,会严重影响计算机的运行速度,甚至会出现”卡死“现象,即使CPU强劲,也不会有大的改观。如果经济条件允许,建议将内存升级为 4G,在 win7、win8、win10 下运行软件就会比较流畅了。

总结:CPU直接从内存中读取数据,处理完成后将结果再写入内存。

编译器介绍及使用技巧

C语言的编译器有很多种,不同的平台下有不同的编译器,例如:

  • Windows 下常用的是微软开发的 cl.exe,它被集成在 Visual Studio 或 Visual C++ 中,一般不单独使用;
  • Linux 下常用的是 GUN 组织开发的 GCC,很多 Linux 发行版都自带 GCC;
  • Mac 下常用的是 LLVM/Clang,它被集成在 Xcode 中(Xcode 以前集成的是 GCC,后来由于 GCC 的不配合才改为 LLVM/Clang,LLVM/Clang 的性能比 GCC 更加强大)。

你的代码语法正确与否,编译器说了才算,我们学习C语言,从某种意义上说就是学习如何使用编译器,让编译器生成可执行程序(例如 Windows 下的 .exe 程序)。

Visual C++ 6.0常用的快捷键

Ctrl+N:创建一个新文件

Ctrl+]:检测程序中的括号是否匹配

F7:Build操作

Ctrl+F5:Execute执行操作

Alt+F8:整理一段不整洁的源代码

F5:进行调试

C语言基础

认识C程序

最简单的C语言程序
#include<stdio.h>//引用头文件
int main()//main函数
{//{}内为函数体,程序中非字符串中的符号均为英文字符
    printf("Hello,World!\n");//输出要显示的字符串
    return 0;//程序返回0
}
printf()函数

printf()输出函数(关键字中的 f 就是表示 format,格式化的意思)用于输出变量的值时,调用格式为printf(格式控制,输出表列);

“格式控制”是用双引号括起来的字符串,包括格式字符串和普通字符。

格式字符串

格式字符串是以%开头的,在%后跟各种格式字符,以说明输出数据的类型、形式、长度、小数位数等。

如“%d”表示按十进制整型输出,“%ld”表示按十进制长整型输出等。

c——输出单个字符;s——输出字符串

求输入的两个整数之和
#include<stdio.h>
main()
{
    int a,b,sum;
    printf("please input two number:\n");
    scanf("%d%d",&a,&b);//输入两个数a和b
    sum=add(a,b);//调用函数add
    printf("%d+%d=%d",a,b,sum);
}
int add(int a,int b)//自定义函数add
{
    int c;
    c=a+b;
    return c;//将两数之和返回
}

= 赋值

== 等于

scanf()函数

scanf()为输入函数,即按用户指定的格式从键盘上把数据输入到指定的变量之中。scanf 函数的一般形式为scanf(“格式控制字符串”,地址表列);

其中,格式控制字符串的作用与 printf函数相同,但不能显示普通字符串,也就是不能显示提示字符串。

地址表列中给出各变量的地址。地址是由地址运算符“&”后跟变量名组成的。

例如,“&a,&b”分别表示变量 a 和变量 b 的地址。
这个地址就是编译器在内存中给 a 和 b 变量分配的地址

输出一个正方形
#include<stdio.h>
main()
{
    printf(" ***** \n");//\n为转义字符,作用为换行
    printf("*     *\n");
    printf("*     *\n");
    printf("*     *\n");
    printf(" ***** \n");
}
十进制转十六进制
#include<stdio.h>
main()//main函数
{
    int i;//定义一个变量i
    printf("please input decimalism number:\n");
    scanf("%d",&i);//scanf函数以十进制形式获得i的值
    printf("the hex number is %x",i);//将i的值以十六进制形式输出
}
3个数由小到大排序
#include<stdio.h>
main()
{
    int a,b,c,t;
    printf("Please input a,b,c:\n");
    scanf("%d%d%d",&a,&b,&c);//输入任意3个数
    if(a>b)//如果a>b,借助中间变量t实现a、b值转换
    {
        t=a;
        a=b;
        b=t;
    }
    if(a>c)
    {
        t=a;
        a=c;
        c=t;
    }
    if(b>c)
    {
        t=b;
        b=c;
        c=t;
    }
    printf("the order of the number is:\n");
    printf("%d,%d,%d",a,b,c);//按从小到大顺序输出
}

标识符

C语言里大量使用名称区分不同内容,这些名称叫做标识符。

编写规则
  • 第一个字符只能是英文字母或下划线;
  • 后面的每个字符可以是英文字母,下划线或阿拉伯数字;
  • 大小写不同的标识符是不同的标识符(大小写敏感)
  • 关键字不可以作为标识符使用
  • 标识符的长度没有限制,但是计算机只会截取前面一部分使用
  • 标识符应该采用驼峰方式或下划线方式书写

变量和数据类型

数据类型
1.基本类型:a.整型:·短整型
​                   ·基本整型
​                   ·长整型
​            b.字符型
​            c.实型(浮点型):·单精度型
​                             ·双精度型
​            d.枚举类型
2.构造类型: a.数组类型
​            b.结构体类型
​            c.共用体类型
3.指针类型
4.空类型:void
基本数据类型
说明字符型短整型整型长整型floatdouble无类型
数据类型charshortintlongfloatdoublevoid
长度124448

最后需要说明的是:数据类型只在定义变量时指明,而且必须指明;使用变量时无需再指明,因为此时的数据类型已经确定了。

扩展:

在 16 位环境下,short 的长度为 2 个字节,int 也为 2 个字节,long 为 4 个字节。16 位环境多用于单片机和低级嵌入式系统,在PC和服务器上已经见不到了。

对于 32 位的 Windows、Linux 和 Mac OS,short 的长度为 2 个字节,int 为 4 个字节,long 也为 4 个字节。PC和服务器上的 32 位系统占有率也在慢慢下降,嵌入式系统使用 32 位越来越多。

在 64 位环境下,不同的操作系统会有不同的结果,如下所示:

操作系统shortintlong
Win64(64位 Windows)244
类Unix系统(包括 Unix、Linux、Mac OS、BSD、Solaris 等)248

目前我们使用较多的PC系统为 Win XP、Win 7、Win 8、Win 10、Mac OS、Linux,在这些系统中,short 和 int 的长度都是固定的,分别为 2 和 4,可以放心使用,只有 long 的长度在 Win64 和类 Unix 系统下会有所不同,使用时要注意移植性。

用sizeOf()求基本数据类型的长度

sizeof()是保留字,它的作用是求某类型或某变量类型的字节数,括号中可以是类型保留字或变量。

比如,int a = 1; sizeof(a) = 2;

/*
 * sizeof关键字演示
 * */
#include <stdio.h>
int main() {
    int val = 0;
    printf("sizeof(val)是%d\n", sizeof(val));
    printf("sizeof(int)是%d\n", sizeof(int));
    printf("sizeof(3 + 6)是%d\n", sizeof(3 + 6));
    sizeof(val = 10);
    printf("val是%d\n", val);
    return 0;
}
#include <stdio.h>
int main()
{
    short a = 10;
    int b = 100;
   
    int short_length = sizeof a;
    int int_length = sizeof(b);
    int long_length = sizeof(long);
    int char_length = sizeof(char);
   
    printf("short=%d, int=%d, long=%d, char=%d\n", short_length, int_length, long_length, char_length);
   
    return 0;
}

在 32 位环境以及 Win64 环境下的运行结果为:

short=2, int=4, long=4, char=1

在 64 位 Linux 和 Mac OS 下的运行结果为:

short=2, int=4, long=8, char=1

sizeof 用来获取某个数据类型或变量所占用的字节数,如果后面跟的是变量名称,那么可以省略(),如果跟的是数据类型,就必须带上()。

注意:sizeof 是C语言中的操作符,不是函数,所以可以不带()

常量
字符常量的输出
#include<stdio.h>
int main()
{
    putchar('H');//输出字符常量H
    putchar('e');
    putchar('l');
    putchar('l');
    putchar('o');
    putchar('\n');
    return 0;
}
输入圆半径得圆面积(符号常量)
#include<stdio.s>
#define PAI 3.14//定义符号常量
int main()
{
    double fRadlius;//定义半径变量
    double fResult=0;//定义结果变量
    printf("请输入圆的半径:");
    scanf("%lf",&fRadlius);
    fResult=fRadlius*fRadlius*PAI;
    printf("圆的面积为:%lf\n",fResult);
    return 0;
}
计算学生平均身高
#include<stdio.h>
void main()
{
    float a1=0,a2=0,a3=0;
    float avg=0;
    printf("输入3个学生的身高:(单位:cm)\n");
    printf("%f%f%f",&a1,&a2,&a3);
    avg=(a1+a2+a3)/3;
    printf("平均身高为:%f\n",avg);
}
变量
变量的概念
  • 现实生活中我们会找一个小箱子来存放物品,一来显得不那么凌乱,二来方便以后找到。计算机也是这个道理,我们需要先在内存中找一块区域,规定用它来存放整数,并起一个好记的名字,方便以后查找。这块区域就是“小箱子”,我们可以把整数放进去了。

  • int a; a=290;

    /*

    把 290 放到了一块叫做 a 的内存区域。=是一个符号,它在数学中叫“等于号”,例如 1+2=3,但在C语言中,这个过程叫做赋值(Assign)。赋值是指把数据放到内存的过程。

    */

局部变量和全局变量
  • 定义在函数内部的变量称为局部变量(Local Variable),它的作用域仅限于函数内部, 离开该函数后就是无效的,再使用就会报错。

  • 标准C语言(ANSI C)共定义了15 个头文件,称为“C标准库”,所有的编译器都必须支持,如何正确并熟练的使用这些标准库,可以反映出一个程序员的水平。

  • 合格程序员:<stdio.h><ctype.h><stdlib.h><string.h>

  • 熟练程序员:<assert.h><limits.h><stddef.h><time.h>

  • 优秀程序员:<float.h><math.h><error.h><locale.h><setjmp.h><signal.h><stdarg.h>

  • 在所有函数外部定义的变量称为全局变量(Global Variable),它的作用域默认是整个程序,也就是所有的源文件,包括 .c 和 .h 文件。

例:

#include <stdio.h>
	int a = 10;
	int b = a + 20;
	int main()
    {
	    return 0;
	}

int b = a + 20;是具有运算功能的语句,要放在函数内部。

#include<studio.h>
int main()
int age=20//全局变量

 {
	int age=20//局部变量
     printf("%d",&age);
  }

整型数据的溢出问题
#include<stdio.h>
int main()
{
    short a,b;
    a=32767;
    b=a+1;
    printf("%d,%d\n",a,b);
    return 0;
}
使用单精度类型变量
#include<stdio.h>
int main()
{
    float x;
    x=12.345f;
    printf("%f\n",x);
    return 0;
}
使用双精度类型变量
#include<stdio.h>
int main()
{
    double x;
    x=61.458;
    printf("%f\n",x);
    return 0;
}
使用长双精度类型变量
#include<stdio.h>
int main()
{
    long double x;
    x=123.456;
    printf("%f\n",x);
    return 0;
}
利用sizeof关键字求长双精度的长度
#include<stdio.h>
int main()
{
    int a;
    a=sizeof(long double);//将长双精度的长度的值赋给变量a
    printf("%d\n",a);
    return 0;
}
字符型数据与整型数据之间的运算
#include<stdio.h>
main()
{
    char c1,c2;
    c1='a';
    c2='b';
    c1=c1+8;
    c2=c2-c1+10;
    printf("%c,%d\n%c,%d\n",c1,c1,c2,c2);
}
实型数据的储存
#include<stdio.h>
int main()
{
    float a;//有效位数7
    double b;//有效位数16
    a=55555.55555;
    b=55555.55555555555555;//系统规定小数点后最多保留6位,其余四舍五入
    printf("%f\n%f\n",a,b);
    return 0;
}
字符型数据进行算术运算
#include<stdio.h>
main()
{
    char ch1,ch2;
    ch1='a',ch2='B';//给ch1,ch2赋值
    printf("ch1=%c,ch2=%c\n",ch1-32,ch2+32);//用字符形式输出一个大于256的数值
    printf("ch1+10=%d\n",ch1+10);
    printf("ch1+10=%c\n",ch1+10);
    printf("ch2+10=%d\n",ch2+10);
    printf("ch2+10=%c\n",ch2+10);
}

进制

二进制

二进制和十进制可以相互转换。

例:

15 652 1008(1111110000)

380(101111100)

79(1001111)

2018 (11111100010)

负数的二进制和十进制之间不能直接转换,需要借助相反的非负数把二进制数字每个数位的内容都变成相反内容(0变1,1变0),然后再加一就得到相反数的二进制。

例:
0000 1110                                                     14
1111 0001 + 1 = 1111 0010(-14的二进制)    -14

有符号类型数字最左边二进制数位的内容可以决定数字的符号,这个数位叫做数字的符号位。符号位内容是0表示非负数,符号位内容是1表示负数。

例:

1100 0101                                 -59
0011 1010 + 1 = 0011 1011        59

八进制

把二进制数字的所有数位从右向左每三个数位分为一组,每组用一个0到7之间的数字替换,这个替换结果叫做数字的八进制表示方式。

例:

0110 1011 01 101 011     153(八进制)

十六进制

把二进制数字的所有数位从右向左每四个数位分为一组,每组用一个字符替换。这个替换结果叫做数字的十六进制表示方式。
用a到f之间的字母替换10到15之间的数字。

例:

1100 1011      cb(十六进制)

1111 1111       ff

运算符

基本概念
单目操作符

自增操作符(++)和自减操作符(–)都是单目操作符

自增或自减表达式本身可以当做数字使用,前操作(++i)当做数字使用的时候是修改后的数字,后操作(i++)当数字使用的时候是修改前的数字

补充:不管前加还是后加,都会进行加一的操作,只不过是先参与运算还是后参与运算

双目位操作符

双目位操作符包括按位与(&),按位或(|)以及按位异或(^),它们可以把两个数字对应二进制数位的内容进行计算

按位与(&)可以把两个数字对应二进制数位的内容做与计算,只要一个数位的内容是0则与计算以后结果就是0

例:

3        0000 0011

&

5        0000 0101

==

          0000 0001

按位或(|)可以把两个数字对应二进制数位的内容做或计算,只要一个数位的内容是1则或计算以后结果就是1

例:

3      0000 0011

|

5      0000 0101

==

        0000 0111

按位异或(^)可以把两个数字对应二进制数位的内容做异或计算,如果两个数位的内容一样则异或计算以后结果是0,否则结果是1,利用按位异或可以把一个数字的某些二进制数位内容变成相反数字。

例:

3        0000 0011

^

5        0000 0101

==

          0000 0110

三目运算符(条件运算符)

它是唯一有3个操作数的运算符,有时又称为三元运算符。一般来说,三目运算符的结合性是右结合的(从右向左分组计算)。

表达式1?表达式2:表达式3;

1+1>3?3:2;(一加一正确的话就执行2,不正确的话执行3)

例:

如果int a=3,b=4;则条件表达式"a<b? a:b"的值是3

用“三目运算符”解决 2018年是闰年还是平年
#include<stdio.h>
int main()
{
    int year=2018;
    int mouth=12;
    int day=31;
    
    int days=((year%400==0)||(year%4==0 && year%100!=0))?29:28
    printf("years=%d\n",days);
}
求余运算符

%为求余运算符,该运算符只能对整型数据进行运算。且符号与被模数相同。

例:

5%2=1;     5%(-2)=1;(-5)%2=-1;(-5)%(-2)=-1;

分支、循环的使用

C语言中常用的编程结构有三种(其它编程语言也是如此),它们分别是:

  • 顺序结构:代码从前往后依次执行,没有任何“拐弯抹角”,不跳过任何一条语句,所有的语句都会被执行到。
  • 选择结构:也叫分支结构。代码会被分成多个部分,程序会根据特定条件(某个表达式的运算结果)来判断到底执行哪一部分。
  • 循环结构:程序会重新执行同一段代码,直到条件不再满足,或者遇到强行跳出语句(break 关键字)。
顺序结构
#include<stdio.h>
int marn()
{
	printf("今天天气很好。\n");
	printf("周日我们在教室里学习。\n");
	printf("很认真地学习。\n");
	return 0;
}
分支结构
if 条件语句

if 语句有以下 3 种形式。
(1)if 语句

if(判断条件){
    语句块
}

例:

if(x>y) printf("%d",x);
其中,if 语句就是根据 x>y 这个表达式的值来决定是否要执行后面的 printf 语句,当表达式的值为真(x>y)时,就执行,当表达式的值为假(x≤y)时,就跳过 printf 语句而直接执行后面的语句。

(2)ifelse 语句

if(判断条件){
    语句块1
}else{
    语句块2
}

例:

if(x>y) printf("%d",x); else printf("%d",y);
如果 x>y 成立,则打印出 x,否则打印出 y。与第一种 if 语句的不同之处在于,不论表达式的值是真是假,if 语句都分别给它们安排了任务。也就是说无论 x,y 哪一个大,总能打印出一个数。

练习:

#include<stdio.h>
int main()
{
    int age;
    printf("请输入你的年龄:");
    scanf("%d", &age);
    if(age>=18){
        printf("恭喜,你已经成年,可以使用该软件!\n");
    }else{
        printf("抱歉,你还未成年,不宜使用该软件!\n");
    }
    return 0;
}
#include <stdio.h>
int main()
{
    int a, b, max;
    printf("输入两个整数:");
    scanf("%d %d", &a, &b);
    if(a>b) max=a;
    else max=b;
    printf("%d和%d的较大值是:%d\n", a, b, max);
    return 0;
}

(3)if…elseif 语句

if(判断条件1){
    语句块1
} else  if(判断条件2){
    语句块2
}else  if(判断条件3){
    语句块3
}else  if(判断条件m){
    语句块m
}else{
     语句块n
}

if(count>500) price = 0.15;
else if(count>300) price = 0.10;
else if (count>100) price = 0.05;
else price = 0;

练习:

#include<stdio.h>
void main()
{
	int day;
    printf("day:");
	scanf("%d",&day);
	if(day==1)
	{
	printf("今天星期一。\n");
	}
	else if(day==2)
	{
	printf("今天星期二。\n");
	}
	else if(day==3)
	{
	printf("今天星期三。\n");
	}
	else if(day==4)
	{
	printf("今天星期四。\n");
	}
	else if(day==5)
	{
	printf("今天星期五。\n");
	}
	else if(day==6)
	{
	printf("今天星期六。\n");
	}
	else if(day==7)
	{
	printf("今天星期日。\n");
	}
}
switch 语句

switch 的中文意思是开关,因此 switch 语句又叫开关语句。switch 语句是专门用来处理多分支的,

例如,学生成绩分类(90 分以上为‘A’等;80~90 分为‘B’等;70~79 分为‘C’等);人口统计分类(按年龄分为老、中、青、少、儿童);工资统计分类;银行存款分类等。

当然这些都可以用嵌套的 if 语句,也就是第 3 种形式的 if 语句来处理,但如果分支较多,就显得嵌套的层数太多了,程序冗长且可读性降低。switch 语句就是专门为了解决多分支的问题而设计的。

switch 语句的一般形式如下:

switch(变量或表达式)
{

    case 整型数值1: 语句 1;
    case 整型数值2: 语句 2;
    ......
    case 整型数值n: 语句 n;
    default: 语句 n+1;
}

其中,default 是可有可无的。

它的执行过程是:

(1) 首先计算“表达式”的值,假设为 m。

(2) 从第一个 case 开始,比较“整型数值1”和 m,如果它们相等,就执行冒号后面的所有语句,也就是从“语句1”一直执行到“语句n+1”,而不管后面的 case 是否匹配成功。

(3) 如果“整型数值1”和 m 不相等,就跳过冒号后面的“语句1”,继续比较第二个 case、第三个 case……一旦发现和某个整型数值相等了,就会执行后面所有的语句。假设 m 和“整型数值5”相等,那么就会从“语句5”一直执行到“语句n+1”。

(4) 如果直到最后一个“整型数值n”都没有找到相等的值,那么就执行 default 后的“语句 n+1”。

#include <stdio.h>
int main(){
    int a;
    printf("Input integer number:");
    scanf("%d",&a);
    switch(a){
        case 1: printf("Monday\n");
        case 2: printf("Tuesday\n");
        case 3: printf("Wednesday\n");
        case 4: printf("Thursday\n");
        case 5: printf("Friday\n");
        case 6: printf("Saturday\n");
        case 7: printf("Sunday\n");
        default:printf("error\n");
    }
    return 0;
}

最后需要说明的两点是:

(1) case 后面必须是一个整数,或者是结果为整数的表达式,但不能包含任何变量。请看下面的例子:

case 10: printf("..."); break;  //正确
case 8+9: printf("..."); break;  //正确
case 'A': printf("..."); break;  //正确,字符和整数可以相互转换
case 'A'+19: printf("..."); break;  //正确,字符和整数可以相互转换
case 9.5: printf("..."); break;  //错误,不能为小数
case a: printf("..."); break;    //错误,不能包含变量
case a+10: printf("..."); break;  //错误,不能包含变量

(2) default 不是必须的。当没有 default 时,如果所有 case 都匹配失败,那么就什么都不执行。

循环结构
while循环
while(表达式){
	语句块;
}
//死循环
while(1){
	语句块;
}
do…while循环
do{
	语句块;
}while(表达式)

与while循环的区别:do-while至少执行一次。

while/do…while循环练习

//输入要查询年的一个月份,查询这个月份有几天
#include<stdio.h>
int main()
{
	int year,month,day;
	printf("请输入你要查询的一个月份,月份:");
	printf("(输入格式:年 月)\n");
	scanf("%d%d",&year,&month);	
	switch(month)
	{
    	case 1:
		case 3:
		case 5:
		case 7:
		case 8:
		case 10:	
		case 12:
			day=31;
			break;
		case 4:
		case 6:
		case 9:
		case 11:
			day=30;
			break;
		case 2:
		/*if(year%400==0||year%4==0&&year%100!=0)
		{
		day=29;
		}else{
		day=28;
		}
		*/
        	day=(year%400==0||year%4==0&&year%100!=0)?29:28;//三目运算符
			break;
	}
    printf("%d",day);
    return 0;
}
//猜大小
#include<stdio.h>
int main()
{
    int val=0;
	int guess=225;
	do{
		printf("请输入一个数字:\n");
		scanf("%d",&val);
		if(val>guess){
			printf("猜大了\n");
		}else if(val<guess){
            printf("猜小了\n");
		}else{
			printf("猜对了\n");
		}
		printf
	}while(val!=guess);
	return 0;
}
//1+2+3+....+100
#include<stdio.h>
int main()
{
	int j=1;
	int sum=0;
	do{
		sum+=j;
        j++;
		printf("%d,%d\n",j,sum);
	}while(j<=100);
    return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main() {
    int val = 0, guess = 0;
    srand(time(0));
    //val = rand() % 100;
    /*while (val != guess) {
        printf("请猜一个数字:");
        scanf("%d", &guess);
        if (guess > val) {
            printf("猜大了\n");
        }
        else if (guess < val) {
            printf("猜小了\n");
        }
        else {
            printf("猜对了\n");
        }
    }*/
    do {
        printf("请猜一个数字:");
        scanf("%d", &guess);
        if (guess > val) {
            printf("猜大了\n");
        }
        else if (guess < val) {
            printf("猜小了\n");
        }
        else {
            printf("猜对了\n");
        }
    } while (val != guess);
    return 0;
}

无法预知的数字叫随机数。

rand标准函数可以用来获得随机数。为了使用这个标准函数需要包含stdlib.h头文件。

srand标准函数可以用来设置随机数种子,这个函数把一个整数作为种子使用不同的种子得到的随机数不同。为了使用这个标准函数需要包含stdlib.h头文件。任何程序只需要设置一次随机数种子。

time标准函数可以用来获得当前时间,这个函数用一个整数表示得到的时间
同一秒之内多次获得代表时间的整数应该相同为了使用这个标准函数需要包含time.h头文件。

for循环

鸡兔同笼练习

#include <stdio.h>
int main() {
    int num = 0;
    for (num = 0;num <= 40;num++) {
        if (4 * num + 2 * (40 - num) == 100) {
            printf("兔子有%d只,鸡有%d只\n", num, 40 - num);
            break;
        }
    }
    printf("num是%d\n", num);
    return 0;
}
//鸡兔同笼,上数35个头,下数94只脚
#include<stdio.h>
int main()
{
	int num=0;
	for(num=0;num<35;num++){
		if(4*num+2*(35-num)==94){
			printf("兔子有%d只,鸡有%d只",num,35-num);
		}
		printf("%d\n",num);
	}
    return 0;
}
break和continue

break 和 continue 关键字用于循环结构时的区别:

  • break 用来跳出所有循环,循环语句不再有执行的机会;
  • continue 用来结束本次循环,直接跳到下一次循环,如果循环条件成立,还会继续循环。

break 关键字还可以用于跳出 switch…case 语句。所谓“跳出”,是指一旦遇到 break,就不再执行 switch 中的任何语句,包括当前分支中的语句和其他分支中的语句;也就是说,整个 switch 执行结束了,接着会执行整个 switch 后面的代码。

数组

数组也需要先声明然后才能使用,声明数组的时候除了要提供类型名称和数组名称以外还需要提供一个整数,表示数组里的存储区个数。

数组必须在声明的时候就确定里面包含的存储区个数并且以后不可以改变。

一维数组

int card[17];

  • 在内存中分配了17个int类型的内存空间,总共占17 * 4= 68个字节,起名字叫card。

  • 包含数据的个数,长度 card[17];

  • 注意:

    数组中的每一个数据类型都是相同的;

    数组的长度在定义的时候,最好是整型。

    在访问数组的时候,一定不要超出它的范围。

数组的初始化:

  • 可以只给数组的部分元素赋值。(short,long,int—0, char-----’\0’,float,double----0.0)

    例:

    int arr[3] = {1,2};

    double score[5] = {0.0};

  • 只能给元素逐个赋值,不能给元素整体赋值

    例:

    int a[6] = 666;//错误

    int arr[6] = {666,666,666,666,666,666};

  • 如果全部元素赋值,那么在定义的时候,可以不给出数组的长度。

    例:

    int arr[] = {1,22,333};

    arr[0] = 1;

    arr[1] = 22;

    arr[2] = 333;

用程序算身份证末位

1、将前面的身份证号码17位数分别乘以不同的系数。从第一位到第十七位的系数分别为:7-9-10-5-8-4-2-1-6-3-7-9-10-5-8-4-2。

2、将这17位数字和系数相乘的结果相加。

3、用加出来和除以11,看余数是多少?

4、余数只可能有0-1-2-3-4-5-6-7-8-9-10这11个数字。其分别对应的最后一位身份证的号码为1-0-X -9-8-7-6-5-4-3-2。(即余数0对应1,余数1对应0,余数2对应X…)

#include<stdio.h>
int main()
{
	int card[17]={0};//用来存储身份证17位数
	int sum=0;
	int data[]={7,9,10,5,8,4,2,1,6,3,7,9,10,5,8,4,2};//前17位系数
	//储存得到最后一位的系数
	char code[]=				{'1','0','x','9','8','7','6','5','4','3','2'};
	int i=0;
	for(i=0;i<17;i++){
       printf("请依次输入身份证的前17位数字:");
	   scanf("%d",&card[i]);
	}
	for(i=0;i<17;i++){
		sum=sum+card[i]*data[i];
	}
	printf("最后一位数字,出现了哈:%c\n",code[sum%11]);
	return 0;
}

身份证的练习:

int main() 
{
	int card[17] = { 0 }, sum = 0;
	int data[] = { 7, 9, 10, 5, 8, 4,
		2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2 };
	char code[] = { '1', '0', 'X', '9',
		'8', '7', '6', '5', '4', '3', '2' };
	int num = 0;
	for (num = 0; num <= 16; num++) {
		printf("请输入一个数字:");
		scanf_s("%d", &card[num]);
	}
	for (num = 0; num <= 16; num++) {
		sum += card[num] * data[num];
	}
	printf("结果是%c\n", code[sum % 11]);

}

scanf_s()函数:

scanf_s()函数是Microsoft公司VS开发工具提供的一个功能相同的安全标准输入函数,从vc++2005开始,VS系统提供了scanf_s()。

scanf_s() 的功能虽然与scanf() 相同,但却比 scanf() 安全,因为 scanf_s() 是针对“ scanf()在读取字符串时不检查边界,可能会造成内存泄露”这个问题设计的。
scanf_s()用于读取字符串时,必须提供一个数字以表明最多读取多少位字符,以防止溢出。

另外,很多带“_s”后缀的函数是为了让原版函数更安全,传入一个和参数有关的大小值,避免引用到不存在的元素,防止hacker利用原版的不安全性(漏洞)黑掉系统。

简单的理解,就是scanf_s会比scanf更安全,那么为了安全也需要编程者多传一些参数,这些参数就是变量的长度(占用的字节数)。

冒泡排序

在实际开发中,有很多场景需要我们将数组元素按照从大到小(或者从小到大)的顺序排列,这样在查阅数据时会更加直观。

对数组元素进行排序的方法有很多种,比如冒泡排序、归并排序、选择排序、插入排序、快速排序等,其中最经典最需要掌握的是「冒泡排序」。

以从小到大排序为例,冒泡排序的整体思想是这样的:

  • 从数组头部开始,不断比较相邻的两个元素的大小,让较大的元素逐渐往后移动(交换两个元素的值),直到数组的末尾。经过第一轮的比较,就可以找到最大的元素,并将它移动到最后一个位置。
  • 第一轮结束后,继续第二轮。仍然从数组头部开始比较,让较大的元素逐渐往后移动,直到数组的倒数第二个元素为止。经过第二轮的比较,就可以找到次大的元素,并将它放到倒数第二个位置。
  • 以此类推,进行 n-1(n 为数组长度)轮“冒泡”后,就可以将所有的元素都排列好。

整个排序过程就好像气泡不断从水里冒出来,最大的先出来,次大的第二出来,最小的最后出来,所以将这种排序方式称为冒泡排序(Bubble Sort)。

下面我们以“3 2 4 1”为例对冒泡排序进行说明。

第一轮 排序过程

3 2 4 1 (最初)

2 3 4 2 (比较3和2,交换)

2 3 4 1 (比较3和4,不交换)

2 3 1 4 (比较4和1,交换)

第一轮结束,最大的数字 4 已经在最后面,因此第二轮排序只需要对前面三个数进行比较。

第二轮 排序过程

2 3 1 4 (第一轮排序结果)

2 3 1 4 (比较2和3,不交换)

2 1 3 4 (比较3和1,交换)

第二轮结束,次大的数字 3 已经排在倒数第二个位置,所以第三轮只需要比较前两个元素。

第三轮 排序过程

2 1 3 4 (第二轮排序结果)

1 2 3 4 (比较2和1,交换)

至此,排序结束。

例:

#include <stdio.h>
int main()
{
	    int nums[10] = {4, 5, 2, 10, 7, 1, 8, 3, 6, 9};
	    int i, j, temp;
	    //冒泡排序算法:进行 n-1 轮比较
	    for(i=0; i<10-1; i++){
	    //每一轮比较前 n-1-i 个,也就是说,已经排序好的最后 i 个不用比较
	        for(j=0; j<10-1-i; j++){
	            if(nums[j] > nums[j+1]){
	                temp = nums[j];
	                nums[j] = nums[j+1];
	                nums[j+1] = temp;
	            }
	        }
	    }
	    //输出排序后的数组
	    for(i=0; i<10; i++){
	        printf("%d ", nums[i]);
	    }
	    printf("\n");
	    return 0;
}

优化:其中有一点是可以优化的:当比较到第 i 轮的时候,如果剩下的元素已经排序好了,那么就不用再继续比较了,跳出循环即可,这样就减少了比较的次数,提高了执行效率。未经优化的算法一定会进行 n-1 轮比较,经过优化的算法最多进行 n-1 轮比较,高下立判。

#include <stdio.h>
int main()
{
	    int nums[10] = {4, 5, 2, 10, 7, 1, 8, 3, 6, 9};
	    int i, j, temp, isSorted;
	    //优化算法:最多进行 n-1 轮比较
	    for(i=0; i<10-1; i++){
	        isSorted = 1;  //假设剩下的元素已经排序好了
	        for(j=0; j<10-1-i; j++){
	            if(nums[j] > nums[j+1]){
	                temp = nums[j];
	                nums[j] = nums[j+1];
	                nums[j+1] = temp;
	                isSorted = 0;  //一旦需要交换数组元素,就说明剩下的元素没有排序好
	            }
	        }
	        if(isSorted) break; //如果没有发生交换,说明剩下的元素已经排序好了
	    }
	    for(i=0; i<10; i++){
	        printf("%d ", nums[i]);
	    }
	    printf("\n");
	    return 0;
}

我们额外设置了一个变量 isSorted,用它作为标志,值为“真”表示剩下的元素已经排序好了,值为“假”表示剩下的元素还未排序好。

每一轮比较之前,我们预先假设剩下的元素已经排序好了,并将 isSorted 设置为“真”,一旦在比较过程中需要交换元素,就说明假设是错的,剩下的元素没有排序好,于是将 isSorted 的值更改为“假”。

每一轮循环结束后,通过检测 isSorted 的值就知道剩下的元素是否排序好。

ends

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值