【C语言】程序设计入门——C语言

前言

本文是基于中国MOOC平台上的《程序设计入门——C语言(翁恺)》课程,所作的一篇课程笔记,便于后期进行系统性查阅和复习。


一、程序设计与C语言

本章的内容是提供一些背景知识,关于计算机、程序、编程语言,也关于C语言。是一篇导论,帮助学生建立关于计算机工作方式和编程语言的正确概念。

1.1 计算机与编程语言

1.1.1 计算机怎么做事情?编程语言是什么?

计算机如何解决问题?

需要一步步的告诉计算机如何解决问题。这里可以看到人与计算机的区别,对人(What to do),对计算机(How to do)。

计算机语言:

• 程序是用特殊的编程语言写出来表达如何解决问题的。

• 不是用编程语言来和计算机交谈,而是描述要求它如何做事情的过程或方法。

算法(计算机-程序-算法):

• 我们要让计算机做计算,就需要找出计算的步骤,然后用编程语言写出来。

• 计算机做的所有事情都叫做计算。

• 计算的步骤就是算法。

1.1.2 计算机的思维方式

重复是计算机最擅长的。

程序的执行有两种方案:

1.解释:借助一个程序,那个程序能试图理解你的程序,然后按照你的要求执行。

2.编译:借助一个程序,就像一个翻译,把你的程序翻译成计算机能懂的语言(即机器语言),然后这个机器语言写的程序就能直接执行。

解释语言vs编译语言

• 语言本无解释/编译之分(任何一种语言都可以解释执行,又可以编译执行)。

• 区分只在于传统和习惯(比如C语言常用编译执行,python常用解释执行)。

• 解释型语言有特殊的计算能力。

• 编译型语言有确定的运算性能。

1.2 C语言

C语言在工业界有重要的地位,在很多领域无可替代,几乎所有和硬件打交道的地方都要用C语言。本课程中按照C99来展开教学,但国内很多大学和计算机二级考试仍坚持更老的ANSL C,所以在课程中,凡C99和ANSL C不同之处,均会进行额外标注。

1.2.1 为什么是C?

• 现代的编程语言在语法上差异很小,其他语言几乎都是C-like语言。

• 语句的能力/适用领域主要是由库和传统所决定的。

1.2.2 简单历史

C的版本-标准

• 1989年ANSI发布了一个标准——ANSI C

• 1990年ISO接受了ANSI的标准——C89

• C的标准在1995和1999两次更新——C95和C99

1.3 第一个程序

大学中往往会让初学者使用Dev C++来学习C语言,而对于有一定基础的朋友大多会使用Visual Studio。

1.3.1 程序框架

#include
int main{
    
    return 0;
}

•本课程中所有的程序都需要这一段

•直到学函数之前,我们的代码都只是在这个框架中间

•为了便于理解,目前不对程序框架进行解释,知道第一条即可


二、计算

2.1 变量

2.1.1 变量定义

int price=0;

•这一行,定义了一个变量。变量的名字是price,类型是int,初始值是0。

•变量是一个保存数据的地方,当我们需要在程序里保存数据时,就需要一个变量来保存它。用一个变量保存了数据,它才能参与到后面的计算中。

变量定义的一般形式:<数据类型名称><变量名称>;

•int a;

•double a,b,c;//定义了三个双精度浮点数

变量名称

•变量是一种“标识符”

标识符构造规则:只能由字母、数字和下划线组成,数字不能出现在首位,C语言的关键字/保留字不可以用作标识符。

在变量定义上,ANSI C与C99有一点不同:

ANSI C只能在代码开头的地方定义变量

int price;
int change= 0;
pritf("请输入金额(元):");
scanf("%d",&price);
change=100-price;

 而C99可以在代码中间定义变量

int price;
pritf("请输入金额(元):");
scanf("%d",&price);
int change=100-price;

2.1.2 变量的赋值和初始化

C语言中赋值与初始化的差异并不大,但在其他语言,如C++这两种之间差异会比较大。

变量赋值

•price=0;

•这是个赋值语句,“=”是赋值运算符,表示将“=”右边的值赋给左边的变量。

