Shiroki初号机

Self-study | wanna be a game developer

[程序设计入门——C语言]笔记,代码-2021.08.20

程序设计入门——C语言 from Shiroki

1.1 计算机和编程语言

计算机语言:程序是用特殊的编程语言洗出来表达如何解决问题的。不是用编程语言和计算机交谈,而是描述它如何做事情的过程或方法。
算法:我们让计算机做计算,就需要像这样找出计算的步骤,然后用编程语言写出来。计算机做的所有事情都叫做计算。
程序的执行:a.解释:接触一个程序,那个程序能试图理解你的程序,然后按照你的要求执行。b.编译:借助一个程序,就像一个翻译,把你的程序翻译成计算机真正能懂的语言(机器语言)写的程序,然后这个机器语言写的程序就能直接执行了。
解释语言VS编译语言:语言本无编译/解释之分,只有常用的执行方式而已。区别在于,解释型语言有特殊的计算能力,但编译型语言有确定的运算性能。

编程语言排名:https://www.tiobe.com/index.php/content/paperinfo/tpci/index.html

1.2 C语言

C语言-B语言-BCPL语言-Fortran
C语言的发展-K&R,也叫经典C
C的发展和版本
1989-ANSI发布第一个标准:ANSI C
1990-ISO接受ANSI标准:C 89
C的标准在1995年和1999年两次更新:C95和C99
所有当代编译器都支持C99
C语言用在哪里?:操作系统,嵌入式系统,驱动系统,底层系统,图形引擎,图像处理,声音效果
C:编译→运行:需要编辑器,编译器或者IDE(集成开发环境)

1.3第一个程序

使用的软件:Dev C++
第一个C程序:Hello World!

#include <stdio.h>
int main()
{
    printf("Hello World!\n");

    return 0;
}

程序框架

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

    return 0;
}

①相加

#include <stdio.h>
int main()
{
    printf("23+45=%d\n",23+45);

    return 0;
}

②计算找零

#include <stdio.h>
int main()
{
    int price = 0;
    printf("请输入支付的金额(元):");
    scanf("%d",&price);

    int change = 100 - price;
    printf("找零%d元。\n",change);

    return 0;
}
2.1 计算:变量

变量:变量是一个保存数据的地方,当我们需要在程序里保存数据时,就需要一个变量来保存它。用一个变量保存了数据,他才能参加到后面的计算中,比如计算找零。
变量定义:变量定义的一般形式就是:<类型名称><变量名称>
eg:
int price;
int amount;
int price,amount;
变量的名字:变量需要一个名字,变量的名字是一种“标识符”,意思是它是用来识别这个和那个的不同的名字。
原则:标识符只能由字母、数字、下划线组成,数字不可以出现在第一个位置上,C语言的关键字(保留字)不可以用做标识符。
赋值:a=b:将b的值赋值给a。
初始化:当赋值发生在定义变量的时候,就是变量的初始化。C语言是没有强求做初始化,但是所有的变量在第一次被使用的时候(出现在赋值运算符的右边)之前应该被赋值一次。
变量初始化:<类型名称><变量名称>=<初始值>
eg:
int price = 0;
int amount = 100;
组合变量定义的时候,也可以在这个定义中单独给单个变量赋初值,比如:
int price = 0,amount=100;
变量类型
int price = 0; 定义了一个变量,变量的名字是price,类型是int,初始值是0.
C是一种有类型的语言,所有的变量在使用之前必须定义or声明,所有的变量必须具有确定的数据类型。数据类型表示在变量中可以存放什么样的数据,变量中只能存放指定类型的数据,程序运行过程中也不能改变变量的类型。
读整数:scanf("%d",&price);是要求scanf这个函数读入下一个整数,读到的结果赋值给变量price,小心前面的&

#include <stdio.h>
int main() 
{
    int a;
    int b;

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

    return 0;
}
#include <stdio.h>
int main()
{
	const int AMOUNT = 100;
	int price = 0;

	printf("请输入金额(元):");
	scanf("%d", &price);

	int change = AMOUNT - price;

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

	return 0;
}
#include <stdio.h>
int main()
{
	printf("请分别输入身高的英尺和英寸,"
		"如输入\"5 7\"表示5英尺7英寸:");

	double foot;
	double inch;

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

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

	return 0;
}
2.2计算:数据类型

