C语言基础知识【即将完结,建议收藏】

C语言基础知识

  • 一、计算
    • 1、算找零 ~ 思路+代码
    • 2、scanf函数
    • 3、常量 & 变量
      • (1)常量:
      • (2)变量:
    • 4、浮点数
      • (1)计算身高的程序
        • ① 英尺、英寸换算成米
        • ② 厘米换算成英尺、英寸
      • (2)总结:
    • 5、表达式
      • (1)计算时间差:
      • (2)求两数平均值
      • (3)运算符优先级
      • (4)交换变量
      • (5)复合运算符
        • ① 复合赋值
        • ② 递增、递减运算符
    • 6、程序案例——逆序的三位数
  • 二、判断与循环
    • 1、判断
      • (1)引例程序
      • (2)C语言提供了六个关系运算符:
        • ① 优先级
      • (3)找零计算器——优化
      • (4)计算薪水:
      • (5)判断成绩
    • 2、循环
      • (1)判断数字的位数
        • ① 四位数及以下:
        • ② 优化 —— 循环语句
      • (2)do-while 循环
        • ③ do-while 循环进行优化
      • (3)for 循环
        • ① 阶乘 运算
          • 1、while 循环
          • 2、for 循环
      • (4)三种循环选择 tips:
  • 三、进一步的循环与判断
    • 1、逻辑类型和运算
      • (1)bool 类型
      • (2)逻辑运算
        • a. 优先级总结
      • (3)条件运算符
      • (4)逗号运算符
    • 2、级联和嵌套的判断
      • (1)嵌套的 if-else
        • eg. 程序 —— 三个数大小的比较
      • (2)级联的 if-else
        • eg. 分段函数
    • 3、多路分支
      • (1)if - else if - else……
      • (2)switch-case
    • 4、循环的程序案例
      • (1)循环计算 —— 计算log 以 2 为底,x 的对数
      • (2)算平均数
      • (3)猜数游戏
      • (4)整数求逆
  • 四、循环控制
    • 1、循环控制
      • (1)例程:判断一个数是不是素数
    • 2、多重循环
      • (1)例程:输出100以内的素数
      • (2)例程②:输出前50个素数
      • (3)凑硬币 _ 用1角、2角、5角的硬币凑出10元以下的金额
        • 【代码①】:—— 接力 ```break```
        • 【代码②】:—— ```goto```语句
    • 3、循环应用案例
      • (1)求前 n 项和
        • ① f(n)=1+1/2+1/3+…1/n
        • ② f(n)=1-1/2+1/3-1/4+…+1/n
      • (2)求最大公约数
        • 法①:枚举
        • 法②:==辗转相除法==
      • (3)正序分解整数
        • 法①:先逆序,再逆序
        • 法②:正序输出
  • 五、数组与函数
    • 1、数组:
      • (1)引例:如何写一个程序计算用户输入的数字的平均数
        • ① 不需要记录输入的每一个数
        • ② 计算输入的平均数,并输出所有大于平均数的数
      • (2)定义数组
          • 程序优化:
      • (3)用数组做散列计算
    • 2、函数的定义与使用
      • (1)求素数的和
        • 优化:调用函数
      • (2)求和:求出1到10、20到30和35到45的三个和
        • 优化:求和函数
      • (3)什么是函数?
          • ① 调用函数
      • (4)从函数中返回
          • 例一:
          • 例二:
          • ① 没有返回值的函数:
    • 3、函数的参数和变量
      • (1)函数原型
      • (2)参数传递
      • (3)本地变量
          • ①变量的生存期和作用域
          • ② 本地变量的规则
      • (4)其他细节
          • ① 没有参数时
          • ② 逗号运算符
          • ③ 函数里的函数
    • 4、二维数组
      • (1)二维数组:
          • ① 二维数组的遍历
      • (2)二维数组的初始化
      • (3)tic-tac-toe游戏(井字棋)
  • 六、数组运算
    • 1、数组运算
      • (1)引例:
      • (2)数组的大小
      • (3)数组的赋值
    • 2、搜索
      • (1)线性搜索 —— 例子①:
      • (2)线性搜索 —— 例子②:
          • 改进:把面额和名字放的比较近
      • (3)二分搜索
    • 3、排序初步 —— 选择排序
  • 七、指针与字符串
    • 1、指针
      • (1)取地址运算:&运算符取得变量的地址
          • ① `sizeof`
          • ② 运算符`&`
      • (2)指针:指针变量就是记录地址的变量
          • ① 指针 —— 就是保存地址的变量
          • ② 指针变量
          • ③ 作为参数的指针
          • ④ 访问那个地址上的变量*
      • (3)指针与数组:为什么数组传进函数后,sizeof不对了?
          • ① 数组变量是特殊的指针
    • 2、字符类型
      • (1)字符类型
          • ① 字符的输入输出:
          • ② 混合输入:
          • ③ 大小写转换
      • (2)逃逸字符
          • ① `\b`
          • ②`\t`
    • 3、字符串
      • (1)字符串
          • ① 字符数组
          • ② 字符串
          • ③ 字符串变量
          • ④ 字符串常量
      • (2)字符数组
    • 4、字符串计算

一、计算

1、算找零 ~ 思路+代码

【思路】:
① 有地方放输入的数字;
② 有办法输入数字;
③ 输入的数字能参与计算。
【代码】:

#include <stdio.h>

int main()
{
    int price = 0;      // 定义了整型变量price --- 变量是保存数据的地方;变量的名字是一种“标识符”
                            // 标识符只能由字母、数字和下划线组成,数字不能出现在第一个位置上
    printf("请输入金额(元):");
    scanf("%d", &price);    // 注意price前面的 & ;

    int change = 100 - price;

    printf("找您%d元。\n", change);

    return 0;
}

【Note:】
1、变量是保存数据的地方;变量的名字是一种“标识符”;
2、标识符只能由字母、数字和下划线组成,数字不能出现在第一个位置上;

2、scanf函数

1、作用:输入
2、eg.

scanf("%d",&price);

注意:
① scanf 中,price前面有一个&
② “%d",表明scanf要读取一个整数,交给后面的变量;
“%lf”,表示要读取一个浮点数;
③ 出现在scanf的格式字符串中的东西,是一定要输入的东西。

3、常量 & 变量

(1)常量:

1、eg.主程序:

	const int AMOUNT = 100;
    int price = 0;      
                           
    printf("请输入金额(元):");
    scanf("%d", &price);    // 注意price前面的 & ;

    int change = AMOUNT - price;

    printf("找您%d元。\n", change);

2、const 是一个修饰符,加在int 前。初始化后不能修改,即不能再赋值;

(2)变量:

3、让用户输入变量 AMOUNT ,而不是使用固定的初始值
【主程序】:

    int amount = 100;
    int price = 0;      // 定义了整型变量price --- 变量是保存数据的地方;变量的名字是一种“标识符”
                            // 标识符只能由字母、数字和下划线组成,数字不能出现在第一个位置上
    printf("请输入金额(元):");
    scanf("%d", &price);    // 注意price前面的 & ;

    printf("请输入票面:");
    scanf("%d",&amount);

    int change = amount - price;

    printf("找您%d元。\n", change);

【运行结果】:
C语言基础知识【即将完结,建议收藏】_第1张图片
4、读入两个变量的值,将它们相加以后的结果输出:
【代码】:

    int a;
    int b;

    printf("请输入两个整数:");
    scanf("%d %d",&a,&b);
    printf("%d + %d = %d\n",a,b,a+b);

【运行结果】:
C语言基础知识【即将完结,建议收藏】_第2张图片

4、浮点数

(1)计算身高的程序

1、两个整数的运算结果只能是整数;
eg. 10/3*3 = 9
2、浮点数——小数点是浮动的;
3、当整型数和浮点数放在一起运算时,C会将整数转换为浮点数,再进行浮点数的运算;

① 英尺、英寸换算成米

【代码】:

#include <stdio.h>

int main()
{
	printf("请分别输入身高的英尺和英寸,"
		"如输入\"5 7\"表示5英尺7英寸:");

	int foot;
	int inch;

	scanf("%d %d", &foot, &inch);

	printf("身高是%f米。\n", 
		((foot + inch / 12.0) * 0.3048));

    return 0;
}