变量初始化

•<数据类型名称><变量名称>=<初始值>;

•int price=0;

•int amount=100;

•组合变量定义的时候,也可以在这个定义中单独给单个变量赋初值,如:

•int price=0,amount=100;

变量类型

C语言是一种有类型的语言。所有变量在使用之前必须定义或声明,所有变量必须有具体的数据类型。数据类型表示在变量中可以存放什么样的数据,变量中只能存放指定类型的数据,程序运行过程中也不能改变变量的类型。

2.1.3 常量vs变量:不变的量是常量

int change=100-price;

•100直接写在程序里,我们称作直接量。

•更好的方式,是定义一个常量:const int AMOUNT=100;(C99才有的用法)//便于理解、修改

const

•是一个修饰符,加在int的前面,用来给这个变量加一个const(不变的)的属性。这个const的属性表示这个变量的值一旦初始化,就不能再修改了。

2.2 数据类型

10和10.0在C中是两个完全不同的数,10.0是浮点数。

浮点数

•C语言中,人们借助浮点数来表达所有的带小数点的数。

•当浮点数和证书放在一起运算时,C语言会将整数转换成浮点数,然后进行浮点数的运行。进而得到更精准的结果——浮点数。

2.3 表达式

2.3.1 表达式

表达式

•一个表达式是一系列运算符和算子的组合,用来计算一个值。

•运算符是指进行运算的动作,比如加法运算符“+”运算符、减法“-”运算符。

•算子是指参与运算的值,这个值可能是常熟,也可能是变量,还可能是一个方法的返回值。

2.3.2 运算符优先级

运算符优先级

初等运算符>单目运算符>算术运算符(先乘除、后加减)>关系运算符>逻辑运算符(不含!)>条件运算符>赋值运算符>逗号运算符

以上的优先级,从左往右递减。

赋值运算符

•赋值也是运算,也有结果

•a=6的结果是a被赋予的值,也就是6

•a=b=6——>a=(b=6)

2.3.3 交换变量

如何交换两个变量的值?

int a=6;
int b=5;
int c;
c=a;
a=b;
b=c;

2.3.4 复合赋值和递增递减

复合赋值

•5个算术运算符(+-*/%),可以和赋值运算符“=”结合起来,形成复合赋值运算符:“+=”、  “-=”、“*=”、“/=”、“%=”。

•total+=5;

•total=total+5;//这两个表达式的含义是一样的

•注意两个运算符之间不要有空格

递增递减运算符

•“++”和“--”是两个单目运算符,叫做递增和递减运算符。

•与之搭配使用的算子必须是变量,它们的作用是给这个变量+1或-1。

•i++;

•i+=1;

•i=i+1;//这三个表达式的含义是一样的

 前缀后缀

•++和--可以放在变量前面,叫做前缀形式,也可以放在变量后面,叫做后缀形式

•a++的值是a+1之前的值,而++a的值是a+1以后的值。

•无论是a++还是++a,a的值都是a+1以后的值。

int i=0;
printf("i++=%d\n", i++);     //i++=0
printf("i=%d\n", i);         //i=1
printf("++i=%d\n", ++i);     //++i=2
printf("i=%d\n", i);         //i=2
表达式 运算 表达式的值 a的值
a++ 给a加1 a原来的值 a+1以后的值
++a 给a加1 a+1以后的值 a+1以后的值
a-- 给a减1 a原来的值 a-1以后的值
--a 给a减1 a-1以后的值 a-1以后的值

 三、判断与循环

本课程在该章仅对判断与循环语句进行了基础讲解,若想了解更高级的语句,请见第四章。

3.1 判断

3.1.1 if语句

if(条件成立){
   //语句1
}

 3.1.2 判断的条件

条件

•计算两个值之间的关系,叫做关系运算。

•关系运算符的结果:当两个值的关系符合关系运算符的预期时,关系运算的结果为整数1,否则为整数0。 

关系运算符

意义

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

3.1.3 否则:如果条件不成立?

if(条件不成立){
   //语句1
}else{
   //语句2
}

3.1.4 if和else后面也可以没有{}而是一条语句