浮点数:带小数点的数值。浮点本意指的是小数点是浮动的,是计算机内部表达非整数(分数、无理数)的一种方式。另一种方式叫做定点数,不过在C语言中不会遇到定点数。人们借用浮点数这个词来表达所有带小数点的数。
eg:
(foot+inch/12) * 0.3048;
(foot+inch/12.0) * 0.3048;
浮点数和整数放一起运算时,C将整数换成浮点数,进行浮点数运算。
double:双精度浮点数。float:单精度浮点数
数据类型
整数:int,printf("%d",...),scanf("%d",...)
带小数点的数:double,printf("%f",...),scanf("%lf",...)

2.3 计算:表达式

运算符:指进行运算的动作,比如加法运算符“+”,减法运算符“-”。
算子:指参与运算的值,这个值可能是常数,也可能是变量,还可能是一个方法的返回值。
运算符优先级
a+b 单目不变,自右向左
a
-b 单目取负,自右向左
赋值运算符:赋值也是运算,也有结果
eg:a=6的结果是a被赋予的值,也就是6
a=b=6 → a=(b=6)
嵌入式赋值:不利于阅读,容易产生错误
eg:int a = 6;
int b;
int c = 1+(b=a);
结合关系:一般自左向右;单目+-和赋值=自右向左
eg:
result=a=b=3+c;
result=2;
result=(result=result2)6(result=3+result);
这样的表达式太复杂,不容易阅读和理解,容易造成读程序时的误解。所以要避免写出这样复杂的表达式。这个表达式应该被拆成若干个表达式,然后以明显的正确的顺序来进行计算。
复合赋值:5个算术运算符,+-
/%,可以喝赋值运算符=结合起来,形成复合赋值运算符:+=、-=、=、/=、%=,注意两个运算符中间不要有空格
eg:
total+=(sum+100)/2; total=total+(sum+100)/2;
total
=sum+12; total=total*(sum+12);
递增递减运算符:++、--。是单目运算符,这个算子还必须是变量。这两个运算符分别是递增和递减运算符,作用是给变量+1或者-1;
count++; count+=1; count=count+1;
前缀后缀:++、--可以放变量前面(前缀形式)也可以放后面(后缀形式)。
a++的值是a+1以前的值,++a的值是加了1以后的值,无论哪个,a自己的值都加了1了。

表达式 运算 表达式的值
count++ 给count加1 count原来的值
++count 给count加1 count+1以后的值
count-- 给count减1 count原来的值
--count 给count减1 count-1以后的值

代码联系网站:拼题A,https://pintia.cn/problem-sets?tab=0

3.1 判断与循环:判断

如果

if(条件成立)
{
...
}

关系运算:对两个数据之间关系的计算
关系运算符:关系运算的结果是一个逻辑值,逻辑值只有两种可能的值:true(真,表示成立)或false(假,表示不成立)。当两个值的关系符合关系运算符的预期时,关系运算的结果为true(1),否则为false(0)。

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

优先级
1.所有的关系运算符的优先级比算数运算的低,但是比赋值运算的高。
2.判断是否相等的和!=的优先级比其他的低,而连续的关系运算是从左到右进行的。5>36>4,结果是1(true)
注释:以两个斜杠//开头的语句。插入在程序代码中,用来向读者提供解释信息。他们对于程序的功能没有任何影响,但是往往能使得程序更容易被人类读者理解。
/ */注释*:延续数行的注释,要用多行注释的格式来写。多行注释由一对字符序列/开始,以/结束。也可用于一行内的注释。
流程图

if语句:一个基本的if语句是由一个关键字if开头,跟上在括号里的一个表示条件的逻辑表达式,然后是一对大括号{}之间的若干条语句。如果表示条件的逻辑表达式的结果不是零,那么就执行后面跟着的这对大括号中的语句,否则就跳过这些语句不执行,而继续下面的其他语句。
注意:if这一行结束是没有;的,但是赋值语句是有一个;。这表明这条赋值语句是if语句的一部分,if语句拥有和控制这条赋值语句,决定他是否要被执行。

#include <stdio.h>