【运行结果】:
C语言基础知识【即将完结,建议收藏】_第3张图片

② 厘米换算成英尺、英寸

C语言基础知识【即将完结,建议收藏】_第4张图片

#include<stdio.h>

int main(){
    int foot,inch;
    int cm;
    float m,t;
    
    scanf("%d",&cm);
    m = cm/100.0;
    // foot + inch / 12 = m/0.3048;
    t = m/0.3048;
    // foot = t的整数部分;
    // inch = t的小数部分*12
    foot = t;
    inch = (t - foot)*12;
    printf("%d %d",foot,inch);
    
    return 0;
    
}

Alt

4、双精度浮点数 double
【代码】:

#include <stdio.h>

int main()
{
	printf("请分别输入身高的英尺和英寸,"
		"如输入\"5 7\"表示5英尺7英寸:");

	double foot;
	double inch;

	scanf("%lf %lf", &foot, &inch); // lf 来表达输入的事 double 类型

	printf("身高是%f米。\n", 
		((foot + inch / 12) * 0.3048));

    return 0;
}

运行结果与上述相同。

(2)总结:

1、整数 int :输入输出都用%d

printd("%d",...);
scanf("%d",...);

2、带小数点的数:

double
printf("%f",...);
scanf("lf",...);

5、表达式

(1)计算时间差:

【思路】:
1、程序中要有哪些变量,这些变量要怎么读入数据、表达数据;
2、有了数据以后,怎么去计算;
【代码】:

#include <stdio.h>

int main()
{
	int hour1,minute1;
    int hour2,minute2;
    int t1,t2,t;

    scanf("%d %d",&hour1,&minute1);
    scanf("%d %d",&hour2,&minute2);

    t1 = hour1 * 60 + minute1;  // 转换成以分钟为单位
    t2 = hour2 * 60 + minute2;

    t = t2 - t1;

    printf("时间差是%d小时%d分", t/60, t%60 );  // t/60 -- 小时部分, t%60 -- 分钟部分

	return 0;
}

【运行结果】:
C语言基础知识【即将完结,建议收藏】_第5张图片

(2)求两数平均值

【思路】:
1、有什么样的变量来保存读进来的东西;
2、怎么计算;

【代码】:

#include <stdio.h>

int main()
{
    int a,b;
    double c;

    scanf("%d %d",&a,&b);

    c = (a + b) / 2.0;

    printf("%d和%d的平均值=%lf\n",a,b,c);

	return 0;
}

【运行结果】:
C语言基础知识【即将完结,建议收藏】_第6张图片

(3)运算符优先级

C语言基础知识【即将完结,建议收藏】_第7张图片
1、单目运算,只有一个算子。eg. -a;
2、单目运算符的优先级高于乘除:
eg.

a*-b;	// 先算-b,再与a相乘;

3、相同优先级——从左到右→结合;
【例外】:单目运算符、赋值运算——从右向左结合;

4、【例题】:

10 + 9 * ((8 + 7) % 6) + 5 * 4 % 3 * 2 + 3 	// 44

1 + 2 + (3 + 4) * ((5 * 6 % 7 / 8) - 9) * 10	//-627

5、嵌入式赋值:

	int a = 6;
	int b;
	int c = 1 + (b=a);

缺点:不利于阅读;易产生错误;

6、举例:

result = a = b = c + 3; // 先算3+c,再赋给b,再赋给a,再赋给result;
result = (result = result * 2)*6*(result = 3+result);	//最好不要用嵌入式赋值!!要拆开几个表达式

(4)交换变量

【程序】:

#include <stdio.h>

int main()
{
    int a,b;
    int c;

    printf("请输入a与b的值:");
    scanf("%d %d",&a,&b);

    c = a;
    a = b;
    b = c;

    printf("a=%d,b=%d\n",a,b);

	return 0;
}

【运行结果】:
C语言基础知识【即将完结,建议收藏】_第8张图片

(5)复合运算符

① 复合赋值

1、包括+=-=*=/=%=
2、eg.

total += 5;	// total = total + 5
total *= sum + 12;  // total = total * (sum + 12) --- 先把右侧算完,再与total计算

② 递增、递减运算符

1、形式:++--。是单目运算符。
2、如:

count ++;  // count += 1 ;即 count = count + 1
a++;	// a++的值是a加1以前的值
++a;	//++a 的值是加了1以后的值

3、案例程序

#include <stdio.h>

int main()
{
    int a=10;

    printf("a++ =%d\n",a++);
    printf("a = %d\n",a);

    printf("++a = %d\n",++a);
    printf("a = %d\n",a);
    
	return 0;
}

C语言基础知识【即将完结,建议收藏】_第9张图片

6、程序案例——逆序的三位数

(1)题目内容:
逆序的三位数:
程序每次读入一个正三位数,然后输出逆序的数字。注意,当输入的数字含有结尾的0时,输出不应带有前导的0。比如输入700,输出应该是7。
提示:用%10可以得到个位数,用/100可以得到百位数…。将这样得到的三个数字合起来:百位100+十位10+个位,就得到了结果。

【代码】:

#include <stdio.h>

int main()
{
    int a;

    printf("请输入一个三位数:");
    scanf("%d",&a);

    printf("逆序数 = %d\n", (a%10)*100+(((a-(a/100)*100)/10)*10)+a/100);
        // 得到十位数的方法也可以是:① a%100,再/10 ;或 ② a/10 再 %10
	return 0;
}

C语言基础知识【即将完结,建议收藏】_第10张图片
C语言基础知识【即将完结,建议收藏】_第11张图片

二、判断与循环

1、判断

(1)引例程序

#include <stdio.h>

int main()
{
	int hour1,minute1;
    int hour2,minute2;
    int ih,im;

    scanf("%d %d",&hour1,&minute1);
    scanf("%d %d",&hour2,&minute2);

    ih = hour2 - hour1;
    im = minute2 - minute1;

    if(im<0){
        im = 60+im;
        ih --;
    }

    printf("时间差是%d小时%d分", ih, im );  
    
	return 0;
}

C语言基础知识【即将完结,建议收藏】_第12张图片
运行结果正确。

(2)C语言提供了六个关系运算符:

	==	相等
	!=	不相等
	>	大于
	>=	大于或等于
	<	小于
	<=	小于或等于

Note:
关系运算的结果只有2种:关系成立,结果是1;否则,是0.

① 优先级

1、所有的关系运算符的优先级比算术运算的低,但比赋值运算的高。

    printf("%d", 7>=3+4 );  	// 运行结果 = 1

2、判断是否相等的==!=的优先级比其他的低,而连续的关系运算是【从左到右】结合的。

    printf("%d", 6>5>4 );  	// 运行结果 = 0
    printf("%d", 5>3 == 6>4 );  	// 运行结果 = 1
    printf("%d", a == b == 6 );  	//从左到右

(3)找零计算器——优化

#include <stdio.h>

int main()
{
    // 初始化
	int price = 0;
    int bill = 0;
    //读入金额和票面
    printf("请输入金额:");
	scanf("%d", &price);
	printf("请输入票面:");
	scanf("%d", &bill);
	//	计算找零
    if(bill>=price)
	    printf("应该找您:%d\n", bill - price);  
    else
        printf("你的钱不够\n");
        
	return 0;
}

(4)计算薪水:

#include <stdio.h>

int main()
{
	const double RATE = 8.25;  // 每小时的薪水
	const int STANDARD = 40;   // 一周的标准工作时间
	double pay = 0.0;
	int hours;

	printf("请输入工作的小时数: ");
	scanf("%d", &hours);
	printf("\n");
	if (hours > STANDARD)
   		pay = STANDARD * RATE + (hours-STANDARD) * (RATE * 1.5);
	else
   		pay = hours * RATE;
	printf("应付工资: %f\n", pay);

	return 0;
}

(5)判断成绩

#include <stdio.h>

int main()
{
	const int PASS=60;
	int score;

	printf("请输入成绩: ");
	scanf("%d", &score);
	
	printf("你输入的成绩是%d.\n", score);
	if ( score < PASS )
		printf("很遗憾,这个成绩没有及格。");
	else {
		printf("祝贺你,这个成绩及格了。");
		printf("再见\n");
	}

	return 0;
}