if(条件不成立)
   //语句1
else
   //语句2

提示:这样进行编程是不好的习惯!if和else后面一定要用{}! 

如果不使用{},if和else只能执行if和else后的单个语句,不便于执行多条语句的情况。使用{},能够明确标识出哪些语句属于if和else语句的范围,避免了可能的歧义,增加了代码的可读性和可维护性。

3.2 循环

循环语句选择小tips:如果有固定次数,用for;

                                  如果必须执行一次,用do-while;

                                  其他情况,用while。

3.2.1 while循环

while循环基本语句:

while(循环条件){
      //循环体
}

•while循环的意思:当条件满足时,不断重复循环体内的语句,直至条件不满足。

•循环执行之前判断是否继续循环,所以有可能循环一次也没有被执行

3.2.2 do-while循环

do-while循环基本语句:

do{
    //循环体
}while(循环条件)

•先执行循环体,然后再检查循环条件是否成立,若成立再执行循环体。

3.2.3 for循环

for循环基本语句:

for(循环变量赋初值;循环条件;循环变量增值){
            //循环语句
}

for语句能用于两种情况:1.循环次数已经确定。2.循环次数不确定而只给出循环结束条件。


四、进一步的判断与循环

4.1 逻辑类型和运算

4.1.1 bool类型

使用方式:

•头文件#include

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

#include
#include
int main(){
    bool b=6>5;   //b为true
    bool t=false; //t为false
    printf("%d %d",b,t);//输出1,0
    return 0;
}

4.1.2 逻辑运算

•逻辑运算是对逻辑量进行运算,结果只有0或1

•逻辑量是关系运算或逻辑运算的结果

运算符 描述 示例 结果
逻辑非 !a

如果a是true,结果就是false;

如果a是false,结果就是true。

&&

逻辑与 a&&b 如果a和b都是true,结果就是true,否则结果是false
|| 逻辑或 a||b 如果a和b都是false,结果就是false,否则结果是true

4.1.3 条件运算和逗号运算

条件运算符(表达式1?表达式2:表达式3)

•条件?条件满足时的值:条件不满足时的值

•条件运算符的优先级高于赋值运算符,但低于其他所有运算符

嵌套条件运算符

•条件运算符是自右向左结合的

•w

•嵌套条件运算符不利于理解、调试,不推荐使用!

逗号运算符

•逗号用来连接两个表达式,并以其右边的表达式的值作为它的结果。

•i=3+4,5+6;与i=(3+4,5+6);//i=7与i=11

•该运算符常用于for语句

4.2 级联和嵌套的判断

4.2.1 嵌套的if-else