int main()
{

	const int MINOR = 35;

	int age = 0;

	printf("请输入你的年龄: ");
	scanf("%d", &age);

	printf("你的年龄是%d岁。\n", age);

	if ( age < MINOR ) {
    	printf("年轻是美好的,");
	}

	printf("年龄决定了你的精神世界,好好珍惜吧。\n");

	return 0;
}

找零计算器

#include <stdio.h>

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

	return 0;
}

找零计算器2

#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);
	}

	return 0;
}

找零计算器-特殊情况

#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;
}

计算时间差

#include <stdio.h>

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

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

	int ih = hour2 - hour1;
	int im = minute2 - minute1;
	if ( im <0 ) {
		im = 60 + im;
		ih --;
	}
	
	printf("时间差是%d小时%d分。\n", ih, im);
	
	return 0;
}

比大小

#include <stdio.h>

int main()
{
	int a,b;

	printf("请输入两个整数:");
	scanf("%d %d", &a, &b);
	
	int max = b;
	if ( a > b ) {
		max = a;
	}

	printf("大的那个是%d\n", max);

	return 0;
}

三个数比大小

//	max3.c
#include <stdio.h>

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

	int max = 0;

	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;
}

计算工资

#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;
}

计算成绩

#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;
}
3.2判断与循环:循环

if语句可以判断条件是否满足,满足时才做相应的动作,而循环语句可以在满足条件时,不断地重复执行一些动作。
重复执行的语句(循环语句)可以多次执行其他语句,它检查一个逻辑条件是否满足,只在满足的时候执行它的循环体。

while语句是一个循环语句,它会首先判断一个条件是否满足,如果条件满足,则执行后面紧跟着的语句或语句括号,然后再次判断条件是否满足,如果条件满足则再次执行,直到条件不满足为止。后面紧跟的语句或语句括号,就是循环体。
do-while循环和while循环很像,唯一的区别是我们在循环体执行结束的时候才来判断条件。也就是说,无论如何,循环都会执行至少一遍,然后再来判断条件。与while循环相同的是,条件满足时执行循环,条件不满足时结束循环。

数数几位数(4位数以下的数字)

#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(x>9)
    {
        n=2;
    }

    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;
}

if-条件满足才做;while-反复的做
while循环:如果我们把while翻译做“当”,那么一个while循环的意思是:当条件满足时,不断重复循环体内的语句。
循环执行之前判断是否继续循环,所以有可能循环一次也没有被执行;条件成立是循环继续的条件
do-while循环:在进入循环的时候不做检查,而是在执行完一轮循环体的代码之后,再来检查循环的条件是否满足,如果满足则继续下一轮循环,不满足则结束循环

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

比较两种循环
do-while循环和while循环很像,区别在于,循环体执行结束的时候才来判断条件。也就是说,无论如何,循环都会执行至少一遍,然后再来判断条件。与while循环相同的是,条件满足时执行循环,条件不满足时结束循环。

#include <stdio.h>
int main(){
    int x;
    scanf("%d",&x);
    int n=0;
    do{
        x /= 10;
        n++;
    }while(x>0);
    printf("%d",n);
    return 0;
}

for循环
for循环像是一个计数循环:设定一个计数器,初始化它,然后在计数器到达某值前,重复执行循环体,而每执行一轮循环,计数器值以一定步骤进行调整,比如+1or-1;
for(i=0;i<n;i++):循环n次,循环结束后i的值为n。
for==while:

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

for循环
for(初始动作;条件;每轮的动作){
}
*for中的每一个表达式都是可以省略的。for(;条件;) == while(条件)
小Tips

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

4.1 逻辑类型和运算

bool布尔类型:#include <stdbool.h>,之后就可以使用bool,true,false

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

int main() {
    bool b = 6>5;
    bool t = true;
    t=2;
    printf("%d\n",b);

    return 0;
}

逻辑运算:逻辑运算是对逻辑量进行的运算,结果只有0或者1.逻辑量是关系运算或逻辑运算的结果。