2、循环

(1)判断数字的位数

① 四位数及以下:

#include<stdio.h>

int main()
{
    int x;
    int n = 1;

    scanf("%d",&x);

    if(x>999)
        n = 4;
    else if (x > 99)
        n = 3;
    else if(n > 9)
        n = 2;
    else
        n = 1;
    printf("%d\n",n);

    return 0;
}

② 优化 —— 循环语句

#include<stdio.h>

int main()
{
    int x;
    int n = 0;

    scanf("%d",&x);
    n++;
    x /= 10;

    while (x > 0){
        n++ ;
        x /= 10;
    }
    printf("%d\n",n);
    
    return 0;
}

【Note】:程序中, ++n; x/=10;不能全写到while循环语句中。因为x = 0 时,n应该 = 1。

(2)do-while 循环

	do
	{
		<循环体语句>
	}while(<循环条件>);

【note】:
1、无论条件满不满足,do-while一定会做一遍;而while在条件不满足时,什么都不做。

③ do-while 循环进行优化

#include<stdio.h>

int main()
{
    int x;
    int n = 0;

    scanf("%d",&x);
    do
    {
        x /= 10;
        n++ ;
    } while(x > 0);
    printf("%d\n",n);

    return 0;
}

(3)for 循环

① 阶乘 运算

1、while 循环
#include<stdio.h>

int main()
{
    int n;
    int fact = 1;
    int i = 1;

    scanf("%d",&n);

    while(i <= n){
        fact *= i;
        i++;
    }
    printf("%d!=%d\n",n,fact);

    return 0;
}
2、for 循环
#include<stdio.h>

int main()
{
    int n;
    int fact = 1;
    int i = 1;

    scanf("%d",&n);

    /* 从 1 乘到 n */
    //for(i=1; i<=n; i++ ){  // ① 初始条件;② 循环条件;③ 增加步长
    //    fact *= i;
    //}

    for(i=n; i>1; i--){ // 从 n 乘到 1
        fact *= i;
    }
    printf("%d!=%d\n",n,fact);

    return 0;
}

for 语句也可写成如下,但只有C99编译能使用;

for( int i=1; i<=n; i++ ){  
        fact *= i;
    }

【note】:for 语句中的每个表达式都是可以省略的;但是分号不能省略!而

	for(;条件;) == while(条件)

(4)三种循环选择 tips:

  • 如果有固定次数,用 for
  • 如果必须执行一次,用do_while
  • 其他情况用while

三、进一步的循环与判断

1、逻辑类型和运算

(1)bool 类型

头文件:

	#include<stdbool.h>

之后就可以使用bool 和 true 、false

(2)逻辑运算

  • 逻辑运算的结果只有0 或 1 ;
  • 运算符:逻辑非;&&逻辑与;||逻辑或;
  • 优先级! > && > ||
  • 如:x∈[4,6],写出x的表达式
	!age < 20;	// 单目运算符的优先级高于双目运算符,故 !age 只会 = 0 或1;一定 < 20.

a. 优先级总结

C语言基础知识【即将完结,建议收藏】_第13张图片

  • 赋值永远最低; and 比 or 高一点;
  • 关系运算比逻辑运算还要高

(3)条件运算符

	count = (count > 20)?count-10:count
	// 条件?条件满足时的值:条件不满足时的值
  • 条件运算符优先级高于赋值运算符,但低于其它的运算符;
  • 条件运算符是自右向左结合的
  • 最好别用嵌套的条件运算表达式,可读性差!