找三个数的最大?

    int a, b, c;
	int max = 0;
	scanf_s("%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("三位数中最大为%d", max);

4.2.2 级联的if-else

分段函数?(单一出口比多出口的代码更优秀)

	int x, f;
	scanf("%d", &x);
	if (x > 0) {
		f = x + 1;
	}
	else if (x == 0) {
		f = 0;
	}
	else {
		f = 2*x - 1;
	}
	printf("%d", f);//单一出口
	return 0;0;
	int x, f;
	scanf_s("%d", &x);
	if (x > 0) {
		printf("%d",f = x + 1);//多出口
	}
	else if (x == 0) {
		printf("%d",f = 0);
	}
	else {
		printf("%d", f = 2*x - 1);
	}
	return 0;

4.3 多路分支

switch-case基本语句:

switch(控制表达式)
{
  case 常量: 
      语句;
      break;//注意break不要忘掉!
  default:
      语句;
      break;
}

4.4 循环的例子

编程的难点:把问题转换为程序。(变量,算法,流程图,程序)

算平均数

算法:1.初始化变量sum和count为0;

           2.读入number;//输入的数个数是不明确的,如何判断输入结束了?

           3.如果number不是-1,则将number加入sum,并将count+1,回到2;

           4.如果number是-1,则计算和输出sum/count(注意换成浮点来计算)。

	int sum = 0, count = 0;
	int number;
	
	scanf_s("%d", &number);
	while(number != -1) {//使用while循环
		sum += number;
		count++;
		scanf_s("%d", &number);
	}
		printf("%.2f", sum*1.0 / count);
	int sum = 0, count = 0;
	int number;
	
	do {//使用do-while循环
		scanf_s("%d", &number);
		if (number != -1) {
			sum += number;
			count++;
		}
	} while (number != -1);
		printf("%.2f", sum*1.0 / count);

 猜数

算法:1.定义变量number和x=rand();

           2.读入number;

           3.如果number不等于x,进入循环判断猜大猜小,返回2;

           4.如果number等于x,则直接跳过循环,输出恭喜猜对了。

#include
#include
int main() {
	int number;
	int x = rand();
	scanf_s("%d", &number);
	while (number != x) {
		if (number > x) {
			printf("不好意思,猜大了。\n");
		}
		else {
			printf("不好意思,猜小了。\n");
		}
		scanf_s("%d", &number);
	}
		printf("恭喜你!猜对了!\n");
	return 0;
}

整数求逆

#include
int main() {
	int number,x=8;
	scanf_s("%d", &number);

	while (number != 0){
		x = number % 10;
		number /= 10;
		printf("%d",x);
	} 

	return 0;
}

该代码无法在number为“007”时,得到“700”?

4.5 判断和循环常见错误

if语句常见错误

•忘了大括号,

•if后面错误加分号

•错误使用==和=

•令人迷惑的else


五、循环控制

5.1 循环控制

判断数是否是素数?

难点:1.判断整除——取余%为0,说明是整除。

           2.仅输出一句话——加入一个新的变量来判断输出。

           3.循环控制,在第一次整除就可以判断数不是素数了——break跳出循环体。

#include 
int main()
{
	int x,i;
	scanf_s("%d", &x);
	int isPrime = 1;
	for (i = 2; i < x; i++) {
		if (x % i == 0) {
			isPrime = 0;
			break;
		}
	}
	if (isPrime == 0) {
		printf("不是素数");
	}
	else {
		printf("是素数");
	}
	return 0;
} 

5.2 多重循环

5.2.1 嵌套循环

写程序输出100以内的素数

#include 
int main()
{
	int x, i;
	for (x = 2; x < 100; x++) {
		int isPrime = 1;
		for (i = 2; i < x; i++) {
			if (x % i == 0) {
				isPrime = 0;
				break;
			}
		}
		if (isPrime != 0) {
			printf("%d ", x);
		}
	}
	printf("\n");
	return 0;
}

上述正是嵌套循环,需要注意在嵌套循环中内部和外部的循环变量应该不同,避免出现混淆。

5.2.2 跳出循环

如何从循环中跳出(break、continue、goto)

break:只能跳出一层循环体,如需跳出嵌套循环,可使用接力break。

continue:跳过循环体中剩余的语句而强制进入下一次循环,并没有跳出循环体。

goto:goto XX;XX:位于循环体外。//goto最好只用于跳出循环,不要用于其他情况

5.3 循环应用

5.3.1 前n项求和

【C语言】程序设计入门——C语言_第1张图片

	int n, i;
	float f=0.0;
	scanf_s("%d", &n);
	for (i = 1; i <= n; i++) {
		f += 1.0 / i;
	}
	printf("f(%d)=%f",n, f);

【C语言】程序设计入门——C语言_第2张图片

	int n, i;
	float f=0,sign=1.0;
	scanf_s("%d", &n);
	for (i = 1; i <= n; i++) {
		f += sign / i;
		sign=-sign;
	}
	printf("f(%d)=%f",n, f);

5.3.2 求最大公约数

输入两个数a和b,输出它的最大公约数

有两种方法:枚举法,辗转相除法

枚举法:1.设t=1;

              2.如果a和b都能被t整除,则记下这个t;

              3.t+1重复第二步,知道t等于a或者b;

              4.那么,记下的最大的可以同时整除a和b的t就是最大公约数。

	int a, b;//枚举法
	scanf_s("%d%d", &a, &b);
	int min;
	if(a

辗转相除法:1.如果b等于0,计算结束,a就是最大公约数;

                      2.否则,计算a除以b的余数,让a=b,而b等于那个余数;

                      3.回到第一步。

	int a, b;
	scanf_s("%d%d", &a, &b);
	int t=0;
	while(b!=0){
		t = a % b;
		a = b;
		b = t;
	}
	printf("a和b的最大公约数是%d\n", a );

5.3.3 整数分解

输入一个非负整数,正序输出它的每一位数字

我的解答:

	int x,t=1;
	int i = 0;
	scanf_s("%d", &x);
	int number = x;
	while (number != 0) {
		t = number % 10;
		number /= 10;
		i++;
	}//求出数的位数
	i = i - 1;
	for (; i != -1; i--) {
		t = x / pow(10, i);
		x -= t * pow(10, i);
		printf("%d ", t);
	}

翁恺老师的解答:个人感觉有点复杂,这里就不列出来了。

思路是先得到逆序的数字列,再输出正序的数字列。


六、数组与函数

数组是长度固定的数据结构,用来存放指定类型的数据。一个数组里可以有很多个数据,所有数据的类型都是相同的。

如果我们对输入数组的数据个数是未知的,如何安全的把数据存放进数组?

【C语言】如果我们对输入数组的数据个数是不知道的,如何安全的把数据存放进数组?-CSDN博客

6.1 数组

6.1.1 初试数组

写程序计算用户输入数字的平均数,并输出所有大于平均数的数。

	int x = 0;
	double sum = 0;
	int cnt = 0;
	int number[100];//定义数组
	scanf_s("%d", &x);
	while (x != -1) {
		number[cnt] = x;//对数组中的元素赋值
		sum += x;
		cnt++;
		scanf_s("%d", &x);
	}
	int average,i;
	average = sum / cnt;
	for (i = 0; i != cnt; i++) {
		if (number[i] > average) {//使用数组中的元素
			printf("%d ", number[i]);
		}
	}

6.1.2 定义数组

 •<类型>变量名称[元素数量]

   int grades[100];

   double weigh[20];

•元素数量必须是整数

•C99之前:元素数量必须是编译时刻确定的字面量

 数组是一种容器(存储东西的东西),特点是:

•其中所有的元素具有相同的数据类型

•一旦创建,不能改变大小

•数组中的元素在内存中的连续依次排列的

int a[10]

•一个int的数组

•10个单位:a[0], a[1],a[2],...,a[9]

•每个单位就是一个int类型的变量

•可以出现在赋值的左边或者右边:a[2]=a[1]+6;

数组的单位

•数组的每个单元就是数组类型的一个变量

•使用数组时放在[]中的数字叫做下标或索引,下标从零开始计数

•程序员要保证程序只使用有效的下标值:[0,数组的大小-1]

长度为0的数组?

•Int a[0];

•可以存在,但是无用 

6.1.3 用数组做散列计算

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

const int number=10;//数组的大小
int x;
int count[number];//定义数组
int i;
for (i = 0; i < number; i++) {//用for循环初始化数组
	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]);
}