运算符 描述 示例 结果
! 逻辑非 !a 如果a是true结果就是false,如果a是false结果就是true
&& 逻辑与 a&&b 如果a和b都是true,结果就是true,否则是false
|| 逻辑非 a||b 如果a和b有一个是true,则结果是true,两个都是false,结果为false
优先级:!>&&>
短路:逻辑运算是自左向右进行的,如果左边的结果已经能够决定结果,就不会做右边的计算
eg:
a6&&b1,要是左边是false就不做右边啦
a==6 b==1,要是左边是true就不做右边啦
条件运算:条件,条件满足时的值和条件不满足时的值
优先级:调减运算符的优先级高于赋值运算符,但低于其他运算符。
逗号运算符:逗号用来链接两个表达式,并且以其右边的表达式的值作为它的结果。逗号的优先级是所有运算符中最低的,所以他两边的表达式会先计算,逗号的组合关系是自左向右,所以左边的表达式会先计算,而右边的表达式的值就留下来作为逗号运算的结果。
4.2 级联和嵌套的判断

猜数字的实现

#include <stdio.h>
int main(){
    int a,b,c;
    scanf("%d %d %d",&a,&b,&c);

    int max = 0;
    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;
}

嵌套的判断:当if的条件满足或者不满足的时候要执行的语句也可以是一条if或者if-else语句,这就是潜逃的语句。

if(code == READY)
    if(count<20)
        printf("一切正常\n");
    else
        printf("继续等待\n");

else的匹配:else总是和最近的那个if匹配,且缩进格式也不能暗示else的匹配
tips:在if或者else后面总是用{},即使只有一条语句。
级联的if-else if
if(exp1)
st1;
else if(exp2)
st2;
else
st3;

4.4 循环的例子

计数循环

int count =100;
while (count >=0){
    count--;
    printf("%d\n",count);
}
printf("发射!\n");

猜数字游戏:让计算机来想一个数,然后让用户来猜,用户每输入一个数,就告诉他是大了还是小了,直到用户猜中为止,最后还要告诉用户他猜了多少次。
注意:因为要让用户重复猜,所以要用循环;在实际写出程序之前,可以先用文字描述程序的思路;核心是循环的条件。
步骤:
1.计算机随机想一个数,记录在number里;
2.一个负责记次数的变量count初始化为0;
3.让用户输入一个数字a;
4.count递增(+1);
5.判断a和number的大小关系,要是a大输入“大”,要是a小输出“小”;
6.如果a和number不相等,程序转回3
7.否则,程序输出“猜中”和次数,然后结束。

#include <stdio.h>
int main() {
    //  随机数:每次召唤rand()就得到一个随机的整数
    srand(time(0));
    //  %100:x % n的结果是[0,n-1]的一个整数。
    int number = rand()%100+1;
    int a = 0;
    
    printf("我已经想好了一个1-100之间的数字。");
    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;
}
4.5 判断和循环常见错误

if语句常见的错误
忘了大括号:永远在if和else后面加上大括号,即使当时后面只有一条语句;
if后面的分号:是没有滴
错误使用==与=:if只要求()里面的值是0或非0;
使人困惑的else
代码风格:在if和else之后必须加上大括号形成语句块;大括号内的语句缩进一个tab的位置。

5.1 循环控制

素数:只能被1和自己整除的数,不包括1

#include <stdio.h>
int main() {
    int x;
    
    //scanf("%d", &x);
    x = 12;
    int i;
    //  int isPrime = 1;        //X是素数
    for(i=2; i<x; i++) {
        if(x % i == 0) {
            //  isPrime = 0;
            break;
        }
    }
    //  if (isPrime == 1) {
    if (i < x) {
        printf("是素数\n");
    } else {
        printf("不是素数\n");
    }

    return 0;
}
5.2 多重循环

输出100以内的素数

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

    for(x=2; x<100; x++){
        int i;
        int isPrime = 1;        //X是素数
        for(i=2; i<x; i++) {
            if(x % i == 0) {
                isPrime = 0;
                break;
            }
        }
        if (isPrime == 1) {
            printf("%d ", x);
        }
    }
    printf("\n");
    
    return 0;
}

嵌套的循环:循环里面还是循环

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

    x = 2;
    int cnt = 0;
    //  while (cnt < 50) {
    //  for(x=2; x<100; x++){
    for(x=2; cnt<50; x++) {
        int i;
        int isPrime = 1;        //X是素数
        for(i=2; i<x; i++) {
            if(x % i == 0) {
                isPrime = 0;
                break;
            }
        }
        if (isPrime == 1) {
            printf("%d ", x);
            cnt++;
        }
        //  x++; 这里是while循环的地方
    }
    printf("\n");
    
    return 0;
}