(4)逗号运算符

  • 用来连接两个表达式,并以其右边的表达式的值作为它的结果。
  • 逗号运算符的优先级是最低的。
  • 逗号自左向右结合
  • 常在 for中使用
	for ( i=0,j=10; i

2、级联和嵌套的判断

(1)嵌套的 if-else

eg. 程序 —— 三个数大小的比较

#include<stdio.h>
#include<stdbool.h>

int main(){
    int a,b,c,max;
    scanf("%d %d %d",&a,&b,&c);

    if(a>b){
        if(a>c)
            max = a;
        else
            max = c;
    }
    else{
        if(b>c)
            max = b;
        else
            max = c;
    }
    printf("The max is %d\n",max);
    
    return 0;
}

【Note】:

  1. 缩进格式不能暗示else的匹配;
  2. tips: 即使只有一条语句,if 和 else 后面也要用大括号{ }!易于理解!

(2)级联的 if-else

eg. 分段函数

    int f;
    if(x < 0){
        f = -1;
    }else if(x == 0){
        f = 0;
    }else{
        f = x * 2;
    }

3、多路分支

(1)if - else if - else……

#include<stdio.h>

int main(){
    int type;
    scanf("%d",&type);
  
    if(type == 1)
        printf("你好");
    else if(type == 2)
        printf("早上好");
    else if(type == 3)
        printf("晚上好");
    else if(type == 4)
        printf("再见");
    else
        printf("啊,什么呀?");
    
    return 0;
}

(2)switch-case

#include<stdio.h>

int main(){
    int type;
    scanf("%d",&type);
  
    switch (type){
    case 1:             // type == 1 时
        printf("你好");
        break;
    case 2:
        printf("早上好");
        break;
    case 3:
        printf("晚上好");
        break;
    case 4:
        printf("再见");
        break;
    default:
        printf("啊,什么呀?");
        break;
    }
    
    return 0;
}

【Note】:

	switch(控制表达式){
	case 常量:
		语句
		……
	case 常量:
		语句
		……
	default:
		语句
		……
	}
  1. 控制表达式只能是整数型int的结果;
  2. 常量可以是常数,也可以是常数计算的表达式;
  3. 遇到break才会跳出;

4、循环的程序案例

(1)循环计算 —— 计算log 以 2 为底,x 的对数

#include<stdio.h>

/* 计算log 以 2 为底,x 的对数*/
int main(){
    int x,t;
    int ret = 0;
    scanf("%d",&x);

    t = x;
    while( t > 1 ){
        t /= 2;
        ret ++;
    }
   
    printf("log2 of %d is %d.\n", x, ret);
      
    return 0;
}

(2)算平均数

【要求】:输入一系列正整数,最后输入 -1 表示输入结束,然后计算这些数字的平均数,输出输入的数字的个数和平均数。
【思路】:

  • 变量 —> 算法 —> 流程图 —> 程序
    【代码】:
#include<stdio.h>

/* 计算平均数 */
int main(){
    int number;
    int sum = 0;
    int count = 0;

    scanf("%d",&number);
    while(number != -1){
        sum += number;
        count ++;
        scanf("%d",&number);
    }

/*    do{
        scanf("%d",&number);
        if(number != -1){
            sum += number;
            count++;        // count 表明从程序中读入了多少个数据;
        } 
    }while (number != -1);
*/
    printf("输入的%d个数的平均数 = %f\n", count, 1.0*sum / count);
      
    return 0;
}

(3)猜数游戏

【需求】:计算机想一个数,让用户来猜。用户每输入一个数据,就告诉他大了还是小了,直到用户猜中为止。最后还要告诉他猜了多少次。
【思路】:

  1. 计算机随机想一个数,记在变量number中;
  2. 负责计数的变量 count 初始化为0;
  3. 让用户输入一个数字a;
  4. count++;
  5. 判断number与 a 的大小关系,如果 a 大,就输出“大”;如果 a 小,就输出 a “小”;
  6. 重复循环
  7. 否则,输出 “猜中” 和次数,结束。

【代码】:

#include<stdio.h>
#include<stdlib.h>
#include<time.h>

/* 猜数游戏 */
int main(){
    srand(time(0));
    int number = rand()%100+1;  // 每次 召唤 rand() 就得到一个随机的整数;
                                // x % n 的结果是 [0,n-1] 的一个整数;
    int a = 0;
    int count = 0;

    printf("我已经想好了一个 1-100 之间的数。\n");
    do{
        printf("请猜这个 1-100 之间的数:");
        scanf("%d",&a);
        count ++;
        if (a > number){
            printf("你猜的数大了。");
        }else if(a < number){
            printf("你猜的数小了。");
        }
    }while (a != number);

    printf("太好了,你用了%d次就猜到了答案。\n", count);
      
    return 0;
}

【Note】:

  1. x % n的结果是 [0,n-1] 的一个整数;
  2. 每次 召唤rand() 就得到一个随机的整数;

【运行结果】:
C语言基础知识【即将完结,建议收藏】_第14张图片

(4)整数求逆

【思路】:

  • 对一个整数做%10的操作,就可的到 个位数;
  • 对一个整数做/10的操作,就去掉了个位数
  • ……

【代码】:

#include<stdio.h>

/* 整数求逆 */
int main(){
    int x,t;
    int digit;
    int ret = 0; // 初始时结果为0
    
/* 此时若输入为 700,则输出为 7 */
    scanf("%d",&x);
    t = x;
    while(x > 0){
        digit = x%10;
        // printf("%d\n",digit);
        ret = ret *10 + digit;
        printf("x=%d, digit=%d, ret=%d\n", x, digit, ret);
        x /= 10;
    }
    printf("整数%d的逆=%d。\n", t,ret);
      
    return 0;
}

C语言基础知识【即将完结,建议收藏】_第15张图片

/* 此时若输入为 700,则输出为 007 */
    scanf("%d",&x);
    t = x;
    while(x > 0){
        digit = x%10;
        printf("%d",digit);
        ret = ret *10 + digit;
        //printf("x=%d, digit=%d, ret=%d\n", x, digit, ret);
        x /= 10;
    }

四、循环控制

1、循环控制

(1)例程:判断一个数是不是素数

【需求】:读入一个数x,判断x是不是素数。
素数:只能被 1 和自己整除的数,不包括1.

【代码】:

#include<stdio.h>

/* 判断是否是素数 */
int main(){
    int x;
    int i;
    int isPrime = 1;    // isPrime = 1,则 x 是素数
    scanf("%d",&x);

    for (i=2; i<x; i++){
        if( x % i == 0){     // 说明不是素数
            isPrime = 0;
            break;          //break; --- 跳出循环  ; continue; --- 跳过这一轮循环,进入下一轮
        }
    }
    if (isPrime == 1){
        printf("是素数。\n");
    } else {
        printf("不是素数。\n");
    }
     
    return 0;
}

【Note】:

  • break; ---- 跳出循环;
  • continue; ---- 跳过这一轮循环中剩下的语句,进入下一轮

【或】:

/* 判断是否是素数 */
int main(){
    int x;
    int i;
//    int isPrime = 1;    // isPrime = 1,则 x 是素数
    scanf("%d",&x);

    for (i=2; i<x; i++){
        if( x % i == 0){     // 说明不是素数
//            isPrime = 0;
            break;          //break; --- 跳出循环  ; continue; --- 跳过这一轮循环,进入下一轮
        }
    }
//    if (isPrime == 1){
    if (i == x){
        printf("是素数。\n");
    } else {
        printf("不是素数。\n");
    }
  • 优点:节省了程序变量的内存消耗。
  • 缺点:程序有两个要求,一是要机器能读,二是人要能读。这种聪明的做法,逻辑不清晰,其他人读程序时会耗费更多的脑细胞,不利于后期程序移植、维护和团队合作。

2、多重循环

(1)例程:输出100以内的素数

【代码】:

#include<stdio.h>

/* 输出 1-100 的素数 */
int main(){
    int x;
    int i;
    int isPrime = 1;    // isPrime = 1,则 x 是素数
    //scanf("%d",&x);
    x = 6;

    for (x=2; x<100; x++)
    {
        for (i=2; i<x; i++){
            if( x % i == 0){     // 说明不是素数
                isPrime = 0;
                break;          //break; --- 跳出循环  ; continue; --- 跳过这一轮循环,进入下一轮
            }
        }
        if (isPrime == 1){
            printf("%d ",x);
        } 
    }
    printf("\n");

    return 0;
}

(2)例程②:输出前50个素数

【代码1】:

#include<stdio.h>

/* 输出前50个素数 */
int main(){
    int x;
    int i;
    int isPrime = 1;    // isPrime = 1,则 x 是素数
    int cnt = 0;        // 计数器
    //scanf("%d",&x);
    x = 2;

//    for (x=2; x<100; x++)
    while (cnt < 50)
    {
        for (i=2; i<x; i++){
            if( x % i == 0){     // 说明不是素数
                isPrime = 0;
                break;          //break; --- 跳出循环  ; continue; --- 跳过这一轮循环,进入下一轮
            }
        }
        if (isPrime == 1){
            printf("%d ",x);
            cnt++;
        } 
        x++;
    }
    printf("\n");

    return 0;
}

【代码2】:

//    for (x=2; x<100; x++)
//    while (cnt < 50)
    for (x=2; cnt<50; x++)
    {
        for (i=2; i<x; i++){
            if( x % i == 0){     // 说明不是素数
                isPrime = 0;
                break;          //break; --- 跳出循环  ; continue; --- 跳过这一轮循环,进入下一轮
            }
        }
        if (isPrime == 1){
            printf("%d ",x);
            cnt++;
        } 
    //    x++;
    }
    printf("\n");

(3)凑硬币 _ 用1角、2角、5角的硬币凑出10元以下的金额

【代码①】:—— 接力 break

#include<stdio.h>

/* 凑硬币 _ 用1角、2角、5角的硬币凑出10元以下的金额 */
int main()
{
    int x;
    int one,two,five;
    int exit = 0;

    scanf("%d",&x);
    for(one=1; one<x*10; one++){    // 1角最多是 x*10个
        for(two=1; two < x*10/2; two++){    // 2角最多是 x*10/2 个
            for(five=1; five < x*10/5;five++){
                if(one + two*2 + five*5 == x*10){
                    printf("可以用%d个1角+%d个2角+%d个5角得到%d元\n",one,two,five,x);
                    exit = 1;
                    break;              /* 【接力 break】*/
                }
            }
            if(exit)    break;  // 相当于 exit ==1;
        }
        if(exit)    break;
    }

    return 0;
}

【Note】:

  • break 和 continue 只能对它所在的那层循环做;

【代码②】:—— goto语句

  • goto 语句非常适合用于,多重嵌套循环的内层循环直接跳到最外面的情形。
#include<stdio.h>

/* 凑硬币 _ 用1角、2角、5角的硬币凑出10元以下的金额 */
int main()
{
    int x;
    int one,two,five;
    int exit = 0;

    scanf("%d",&x);
    for(one=1; one<x*10; one++){    // 1角最多是 x*10个
        for(two=1; two < x*10/2; two++){    // 2角最多是 x*10/2 个
            for(five=1; five < x*10/5;five++){
                if(one + two*2 + five*5 == x*10){
                    printf("可以用%d个1角+%d个2角+%d个5角得到%d元\n",one,two,five,x);
                    goto out;
                }
            }
        }
    }
out:
    return 0;
}

3、循环应用案例

(1)求前 n 项和

① f(n)=1+1/2+1/3+…1/n

【代码】:

#include<stdio.h>

/* 求前 n 项和 —— 1+1/2+1/3+…1/n */
int main()
{
    int n;
    int i;
    double sum = 0.0;

    scanf("%d",&n);
    for (i=1; i<=n; i++){
        sum += 1.0/i;
    }
    printf("f(%d)=%f\n",n,sum);

    return 0;
}

② f(n)=1-1/2+1/3-1/4+…+1/n

#include<stdio.h>

/* 求前 n 项和 _f(n)=1-1/2+1/3-1/4+…+1/n */
int main()
{
    int n;
    int i;
    double sum = 0.0;
    int sign = 1;

    scanf("%d",&n);
    for (i=1; i<=n; i++){
        sum += sign*1.0/i;
        sign = -sign;
    }
    printf("f(%d)=%f\n",n,sum);

    return 0;
}

【或】:直接把 sign 定义为 double 类型
主程序:

    int n;
    int i;
    double sum = 0.0;
    // int sign = 1;
    double sign = 1.0;

    scanf("%d",&n);
    for (i=1; i<=n; i++){
        sum += sign/i;
        sign = -sign;
    }
    printf("f(%d)=%f\n",n,sum);

(2)求最大公约数

法①:枚举

【思路】:

  1. 设 t 为2;
  2. 如果 u 和 v 都能被 t 整除,则记下这个 t;
  3. t 加1后重复第二步,直到 t 等于 u 或 v;
  4. 那么,曾经记下的最大的可以同时整除 u 和v 的 t 就是 gcd。

【代码】:

#include<stdio.h>

/* 求最大公约数 */
int main()
{
    int a,b;
    int min;
    int ret;
    int i;

    scanf("%d %d",&a,&b);
    if (a<b){
        min = a;
    }else {
        min = b;
    }
    for (i=1;i<min;i++){        // 从 1 开始,到 a 和 b 的最小值
        if (a%i == 0){          // a 能被 i 整除
            if ( b%i == 0){     // b 能被 i 整除
                ret = i;    // 目前的公约数
            }
        }
    }
    printf("%d和%d的最大公约数是%d\n",a, b, ret);

    return 0;
}

法②:辗转相除法

【思路】:

  1. 如果b等于0,计算结束,a 就是最大公约数;
  2. 否则,计算 a 除以 b 的余数,让 a 等于 b ,而 b 等于那个余数;
  3. 回到第一步;
    eg.
	a	b	t	// t—— 余数
	12	18	12
	18	12	6
	12	6	0
	6	0

【代码】:

#include<stdio.h>

/* 求最大公约数 */
int main()
{
    int a,b;
    int t;
    scanf("%d %d",&a, &b);

    while ( b!=0)
    {
        t = a%b;
        a = b;
        b = t;
        printf("a=%d,b=%d,t=%d\n",a, b, t);
    }
    printf("gcd=%d.\n", a);

    return 0;
}

【运行结果】:
C语言基础知识【即将完结,建议收藏】_第16张图片

(3)正序分解整数

  • 输入一个非负整数,正序输出它的每一位数字;
  • 输入:13425
  • 输出:1 3 4 2 5

法①:先逆序,再逆序

【代码】:

#include<stdio.h>

/* 求最大公约数 —— 先逆序,再逆序,—— 只适用于末尾不是0的数*/
int main()
{
    int x,d;
    int t = 0;

    scanf("%d",&x);
    do{             /* 做【逆序】 */
        d = x%10;
        t = t*10 + d;
        x /= 10;    // x 右移一位
    }while (x>0);
    printf("x=%d,t=%d\n",x,t);
    x = t;
    do{             /* 【再逆序】 */
        int d = x%10;   // 得到最右边的一位
        printf("%d",d);
        if (x>=10){
            printf(" ");    // 使最后一轮时不输出空格
        }
        x /= 10;        // x 右移一位
    } while( x>0 );
    printf("\n");

    return 0;
}

【运行结果】:
C语言基础知识【即将完结,建议收藏】_第17张图片

法②:正序输出

【思路】:

eg.
		x = 13425;
		13425 / 10000	--> 1
		13425 % 10000	--> 3425
		10000 / 10		--> 1000
		3425 / 1000	 -->3
		3425 % 1000	 -->435
		1000 / 10	 -->100
		425 / 100  -->4
		425 % 100  -->25
		100 /10	   -->10
		25 / 10	-->2
		25 % 10	-->5
		10 /10	-->1
		5 / 1 -->5
		5 % 1 -->5
		1 /10 -->0
	 		

【代码】:

#include<stdio.h>

/* 求最大公约数 —— 正序输出 */
int main()
{
    int d,x;
    int mask = 1;
    int t = 0;
    int cnt = 0;
    x = 13425;

    t = x;
    while (x > 9){                 // 可以知道 x 的位数
        x /= 10;
        mask *= 10;
        //cnt++;
    } 
    printf("mask = %d\n",mask);
    // mask = pow(10,cnt-1);   // mask=10^(cnt-1)
    do{
        d = t / mask;
        printf("%d",d);
        if( mask >9 ){
            printf(" ");
        }
        t %= mask;
        mask /= 10;
        // printf("x=%d,mask=%d,d=%d\n",x,mask,d);
    }while ( mask >0 );
    printf("\n");

    return 0;
}

五、数组与函数

1、数组:

(1)引例:如何写一个程序计算用户输入的数字的平均数

① 不需要记录输入的每一个数

#include<stdio.h>

int main(){
	int x;
	double sum = 0;
    int cnt = 0;
    scanf("%d",&x);	// 读入x
    while(x != -1){
        sum += x;
        cnt ++;
        scanf("%d",&x);	// 读下一个数
    }
    if(cnt > 0 ){
        printf("%f\n",sum / cnt);
    }

    return 0;
}

② 计算输入的平均数,并输出所有大于平均数的数

【思路】:

  • 把输入的数都存下来
  • 等输入完成后,算出平均数
  • 再来判断每个数该不该输出

【如何记录很多的数】:

	int num1,num2,num3……?

【数组】:

	int number[100];
	scanf("%d",&number[i]);

【代码】:

#include<stdio.h>

int main(){
	int x;
	double sum = 0;
    int cnt = 0;
    int number[100];    // 定义名为number的数组:数组里的每个单元都是 int;数组大小是100个。
    scanf("%d",&x);
    while(x != -1){
        number[cnt] = x;	// 对数组中的元素赋值 
        //
        {
        	int i;
        	for ( i=0; i<=cnt; i++){
        		printf("%d\t",number[i]);
			}
			printf("\n");
		}
		// 
        sum += x;
        cnt ++;
        scanf("%d",&x);
    }
    if( cnt > 0 ){
        // printf("%f\n", sum/cnt);
        int i;
        double average = sum/cnt;
        for( i=0; i<cnt; i++ ){
            if (number[i] > average) {	// 使用数组中的元素 
                printf("%d",number[i]);		// 遍历数组中的元素 
            }
        }
    }

    return 0;
}

(2)定义数组

	<类型> 变量名称[元素变量];
	如:	int grades[100];
		double weight[20];

【Note】:

  • 元素数量必须是整数;
  • C99之前:元素数量必须是编译时刻确定的字面量
  • 长度为0的数组——可以存在,但是无用 int a[0];

【数组的特点】:

  • 所有元素具有相同的数据类型;
  • 一旦创建,不能改变大小;
  • *(数组中的元素在内存中是连续依次排列的)
  • 可以出现在赋值的左边或右边;
  • 在赋值 左/右 边叫做 左/右 值
  • 数组的每个单元就是数组类型的一个变量;
  • 使用数组时,放在[]中的数字叫做 下标或索引,下标从0开始计数;
	int a[10];
	// 10个单元: a[0],a[1],……,a[9]
	a[2] = a[1] + 6;	// 把a[1]的值读出来,加上6后,写入到a[2]中去;

【注意】:
有效的下标范围:

  • 一旦程序运行,越界的数组访问可能造成问题,导致程序崩溃
  • segmentation fault

【案例程序】:

#include<stdio.h>

void f();

int main()
{
	f();

	return 0;
 } 
 
 void f()
 {
 	int a[10];
 	a[10] = 0;	// a[10] 不是有效下标 
 }
程序优化:

问题:之前的程序是危险的,因为输入的数据可能超过100个。
优化:如果先让用户输入有多少数字要计算,可以用C99的新功能来实现。

C语言基础知识【即将完结,建议收藏】_第18张图片

(3)用数组做散列计算

【案例】:
写一个程序,输入数量不确定的 [0,9] 范围内的整数,统计每一种数字出现的次数,输入 -1 表示结束

【程序】:

#include<stdio.h>

int main(void)
{
	const int  number = 10;		// 数组的大小 
	int x;
	int count[number];	// count[i] 说明 i 这个数字出现了多少次 
	int i;
	
	for( i=0; i<number; i++) {	// 初始化数组 
		count[i] = 0;
	}
	scanf("%d",&x);
	while ( x!= -1 ){
		if ( x>=0 && x<=9 ){
			count[x] ++; 	// 运算 
		}
		scanf("%d",&x);
	}
	for ( i=0; i<number; i++){	// 遍历数组 
		printf("%d:%d\n", i, count[i] );	// i 这个数字出现了多少次 
	}
	return 0;
}

2、函数的定义与使用

(1)求素数的和

#include<stdio.h>

int main(void)
{
	int m,n;
	int sum = 0;
	int cnt = 0;
	int i;
	
	scanf("%d %d",&m,&n);
	// m=10,n=31;
	if(m==1) m=2;
	for ( i=m; i<=n; i++){	//判断 i是不是素数
		int isPrime = 1;
		int k;
		for(k=2; k<i-1;k++){
			if(i%k == 0){
				isPrime = 0;
				break;
			}
		}
		if(isPrime){
			sum += i;
			cnt++;
		}
	}
	printf("%d %d\n",cnt,sum);
	
	return 0;
}

优化:调用函数

#include<stdio.h>

int isPrime(int i )		// 定义的函数
{
	int ret = 1;
	int k;
	for(k=2; k<i-1;k++){
		if(i%k == 0){
			ret = 0;
			break;
		}
	}
	return ret;
}

int main(void)
{
	int m,n;
	int sum = 0;
	int cnt = 0;
	int i;
	
	scanf("%d %d",&m,&n);
	// m=10,n=31;
	if(m==1) m=2;
	for ( i=m; i<=n; i++){	//判断 i是不是素数		
		if(isPrime(i)){
			sum += i;
			cnt++;
		}
	}
	printf("%d %d\n",cnt,sum);
	
	return 0;
}

(2)求和:求出1到10、20到30和35到45的三个和

#include<stdio.h>
int main()
{
    int i;
    int sum;

    for( i=1,sum=0; i<=10; i++ ){
        sum += i;
    }
    printf("%d到%d的和是%d\n",1,10,sum);

    for( i=20,sum=0; i<=30; i++ ){
        sum += i;
    }
    printf("%d到%d的和是%d\n",20,30,sum);

    for( i=35,sum=0; i<=45; i++ ){
        sum += i;
    }
    printf("%d到%d的和是%d\n",35,45,sum);

    return 0;
}

注意:代码复制 是程序质量不良的表现。

优化:求和函数

#include<stdio.h>
void sum(int begin, int end)
{
    int i;
    int sum = 0;
    for( i=begin; i<=end; i++){
        sum += i;
    }
    printf("%d到%d的和是%d\n",begin,end,sum);
}

int main()
{
    sum(1,10);
    sum(20,30);
    sum(35,45);

    return 0;
}

(3)什么是函数?

函数是一块代码,接收零个或多个参数,做一件事情,并返回零个或一个值。

  1. void sum(int begin, int end)—— 函数头
  2. {}里面的 —— 函数体
  3. sum—— 函数名
  4. void—— 返回类型 void类型不返回结果
  5. ()里的 —— 参数表
① 调用函数
  • 函数名(参数值);
  • ()起到了表示函数调用的重要作用
  • 即使没有参数,也需要()
  • 函数知道每一次是哪里调用它,会返回到正确的地方。

(4)从函数中返回

例一:

上述 素数求和 例子中:

int isPrime(int i )		// 定义的函数
{
	int ret = 1;
	int k;
	for(k=2; k<i-1;k++){
		if(i%k == 0){
			ret = 0;
			break;
		}
	}
	return ret;
}

注意:

  • 如果函数要返回一个结果,那就需要用return把那个结果交给调用它的地方。
  • 上述函数,isPrime会返回一个int类型的结果
例二:
int max(int a, int b)
{
    int ret;
    if (a>b){
        ret = a;
    }else (
        ret = b;
    )

    return ret;
}

或者:(但最好用上面那种!)

int max(int a, int b)
{
    // int ret;
    if (a>b){
        return a;
    }else {
        return b;
    }

    // return ret;
}

第二种虽然没有错,但是不符合单一出口的理念;

注意:

  • 一旦遇到return,就会 ①停止函数的执行,②并送回一个值;

  • 两种写法:

  • return;

  • return 表达式;

  • 一个函数里面可以出现多个return语句

如:

#include<stdio.h>

int max(int a, int b)
{
    int ret;
    if (a>b){
        ret = a;
    }else {
        ret = b;
    }

    return ret;
}

int main()
{
    int a,b,c;
    a = 5;
    b = 6;
    c = max(10,12);
    c = max(a,b);
    c = max(c, 23);
    printf("%d\n",max(a,b));

    return 0;
}
① 没有返回值的函数:
  • void 函数名(参数表);
  • 不能使用带值的return
  • 可以没有return
  • 调用的时候不能做返回值的赋值
  • 注意:如果函数有返回值,则必须使用带值的return

3、函数的参数和变量

(1)函数原型

  • 要把函数写在main()上面 —— 因为C的编译器自上而下顺序分析代码!
  • 或者,为了更直观,可以这样改动:
#include<stdio.h>

void sum(int begin, int end);   // 函数的【原型声明declaration】

int main()
{
    sum(1,10);      // 如没有 声明,会猜测 int sum(int,int)
    sum(20,30);
    sum(35,45);

    return 0;
}

void sum(int begin, int end)    // 函数 定义
{
    int i;
    int sum = 0;
    for( i=begin; i<=end; i++){
        sum += i;
    }
    printf("%d到%d的和是%d\n",begin,end,sum);
}
  • 函数头,以分号结尾,就构成了函数的原型;
  • 函数原型的目的是告诉编译器这个函数长什么样①名称、②参数(数量及类型)、③返回类型;
  • 旧标准习惯把函数原型写在调用它的函数里面;
  • 现在一般写在函数前面
  • 原型里可以不写参数的名字,但一般仍然写上;

(2)参数传递

  • 可以传递给函数的值是表达式的结果,这包括
	① 字面量[eg.10]、
	② 变量[eg.a]、
	③ 函数的返回值[eg.max(23,45)]、
	④ 计算的结果[eg.23+45]
  • 如果函数有参数,调用函数时必须传递给它数量、类型正确的值;
  • C语言在调用函数时,只能传值给函数!
  • 每个函数有自己的变量空间,参数也位于这个独立的空间中,和其他的函数没有关系!
  • 过去,对于函数参数表中的参数,叫做“形式参数”;调用函数时给的值,叫做“实际参数”;
  • 现在,我们把形参叫做参数;把实参叫做

(3)本地变量

  • 函数每次运行,就产生一个独立的变量空间,在这个空间中的变量,是函数的这次运行所独有的,称作本地变量
  • 定义在函数内部的变量就是本地变量;
  • 参数也是本地变量;
①变量的生存期和作用域
  • 生存期:什么时候这个变量开始出现了,到什么时候它消亡了;
  • 作用域:在代码的什么范围内可以访问这个变量(这个变量可以起作用);
  • 对于本地变量,这两个问题的答案是统一的:大括号内 —— 块
② 本地变量的规则
  • 本地变量是定义在块内的(大括号内);
		1.它可以是定义在 函数的块 内;
		2.也可以定义在 语句的块 内
  • 程序进入这个块前,其中的变量不存在;
  • 离开这个块,其中的变量就消失了
  • 在块外面定义的变量,在里面仍然有效;
  • 块里面定义了和外面同名的变量,则掩盖了外面的
  • 不能在一个块内定义同名的变量
  • 本地变量不会被默认初始化
  • 参数在进入函数的时候就被初始化了!

(4)其他细节

① 没有参数时
void f(void);	// 在参数表里放了 void,明确的告诉编译器,这个函数不接收任何参数;
	还是?
	× void f();	// 在传统C中,它表示 f函数的参数表未知,并不表示没有参数
② 逗号运算符
  • 调用函数时,逗号和逗号运算符怎么区分?
  • 调用函数时的圆括号里的逗号是标点符号,不是运算符
	f(a,b)		// 是标点
	f((a,b))	// 是运算符
	// 二者区别:到底是传了2个还是1个参数进去
③ 函数里的函数
  • C语言不允许函数嵌套定义
  • 可以在一个函数中放另一个函数的【声明】,但不能放另一个函数的【定义】

4、二维数组

(1)二维数组:

	int a[3][5];	// 通常理解为a是一个3行5列的矩阵
① 二维数组的遍历
  • 需要二重循环
  • 外面的一层遍历行号;里面的一层遍历列号
  • 如:
for ( i=0; i<3; i++){
    for ( j=0; j<5; j++ ){
        a[i][j] = i*j;
    }
}
  • a[i][j]表示第 i 行第 j 列上的单元
  • a[i,j]是一个表达式

(2)二维数组的初始化

	int a[][5] = {
	    {0,1,2,3,4},	// 看做 5个int的数组,作为 a[0];
    	{2,3,4,5,6},	// 5个int的数组,作为a[1];
	};
  • 列数是必须给出的,行数可以由编译器来数;
  • 每行一个{},逗号分隔
  • 如果有省略的,表示补零
  • 也可以用定位( * C99 ONLY)

(3)tic-tac-toe游戏(井字棋)

  • 读入一个3×3的矩阵,矩阵中的数字为1表示该位置上有一个X,为0表示为O;
  • 程序判断这个矩阵中是否有获胜的一方,输出表示获胜一方的字符X或O,或输出无人获胜;
    const int size = 3;
    int board[size][size];  // 定义3×3 的棋盘
    int i,j;
    int numOfX;
    int numOfO;
    int result = -1;    // -1:没人赢,1:X赢,0:O赢

    // 读入矩阵
    for ( i=0; i<size; i++ ){
        for( j=0; j<size; j++){
            scanf("%d", &board[i][j]);  // 读入数组中的每一个元素
        }
    }

    // 检查行
    for ( i=0; i<size && result == -1; i++){
        numOfO = numOfX =0;             // 初始化最开始的数量为0
        for( j=0; j<size; j++){         // 判断每一列
            if( board[i][j] == 1){      //行号不变,列号从0到size
                numOfX ++;
            }else{
                numOfO ++;
            }
        }
        if( numOfO ==size ){
            result =0;
        }else if(numOfX == size){
            result = 1;
        }
    }

    // 检查列
    for ( result == -1 ){
        for( j=0; j<size && result == -1; j++){         
            numOfO = numOfX =0;
            for( i=0; i<size; i++){
                if( board[i][j] == 1){     
                    numOfX ++;
                }else{
                    numOfO ++;
                }
            }  
        }
        if( numOfO ==size ){
            result =0;
        }else if(numOfX == size){
            result = 1;
        }
    }

    // 检查对角线。。。。

六、数组运算

1、数组运算

(1)引例:

  • 在一组给定的数据中,如何找出某个数据是否存在?

【代码】:
C语言基础知识【即将完结,建议收藏】_第19张图片
C语言基础知识【即将完结,建议收藏】_第20张图片

  • 数组的集成初始化:
	int a[] = {2,4,6,7,1,3,5,9,11,13,23,14,32};
	int a[13] = {2};
	// 2 0 0 0 0 0 0 0 0 0 0 0 0
  • 集成初始化时的定位:
	int a[10] = {
		[0] = 2, [2] = 3, 6,
		};
	// a[0]=2; a[2]=3; a[3]=6; 其它的都为0;

(2)数组的大小

  • sizeof给出整个数组所占据的内容的大小,单位是字节;
	int a[] = {[1]=2,4,[5]=6};	// 0 2 4 0 0 6
	
	printf("%lu\n",sizeof(a));	// 24
	printf("%lu\n",sizeof(a[0]));	// 4
  • 所以,数组的大小= sizeof(a)/ sizeof(a[0])
  • sizeof(a[0])给出数组中单个元素的大小,于是相除就得到了数组的单元个数;

(3)数组的赋值

	int a[] = {[1]=2,4,[5]=6};	// 0 2 4 0 0 6
(False):  int b[] = a;	// 大错特错!!!!不行!!!!
  • 数组变量本身不能被赋值;
  • 要把一个数组的所有元素交给另一个数组,必须采用遍历
	int a[] = {[1]=2,4,[5]=6};	// 0 2 4 0 0 6
	
	for ( i=0; i<length; i++) {
		b[i] = a[i];
	}
  • 通常都是使用 for循环
  • 常见错误——要避免:
  • ① 循环结束条件是<=数组长度; ×××
  • ② 离开循环后,继续使用 i 的值来做数组元素的下标!×××
  • 注意:数组作为函数参数时,往往必须再用另一个参数来传入数组的大小!
  • 因为数组作为函数参数时:① 不能在 [ ] 中给出数组的大小; ② 不能再利用 sizeof 来计算数组的元素个数!

2、搜索

  • 在一个数组中找到某个数的位置(或确认是否存在);
  • 基本方法:遍历

(1)线性搜索 —— 例子①:

#include<stdio.h>

int search(int key, int a[], int len)   // len 来表示数组有多大
{
    int ret = -1;
    for ( int i=0; i<len; i++)
    {
        if (key == a[i] )
        {
            ret = i;
            break;
        }
    }
    return ret;
}

int main()
{
    int a[] = {1,3,4,5,12,14,23,6,9,45};
    int r = search(12, a, sizeof(a)/sizeof(a[0]));
    printf("%d\n", r);
    
    return 0;
}

(2)线性搜索 —— 例子②:

#include<stdio.h>

int amount[] = {1,5,10,25,50};
char *name[] = {"penny","nickel","dime","quarter","half-dollar"};

int search(int key, int a[], int len)   // len 来表示数组有多大
{
    int ret = -1;
    for ( int i=0; i<len; i++)
    {
        if (key == a[i] )
        {
            ret = i;
            break;
        }
    }
    return ret;
}

int main()
{
    int k = 25;
    int r = search(k, amount, sizeof(amount)/sizeof(amount[0]));
    if ( r > -1)
    {
        printf("%s\n",name[r]);
    }
    
    return 0;
}
改进:把面额和名字放的比较近
#include<stdio.h>

int amount[] = {1,5,10,25,50};
char *name[] = {"penny","nickel","dime","quarter","half-dollar"};

struct {
    int amount;
    char *name;
} coins[] = {
    {1,"penny"},
    {5,"nickel"},
    {10,"dime"},
    {25,"quarter"},
    {50,"half-dollar"}
};

int search(int key, int a[], int len)   // len 来表示数组有多大
{
    int ret = -1;
    for ( int i=0; i<len; i++)
    {
        if (key == a[i] )
        {
            ret = i;
            break;
        }
    }
    return ret;
}

int main()
{
    int k = 10;
    // int r = search(k, amount, sizeof(amount)/sizeof(amount[0]));
    for ( int i=0; i<sizeof(coins)/sizeof(coins[0]); i++ )
    {
        if ( k == coins[i].amount ){
            printf("%s\n", coins[i].name);
            break;
        }
    }
    
    return 0;
}

(3)二分搜索

  • 前提:排好序!
  • C语言基础知识【即将完结,建议收藏】_第21张图片

【思路】:
C语言基础知识【即将完结,建议收藏】_第22张图片

【程序】:

#include<stdio.h>

int search(int key, int a[], int len)   // len 来表示数组有多大
{
    int ret = -1;
    int left = 0;
    int right = len-1;
    while ( left < right )
    {
        int mid = (left+right)/2;
        if( a[mid] == k )
        {
            ret = mid;
            break;
        } else if ( a[mid] > k)
        {
            right = mid-1;
        } else{
            left = mid+1;
        }
    }
    return ret;
}

int main()
{
    
    
    return 0;
}

3、排序初步 —— 选择排序

  • 给定无序的数组,如何排列成有序?

【思路】:

  1. 找到最大的数
  2. 最大的数和最后一位做交换,即swap a[maxid],a[len-1];
  3. 再从剩下的当中找到最大的
  4. 再如法炮制
  5. ……
  6. 直到剩下最后的两个数为止

【代码】:

#include<stdio.h>

int max ( int a[], int len)
{
    int maxid = 0;
    for (int i=1; i<len ; i++ )
    {
        if( a[i]> a[maxid] )
        {
            maxid = i;
        }
    }
    return maxid;
}

int main()
{
    int a[] = {2,45,6,12,87,34,90,24,23,11,65};
    int len = sizeof(a)/sizeof(a[0]);

    for ( int i=len-1; i>0 ; i-- )
    {
        int maxid = max(a, i+1);
        // swap a[maxid],a[len-1]
        int t = a[maxid];
        a[maxid] = a[i];
        a[i] = t;
    }
    
    for (int i=0; i<len; i++)
    {
        printf("%d ",a[i]);
    }
    
    return 0;
}

七、指针与字符串

1、指针

(1)取地址运算:&运算符取得变量的地址

sizeof
  • 是一个运算符,给出某个类型或变量在内存中所占据的字节数
#include<stdio.h>

int main()
{
    int a;
    a = 6;
    printf("sizeof(int) = %ld\n", sizeof(int));     // sizeof(int) = 4;int在内存中占4个字节,也就是32个bit
    printf("sizeof(a) = %ld\n", sizeof(a));         // sizeof(a) = 4
    printf("sizeof(double) = %ld\n", sizeof(double));   // sizeof(double) = 8
    
    return 0;
}
② 运算符&
  • scanf("%d",&i);里的&

(2)指针:指针变量就是记录地址的变量

① 指针 —— 就是保存地址的变量
	int i;
	int* p = &i;	// 表示p是一个指针,它指向的是一个 int,把i的地址交给了指针p
	int* p,q;	// 表示p是一个指针;q是一个普通的 int类型的变量
	int *p,q;	// 表示p是一个指针;q是一个普通的 int类型的变量
	//【无论 * 是靠近 int 还是 p,都是一样的!!】

【无论 * 是靠近 int 还是 p,都是一样的!!】

② 指针变量
  • 变量的值是内存的地址
  • 普通变量的值是实际的值;
  • 指针变量的值是具有实际值变量的地址;
③ 作为参数的指针
  • void f(int *p);f 函数要一个int的指针
  • 在被调用的时候得到了某个变量的地址int i=0;f(&i);
  • 在函数里面可以通过这个指针访问外面这个 i
#include<stdio.h>

void f(int *p);
void g(int k);

int main(void)
{
    int i=6;
    printf("&i=%p\n", &i);
    f(&i);
    g(i);
    
    return 0;
}

void f(int *p)
{
    printf(" p=%p\n", p);
}

void g(int k)
{
    printf("k=%d\n", k);		// k = 6
}
④ 访问那个地址上的变量*
  • *是一个单目运算符,用来访问指针的值所表示的地址上的变量
  • 可以做右值,也可以做左值 —— 即*可以放在赋值号的右边读它的值,也可以放在赋值号的左边去写它的值。
#include<stdio.h>

void f(int *p);
void g(int k);

int main(void)
{
    int i=6;
    printf("&i=%p\n", &i);
    f(&i);
    g(i);
    
    return 0;
}

void f(int *p)
{
    printf(" p=%p\n", p);
    printf("*p=%d\n", *p);  // 把 *p 看做一个整体,是int  —— *p = 6;
    *p = 26;        // 此时,k = 26 —— 说明在经历了f函数的调用之后,i的值被改了
}

void g(int k)
{
    printf("k=%d\n", k);
}

(3)指针与数组:为什么数组传进函数后,sizeof不对了?

  • 函数参数表里的数组,实际上是指针!
void minmax( int a[], int len, int *min, int *max)
		// 上面的 int a[] ———— 指针!
		// 也可以写成 int *a;
  • sizeof(a) == sizeof(int*);
  • 但是可以用数组的运算符[]进行运算
  • 以下四种函数原型在参数表中出现,是等价的:
		int sum(int *ar, int n);
		int sum(int *, int);
		int sum(int ar[], int n);
		int sum(int [], int);
① 数组变量是特殊的指针
  • 数组变量本身表达地址,所以:
  1. int a[10];int *p=a; // 无需用 & 取地址
  2. 但是数组的单元表达的是变量,需要用 & 取地址
  3. a == &a[0];
  • []运算符可以对数组做,也可以对指针做:
  1. p[0] <==> a[0]
  • *运算符可以对指针做,也可以对数组做:
	*a = 25;
  • 数组变量是 const 的指针,所以不能被赋值
	int b[]; 	// 可以看做是 int * const b;

2、字符类型

(1)字符类型

  • char 是一种整数,也是一种特殊的类型:字符。

这是因为:

  1. 用单引号表示的字符字面量:‘a’,‘1’
  2. " 也是一个字符;
  3. printf 和scanf 里用 %c 来输入输出字符
① 字符的输入输出:
#include<stdio.h>

int main()
{
    char c;
    char d;
    c = 1;
    d = '1';
    if ( c==d ){            // 不相等
        printf("相等\n");
    } else{
        printf("不相等\n");
    }
    printf("c=%d\n", c);    // c=1
    printf("d=%d\n", d);    // d=49

    return 0;
}
  • 如何输入 ‘1’ 这个字符给 char c ?
	scanf("%c", &c); -->1
	scanf("%d", &i); c=i; -->49
  • ‘1’的ASCII编码是49,所以当 c==49时,它代表’1’;
#include<stdio.h>

int main()
{
    char c;
    //scanf("%c",&c);         // 输入: 1
    //printf("c=%d\n", c);    // c=49
    //printf("c='%c'\n", c);  // c='1'

    int i;
    scanf("%d",&i);         // 输入: 1 or [49]
    c = i;
    printf("c=%d\n", c);    // c=1 or  [49]
    printf("c='%c'\n", c);  // c=' ' or  ['1']

    return 0;
}
② 混合输入:
  • 有何不同?
	scanf("%d %c",&i,&c);
	scanf("%d%c",&i,&c);
  • scanf(“%d %c”,&i,&c); :
#include<stdio.h>

int main()
{
    char c;
    int i;
    scanf("%d %c",&i,&c);
    printf("i=%d, c=%d, c='%c'\n", i, c, c);

    return 0;
}

C语言基础知识【即将完结,建议收藏】_第23张图片

  • scanf(“%d%c”,&i,&c); :
#include<stdio.h>

int main()
{
    char c;
    int i;
    scanf("%d%c",&i,&c);
    printf("i=%d, c=%d, c='%c'\n", i, c, c);

    return 0;
}

C语言基础知识【即将完结,建议收藏】_第24张图片

  • 即在 %d 后面,如果没有空格,它的意思是说,整数只读到整数结束为止,后面那个给下面哪个;
  • 如果是有空格的,整数读到空格以后,还要把后面的空格都读掉!
  • 因而,有没有空格是不一样的!
③ 大小写转换
  • a+'a'-'A'可以把大写字母变成小写字母;
  • a+'A'-'a'小写字母变成大写!

(2)逃逸字符

  • 用来表达无法印出来的控制字符或特殊字符,它由一个反斜杠“/”开头,后面跟上另一个字符,这两个字符合起来,组成了一个字符;
	printf("请分别输入身高的英尺和英寸,"
		"如输入\"5 7\"表示5英尺7英寸:");
字符 意义
\b 回退一格
\t 到下一个表格位
\n 换行
\r 回车
\" 双引号
\' 单引号
\\ 反斜杠本身
\b
  • 回去一格,让下一个输出回到这个位置上去
  • 但是如果不输出东西,那就没什么变化
  • 如果输出东西,那就把刚刚的东西给覆盖掉了
  • 通常就是回去昂,但不删除;
  • 但不可否认,可能有终端软件,运行时会删除
\t
  • 每行的固定位置
  • 一个 \t 使得输出从下一个制表位开始
  • 用 \t 才能使得上下两行对齐

3、字符串

(1)字符串

  • C语言的字符串是以字符数组的形态存在的;
  • 不能用运算符对字符串做运算
  • 通过数组的方式可以遍历字符串
  • 唯一特殊的地方是字符串字面量可以用来初始化字符数组
  • 以及标准库提供了一系列字符串函数
① 字符数组
	char word[] = {'H','e','l','l','o','!'};
	// 但 这不是C语言的字符串,因为不能用字符串的方式做计算!!
	char word[] = {'H','e','l','l','o','!','\0'}; // 是字符串!
② 字符串
  • 以0(整数0)结尾的一串字符
  • 0或’\0’ 是一样的,但是和’0’不同
  • 0标志字符串的结束,但它不是字符串的一部分
  • 计算字符串的长度时不包含这个0
  • 字符串以数组的形式存在,以数组或指针的形式访问。更多的是以指针的形式
  • string.h 里有很多处理字符串的函数
③ 字符串变量
	char *str = "hello";
	char word[] = "hello";
	char line[10] = "hello";	// 占据6个字节的line空间 —— 因为还有个结尾的0
④ 字符串常量
  • “hello”
  • “hello” 会被变成一个字符数组放在某处,这个数组的长度是6,结尾还有表示结束的0;
  • 两个相邻的字符串常量会被自动连接起来;

(2)字符数组

4、字符串计算

你可能感兴趣的:(C语言,c语言,算法,c++)