6.2 函数的定义与使用

6.2.1 初见函数

求和函数

#include

	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(23, 42);

	return 0;
}

6.2.2 函数的定义与使用

 什么是函数?

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

•可以想象成数学中的函数:y=f(x)

函数定义

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);
}//函数体

函数头:void返回类型,sum函数名,int begin,int end参数表

调用函数

 •函数名(参数值)

•()起到了表示函数调用的重要作用

•即使没有参数也要()

6.2.3 从函数中返回

 从函数中返回值

•return停止函数的执行,并送回一个值

•return;

•return 表达式;//return会把表达式送回调用函数的位置

 从函数中返回值

•该值可以赋值给变量

•该值可以再传递给函数

•该值也可以丢弃

没有返回值的函数

•void 函数名(参数表)

•不能使用带值的return

•可以没有return

调用时不能做返回值的赋值 

6.3 函数的参数和变量

6.3.1 函数原型

函数先后关系

前文把函数写在调用函数上面,是因为C的编译器自上而下顺序分析代码。

如果不将函数整体放在调用函数之前?

#include

void sum(int begin, int end);//函数的原型声明
	
int main(){
	int x = 9;
	sum(x, 10);
	sum(23, 42);

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

函数原型

 •函数头,以分号;结尾,就构成了函数的原型

void sum(int begin, int end);

 •函数原型的目的:告诉编译器这个函数长什么样(名称、参数数量及类型、返回类型)

 •旧标准习惯把函数原型写在调用它的函数里面

 •现在一般写在调用它的函数前面

6.3.2 参数传递

 调用函数

 •如果函数有参数,调用函数必须传递给它的数量、类型正确的值

 •可以传递给函数的值是表达式的结果,这包括字面量、变量、函数的返回值、计算的结果

int a,b,c;
a=5;
b=6;
c=max(10,12);
c=max(a,b);
c=max(c,23);
c=max(max(12,34),34);
c=max(23+98,b);

如果调用函数所给值与参数类型不匹配,会如何?

 •编译器会悄悄把类型转换好,但这很可能不是编程者所期望的。

 •后续语言,C++/Java在这方面会很严格。

传值

 •C语言在调用函数时,永远只能传值给函数。

 •每个变量有自己的变量空间,参数也位于这个独立的空间中,和其它函数没有关系。

 •过去,对于函数参数表中的参数,叫做“形式参数”,调用函数给的值,叫做“实际参数”

(但这种说法容易对初学者造成误导,故不建议继续使用这种说法)

 •我们认为,调用函数给的就是值,函数参数表中的参数就是参数,两者是值与参数的关系 

6.3.3 本地变量

本地变量

 •函数的每次运行,就产生一个独立的变量空间,在这个空间中的变量,是函数这次运行所独有的,称作本地变量。

 •定义在函数内部的变量就是本地变量。

 •参数也是本地变量。

变量的生存期和作用域

 •生存期:什么时候这个变量开始出现,到什么时候它消亡了。

 •作用域:在(代码的)什么范围内可以访问这个变量(这个变量可以起作用)。

 •对于本地变量而言,这两个量都是:大括号内——块。 

本地变量的规则

 • 本地变量是定义在块内的。

 •程序运行进入这个块之前,其中的变量不存在;离开这个块,其中的变量就消失了。

 •本地变量不会被默认初始化。

6.3.4 杂事 

没有参数时,如何写参数?

 •void f(void)

 •void f()//编译器会默认参数未知,而非没有参数

 •建议使用void f(void);

调用函数中的逗号和逗号运算符怎么区分?

 •f(a,b);//这里的逗号就是单纯的标点符号

 •f(a,b);//这里的逗号就是逗号运算符

可以在函数里面定义另一个函数吗?

  •不可以,C语言不允许嵌套定义 

6.4 二维数组

int a[3][5];

•通常理解为a是一个3行5列的矩阵

a[1][1] a[1][2] a[1][3] a[1][4] a[1][5]
a[2][1] a[2][2] a[2][3] a[2][4] a[2][5]
a[3][1] a[3][2] a[3][3] a[3][4] a[3][5]

二维数组的遍历

•a[i][j]是一个Int,表示第i行第j列的单元

for(i=0;i<3;i++){
    for(j=0;j<5;j++){
        a[i][j]=i*j;
    }
}

二维数组的初始化

int a[][5]={
    {0,1,2,3,4},
    {2,3,4,5,6},
};

•列数是必须给出的,行数可以由编译器来数

•每行一个{},逗号分隔

•最后的逗号可以存在,有古老的传统

•如果省略,表示补零


七、数组运算

7.1 数组运算

数组的集成初始化

int a[23]={0};

数组的大小

 •sizeof()

7.2 线性搜索

7.2.1 线性搜索

搜索

•在一个数组中找到某个数的位置(或确认是否存在)

•基本方法:遍历

#include
int search(int key,int a[],int len) 
{
	int ret = -1;
	for (int i = 0; i < len; i++) {
		if (key == a[i]) {
			ret = 0;
			break;
		}
	}
	return ret;
}

int main(void){
	int ret=6;
	int a[] = { 2,5,3,6,2,654,24,23,45,24,13,1435,334,13,43,42425,25,3,635,3,2 };
	int r=search(ret, a, sizeof(a) / sizeof(a[0]));
	printf("%d\n", r);

	return 0;
}

7.2.2 搜索的例子

7.3 排序初步


八、指针与字符串 

8.1 指针

8.1.1 取地址运算:&运算符取得变量的地址

sizeof

•是一个运算符,给出某个类型或变量在内存中所占据的字节数

•sizeof(int)

•sizeof(i)

运算符&

•scanf("%d",&i);里的&

•获取变量的地址,它的操作数必须是变量

•地址大小是否与int相同取决于编译器

•int i;printf("%p",&i);//输出i的地址,记得要使用%p

&不能取的地址

•&不能对没有地址的东西取地址,必须取明确变量的地址

•&(a+b)

•&(a++)

•&(++a)

对数组进行取地址

	int a[] = { 1,2,3,3,4 };

	printf("%p\n",&a);     //显示&a和a和a[0]的地址一致,a[0],a[1],a[2]之间相差四个字节
	printf("%p\n", a);
	printf("%p\n", &a[0]);
	printf("%p\n", &a[1]);
	printf("%p\n", &a[2]);

8.1.2 指针:指针变量就是记录地址的变量

 指针

•就是保存地址的变量

  int i;

  int* p=&i;

  int* p,q;//p是一个指针,指向一个int;q一个普通的整型变量

  int *p,q;//p是一个指针,指向一个int;q一个普通的整型变量

指针变量

 •变量的值是内存的地址

•普通变量的值是实际的值

•指针变量的值是具有实际值的变量的地址

作为参数的指针

•void f(int *p);

•在被调用的时候得到了某个变量的地址

•int i=0;f(&i);

•在函数里面可以通过这个指针访问外面的这个i 

已知一个地址,想要访问那个地址上的变量

•使用运算符*,*是一个单目运算符,用来访问指针的值所表示的地址上的变量。

•可以做右值,也可以做左值

int k=*p;

*p=k+1; 

8.1.3 指针与数组:为什么数组传进函数后的sizeof不对了

 传入函数的数组成了什么?

•函数参数表的数组实际上是指针

•sizeof(a)==sizeof(int*);

•但是可以用数组的运算符[]进行运算

以下四种函数原型是等价的

•int sum(int *ar,int n);

•int sum(int *,int);

•int sum(int ar[],int n);

•int sum(int [],int); 

数组变量是特殊的指针

•数组变量本身表达地址,所以

  int a[10];int *p=a;//无需用&取地址 

•但是数组的单元表示的是变量,需要用&取地址

  int a[10];int *p=&a[1];

•a==&a[0]

•[]运算符可以对数组做,也可以对指针做:*p==p[0]==a[0]//p[0]意为如果我以为p所指的地方是个数组,那么它就是p所指位置上的第一个整数取出来作为p[0]。

•数组变量是const的指针,所以不能被赋值。

8.2 字符类型

8.2.1 字符类型

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

•用单引号表示的字符字面量:'a'、'1'。

•''也是一个字符

•printf和scanf中用%c来输入输出字符

ASCII表

•字母在其中顺序排列

•大写字母和小写字母分开排列,并不在一起

char c='A';
c++;//ASCII码加一,c='B'
printf("%c\n",c);//输出B

8.2.2 逃逸字符

字符 意义 字符 意义
\b 回退一格 \" 双引号
\t 到下一个表格位 \' 单引号

\n

换行 \\ 反斜杠本身
\r 回车

8.3 字符串

8.3.1 字符串

字符数组

•char world[]={'H','e','l','l','o','!'};

word[0] word[1] word[2] word[3] word[4] word[5]
H e l l o !

•这不是C语言的字符串,因为不能用字符串的方式做计算

字符串

•char world[]={'H','e','l','l','o','!','\0'};//多一个字符'\0'

•以0(整数0)结尾的一串字符(0和'\0'是一样的,但是和'0'不同)

•0标志字符串的结束,但它不是字符串的一部分(计算字符串长度是不包含这个0)

•字符串以数组的方式存在,以数组或指针的方式访问(更多是以指针的方式)

•string.h里有很多处理字符串的函数

word[0] word[1] word[2] word[3] word[4] word[5] word[6]
H e l l o ! \0

字符串变量

•char *str="Hello";//一个叫str的指针,它指向了一个字符数组,这里面放的内容是Hello

 char word[]= "Hello";//这有个叫word的字符数组,它里面的内容是Hello

 char line[10]="Hello";//这有个叫line的字符数组,大小有十个字节那么大,向里面放入Hello

"Hello"

 •"Hello"会被编译器变成一个字符数组放在某处,这个数组的长度是6,结尾还有表结束的0

8.3.2 字符串变量

char *s="Hello World!";

 •s是一个指针,初始化指向一个字符串常量

 •由于这个常量所在的地方(此处只读不可写),所以实际上s是const char* s

 •对s所指的字符串做写入会导致严重的后果

 •如果需要修改字符串,应该用数组

   char s[]="Hello World!";

字符串的两种写法:指针or数组

  •指针:char *s="Hello World!";这个字符串不知道在哪(用于处理参数、动态分配空间)

  •数组:char s[]="Hello World!";这个字符穿在这里,作为本地变量空间自动被回收。

如果要构造一个字符串——>数组

如果要处理一个字符串——>指针

char*是字符串?

   •字符串可以表达为char*的形式

   •char*不一定是字符串

   •本意是指向字符的指针,可能指向的是字符的数组(就像int*)

   •只有它所指的字符数组有结尾的0,才能说它所指的是字符串

8.4 字符串计算

8.4.1 字符串输入输出

字符串赋值

   char *t="title";

   char *s;

   s=t;

   •并没有产生新的字符串,只是让指针s指向t所指的字符串,对s的任何操作就是对t做的

字符串的输入输出

char string[8];

scanf("%s",string);

printf("%s",string); 

   •scanf读入一个单词(到空格、Tab或回车为止)

   •这里scanf是不安全的,因为不知道读入内容长度,可能会超过string[8]

如何安全的输入字符串?

char string[8];

scanf("%7s",string);

   •在%和s之间的数字表示最多允许读入的字符的数量,这个数字应该比数组的大小小一

   •下一个scanf会在上一个scanf结束之后再接着读入 

常见错误

char *string;//这里只是定义了一个指针变量

scanf("%s,string); 

   •误以为char*是字符串类型,定义了一个字符串类型的变量string就可以直接使用

   •由于没有对string初始化为0,所以不一定每次运行都出错

空字符串

char buffer[100]="";

   •这是一个空字符串,buffer[0]=='\0 '

char buffer[]="";

   •这个数组的长度只有1

 8.4.2 常见的字符串函数

头文件#include

 strlen

   •size_t strlen(const char*s);

   •返回s的字符串长度(不包括结尾的0)

strcmp

   •int strcmp(const *s1,const char *s2);

   •比较两个字符串,返回:

                                         0:s1==s2

                                    正数:s1 > s2

                                    负数:s1 < s2

strcpy

   •char *strcpy(char *restrict dst,const char *restrist src);

   •把src的字符串拷贝到dst

   •restrict表明src和dst不重叠

   •返回dst

strcut

   •char *strcat(char *restrict s1, char *restrict s2);

   •把s2拷贝到s1的后面,接成一个长的字符串

   •返回s1

   •s1必须具有足够的空间

strcpy和strcat都可能出现安全问题

   •如果目的地没有足够的空间? 

   •建议不要使用

安全版本:

 •char *strncpy(char *restrict dst,const char *restrist src,size_t n);

 •char *strcat(char *restrict s1, char *restrict s2,size_t n);//认为设定一个n,超过n的字符不进行复制粘贴

 •int strncmp(const *s1,const char *s2,size_t n);//只比较两字符串前n个字符

strchr字符串中找字符

  •char *strchr(const char *s,int c);//从左开始找,在s字符串中找到c字符第一次出现的位置

  •char *strrchr(const char *s,int c);//从右开始找,在s字符串中找到c字符第一次出现的位置

  •返回NULL表示没找到

你可能感兴趣的:(课程学习,C语言,c语言,开发语言)