离开多重循环
break和continue:只能对他所在的那层循环做。break跳出内层循环
goto:goto out,直接跳出最外层循环,不要频繁使用
备注:
1.使用while或for循环时,如果想提前结束循环(在不满足结束条件的情况下结束循环),可以使用break或continue关键字。

#include <stdio.h>
int main(){
    int i=1, sum=0;
    while(1){  //循环条件为死循环
        sum+=i;
        i++;
        if(i>100) break;
   }
    printf("%d\n", sum);
    return 0;
}

while 循环条件为 1,是一个死循环。当执行到第100次循环的时候,计算完i++;后 i 的值为 101,此时 if 语句的条件 i> 100 成立,执行break;语句,结束循环。
在多层循环中,一个 break 语句只向外跳一层。
2.continue 语句的作用:跳过循环体中剩余的语句而强制进入下一次循环。continue语句只用在 while、for 循环中,常与 if 条件语句一起使用,判断条件是否成立。
3.break与continue的对比:break 用来结束所有循环,循环语句不再有执行的机会;continue 用来结束本次循环,直接跳到下一次循环,如果循环条件成立,还会继续循环。

5.3 循环应用
6.1 数组

如何记录很多数?: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]; //定义一个数组
    scanf("%d", &x);
    while (x != -1) {
        number[cnt] = x; //对数组中的元素赋值
        sum += x;
        cnt++;
        printf("请继续输入....\n");
        scanf("%d", &x);
    }
    if (cnt > 0) {
        int i;
        double average = sum / cnt;
        for(i=0; i<cnt; i++) {
            if(number[i] > average) {  //使用数组中的元素
                printf("%d", number[i]);  //遍历数组
            }
        }
    }
    return 0;
}

定义数组
<类型> 变量名称[元素数量];
int grades[100];
double weight[20];
元素数量必须是整数
数组:是一种容器(放东西的东西),特点是:
1.其中所有的元素具有相同的数据类型;
2.一旦创建,不可改变大小;
3.数组中的元素在内存中实力按序依次排列的;
例子:int a[10]
10个单元:a[0],a[1]...,a[9]

每个单元就是一个int类型的变量
可以出现在赋值的左边或右边:eg:a[2]=a[1]+6;在赋值左边的叫做左值
数组的单元:数组的每个单元就是数组类型的一个变量
使用数组时放在[]中的数字叫做下标或索引,下标是从0开始计数的。
有效的下标范围:一旦程序运行,越界的数组访问可能造成问题,导致程序崩溃。segmentation fault。
故正确有效的下标范围为:[0,数组的大小-1]
计算平均数

#include <stdio.h>
int main() {
    int x;
    double sum = 0;
    int cnt;
    printf("请输入数字的数量:");
    scanf("%d", &cnt); //cnt是count的缩写,用做计数器
    if (cnt > 0){
        int number[cnt];
        scanf("%d", &x);
        while (x != -1) {
            number[cnt] = x;
            sum += x;
            cnt ++;
            scanf("%d", &x);
        }
    return 0;
}

长度为0的数组:int a[0],可以存在,但是无用。
写一个程序,输入数量不确定的[0,9]范围内的整数,统计每一种数字出现的次数,输入-1表示结束。

#include <stdio.h>
int main() {
    const int number = 10; //数组的大小
    int x;
    int count[10]; //定义数组
    int i;

    //初始化数组
    for(i=0; i<10; i++) {
        count[i] = 0;
    }  
    scanf("%d", &x);
    while (x != -1) {
        if(x >= 0 && x <= 9) {
            count[x] ++;   //数组参与运算
        }
        scanf("%d", &x);
    }
    
    //数组遍历输出
    for (i=0;i<10; i++) {
        printf("%d:%d\n",i, count[i]);
    } 

    return 0;
}
6.2 函数的定义和使用
#include <stdio.h>
//把sum这个函数直接提出来到前面
void sum(int begin, int end) { //输入两个形参,int形式
    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函数,输入1,10两个值,做完之后换到下一行
    sum(20,30);
    sum(35,45);

    return 0;
}

什么是函数:函数是一块代码,接受0个或者多个参数,做一件事情,并且返回0个或者一个值。可以先想成数学中的函数:y=f(x)

调用函数:函数名(参数值);
1.()起到了表示函数调用的重要作用,即使没有参数也是需要()的
2.如果有参数,则需要给出正确的数量和顺序,这些值会被按照顺序依次用来初始化函数中的参数。
函数返回:函数知道每一次是哪里调用它,会返回到正确的地方。
从函数中返回值:拿一个函数距举例

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

    return ret; //这里把ret返回回去了
}

return停止了函数的执行,并且送回了一个值
return 表达式;
一个函数里可以出现多个return语句。

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

    // return ret;
}

而且函数中的返回值可以赋值给变量,可以再传递给函数,甚至还可以丢弃。
没有返回值的函数
void 函数名(参数表)
1.不能使用带值的return,也可以没有return
2.调用的时候不能做返回值的赋值
注:要是函数有返回值,则必须使用带值的return

6.3 函数的参数和变量

函数先后关系:把函数写在主体语句上面,C的编译器是自上而下的顺序分析代码。在看到sum(1,10)的时候,他需要知道sum()的样子,也就是sum()要几个参数,每个参数的类型如何,返回什么类型,这样他才能检查你对sum()的调用是否正确
函数原型:函数头,以分号结尾,构成了函数的原型。
函数原型的目的是告诉编译器这函数长什么样子:名称,参数(数量和类型),返回类型
eg:

double max(double a, double b); //函数原型
int main() {
    int a,b,c;
    a = 5;
    b = 6;
    c = max(10,12);
    printf("%d\n", c);
    max(12,13); //根据原型判断

    return 0;
}

double max(double a, double b); //实际的函数头

旧标准习惯把函数原型写在调用它的函数里面,现在一般写在调用它的函数前面

#include <stdio.h>

void sum(int begin, int end) { //声明一个函数

int main(){
    sum(1,10);   //调用sum函数,输入1,10两个值,做完之后换到下一行
    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);
}

参数传递:调用函数,如果函数有参数,调用函数的似乎还必须传递给他数量,类型正确的值
可以传递给函数的值是表达式的结果,这包括:字面量,变量,函数的返回值,计算的结果

传值:每个函数都有自己的变量空间,参数也位于这个独立的空间中,和其它函数没关系。过去,对于函数参数表中的参数,叫做“形式参数”,调用函数时给的值,叫做“实际参数”
由于容易让初学者误会实际参数就是在函数中进行计算的参数,误会调用函数的时候把变量而不是值传进去了,所以我们不建议继续用这种古老的方式来称呼他们。
我们认为。,他们是参数和值的关系
本地变量:函数的每次运行,就产生了一个独立的变量空间,在这个空间中的变量,是函数的这次运行所独有的,称作本地变量。定义在函数内部的变量就是本地变量。参数也是本地变量。
变量的生存期和作用域
1.生存期:什么时候这个变量开始出现了,到什么时候它消亡了
2.作用域:在(代码的)什么范围内可以访问这个变量(这个变量可以起作用)
3.对于本地变量,这两个问题的答案是统一的:大括号内————块
本地变量的规则
1.本地变量是定义在块内的:他可以定义在函数的块内,也可以定义在语句的块内。甚至可以随便拉一对大括号来定义变量。
2.程序运行进入这个块之前,其中的变量不存在,离开这个块,其中的变量就消失了
3.块外面定义的变量在里面仍然有效
4.快里面定义乐和外面同名的变量则掩盖了外面的
5.不能再一个块内定义同名的变量
6.本地变量不会被默认初始化
7.参数在进入函数的时候被初始化了。

7.1 数组运算

数组的集成初始化
int a[] = {2,4,6,8,10,3,4,5,6,7,8}
直接用大括号给出数组的所有元素初始值
不需要给出数组大小,编译器替你数数
集成初始化的定位(C99 ONLY)

int a[10] = {
    [0] = 2, [2] = 3, 6,
}

用[n]在初始化数据中给出定位
没有定位的数据接在前面的位置后面
其他位置的值补零
也可以不给出数组大小,让编译器算
特别适合初始数据稀疏的数组
数组的大小:sizeof给出整个数组所占据的内容的大小,单位是字节
ziseof(a)/sizeof(a[0])
sizeof(a[0])给出数组中单个元素的大小,于是相除就得到了数组的单元个数
这样的代码,一旦修改数组中初始的数据,不需要修改遍历这样的代码
数组的赋值
int a[] = {2,4,5,5,2,6,39,2,4,58,78,2,56,5}; √
int b[] = a; ×
注意:数组本身不可以被赋值
要把一个数组的所有元素交给另一个数组,必须采用遍历:

for(i=0; i<length; i++) {
    b[i] = a[i];
}

遍历数组:遍历是数据结构中的一种术语。即沿着某条搜索路线,依次对每个元素均做一次且仅做一次访问。所以你要对数组遍历,当然需要你用循环语句,将数组中存放的元素依次输出。
通常都是使用for循环,让循环变量i从0到<数组的长度,这样循环体内最大的i正好是数组最大的有效下标
常见错误是:循环结束条件是<=数组长度,或:离开循环后,继续用i的值来做数组元素的下标!

int main(void)
{
    int a[] = {2,4,6,7,1,3,5,9,11,13,23,14,32};
    int x;
    int loc;
    printf("请输入一个数字:");
    scanf("%d", &x);
    loc=search(x, a, sizeof(a)/sizeof(a[0]));
    if (log != -1) {
        printf("%d在第%d个位置上\n", x, loc);
    } else {
        printf("%d不存在\n", x);
    }

    return 0;
}

数组作为函数参数时,往往必须再用另一个参数来传入数组的大小
数组作为函数的参数时:
不能在[]中给出数组的大小;
不能再利用sizeof来计算数组的元素个数!

8.1 指针

sizeof 是一个运算符,给出某个类型或者变量在内存中所占据的字节数
sizeof(int),sizeof(i)
运算符&:scanf("%d", &i)里面的&,目的是获得变量的地址,它的操作数必须是变量
int i; printf("%x", &i);
地址的大小是否与int相同取决于编译器
int i;printf("%p", &i);
&不可以取地址?:&(a+b) ×
分配内存:自顶向下
相邻数组单元永远相差4

Q:如果能够将取得的变量的地址传递给一个函数,能否通过这个地址在那个函数内访问这个变量?
scanf("%d", &i);
scanf()原型应该是怎样的?我们需要一个参数能保存别的变量的地址,如何表达能够保存地址的变量?
指针:就是保存地址的变量
int i;
int* p = &i;//p指向了i
int* p,q; //p是point指针,但是q只是int类型的一个数
指针变量:变量的值是内存的地址,普通变量的值是实际的值;指针变量的值是具有实际值的变量的地址
作为参数的指针
void f(int* p);// 在被调用的时候得到了某个变量的地址:int i = 0;f(&i);
在函数里面可以通过这个指针访问外面的这个。

#include <stdio.h>
void f(int *p);
int main(void) {
    int i = 6;
    printf("&i=%p\n", &i);
    f(&i);

    return 0;
}

void f(int *p){
    printf("p=%p\n", p);   //把i的指针p传递给函数,输出的值一致
}

访问那个地址上的变量【*】
是一个单目运算符,用来访问指针的值所表示的地址上的变量
可以做右值,也可以做左值
int k = p; //读这个值
p = k+1; //写这个值
p可以看做一个整数,其实就代表了那个i,而p是指针,p指向了i
数组变量是特殊的指针
数组变量本身表达地址,所以:
1.int a[10]; int
p=a; //无需用&取地址
2.但是数组的单元编导的是变量,需要用&取地址
3.a == &a[0]
4.[]运算符可以对数组做,也可以对指针做:p[0]<==>a[0]
5.
运算符可以对指针做,也可以对数组做:
a=25;
6.数组变量是const的指针,所以不能被赋值:int a[] <==> int
const a=...
字符类型:char是一种证书,也是一种特殊的类型:字符。这是因为:用单引号表示字符字面量:'a','1'
"也是一个字符
printf和scanf里用%c来输入输出字符

posted on 2021-08-25 10:37  Sihroki初号机  阅读(470)  评论(0编辑  收藏  举报

导航