0
点赞
收藏
分享

微信扫一扫

C Primer Plus 8-10章

年夜雪 2022-04-02 阅读 65
c语言

目录

第8章 字符输入/输出和输入验证

8.1 单字符I/O:getchar()和putchar()

getchar()和 putchar()每次只处理一个字符

while ((ch = getchar()) != '#')

使用 while 循环,当读到#字符时停止。

8.2 缓冲区

为什么要有缓冲区?

首先,把若干字符作为一个块进行传输比逐个发送 这些字符节约时间。

其次,如果用户打错字符,可以直接通过键盘修正错 误。当最后按下Enter键时,传输的是正确的输入。

第9章 函数

C的设计思想是,把函数用作构件块

9.1 函数概述

函数(function)是完成特定任务的独立程序代码单元,语法规则定义了函数的结构和使用方式。

1、创建并使用简单的函数
void starbar(void); /* 函数原型 */
int main(){
	...
    starbar(); /* 调用函数 */
}
void starbar(void) /* 定义函数 */{
	...
}

函数原型(function prototype)告诉编 译器函数starbar()的类型;

一般而言,函数原型指明了函数的返回值类型和函数接受的参数类型

第1个void是函数类型,void类型表明函数没有返回值;第2个void(在圆括号中)表明该函数不带参数。

函数调用(function call)表明在此处执行函数;

函数定义(function definition)明确地指定了函数要做什么

2、创建带有形参的函数
void show_n_char(char ch, int num);

形式参数也是局部变量,属该函数私有。

3、调用带实际参数的函数

实际参数和形式参数

  • 实际参数是出现在函数调用圆括号中的表达式。
  • 形式参数是函数定义的函数头中声明的变量。

调用函数时,创建了声明为形式参数的变量并初始化为实际参数的求值结果。

4、使用return从函数中返回值

关键字return后面的表达式的值就是函数的返回值

返回值不一定是变量的值,也可以是任意表达式的值。

return (n < m) ? n : m;

使用 return 语句的另一个作用是,终止函数并把控制返回给主调函数的 下一条语句。

5、函数类型

使用 return 语句的另一个作用是,终止函数并把控制返回给主调函数的 下一条语句。

函数类型指的是返回值的类型,不是函数参数的类型。

double klink(int a, int b);
返回值是double类型
9.2 ANSI C函数原型
9.3 递归

C允许函数调用它自己,这种调用过程称为递归

1、递归的基本原理
  1. 每级函数调用都有自己的变量
  2. 每次函数调用都会返回一次
  3. 递归函数中位于递归调用之前的语句,均按被调函数的顺序执行
  4. 递归函数中位于递归调用之后的语句,均按被调函数相反的顺序执行
  5. 递归调用非常类似于一个循环语句。 实际上,递归有时可用循环来代替,循环有时也能用递归来代替
2、尾递归

最简单的递归形式是把递归调用置于函数的末尾,即正好在 return 语句之前,这种形式的递归被称为尾递归。

3、递归的优缺点
  • 优点是递归为某些编程问题提供了最简单的解决方案
  • 缺点是一些递归算法会快速消耗计算机的内存资源
9.5 查找地址:&运算符

一元&运算符给出变量的存储地址

scanf()函数中就使用地址作为参数

9.7 指针简介

指针(pointer)是一个值为内存地址的变量

ptr = &pooh; // 把pooh的地址赋给ptr

要创建指针变量,先要声明指针变量的类型。

1、间接运算符:*
ptr = &bah;
val = *ptr; // 找出ptr指向的值

val = bah;
2、声明指针
int * pi; // pi是指向int类型变量的指针
char * pc; // pc是指向char类型变量的指针
float * pf, * pg; // pf、pg都是指向float类型变量的指针

*和指针名之间的空格可有可无。

pc指向的值(pc)是char类型。pc本身是什么类型?我们描述它的类型 是*“指向char类型的指针”**。

3、使用指针在函数间通信
void interchange(int * u, int * v);
int main(){
    ...
	interchange(&x, &y); // 把地址发送给函数
}

普通变量把值作为基本量,把地址作为通过&运算符获得的 派生量;

而指针变量把地址作为基本量,把值作为通过*****运算符获得的派生量。

第10章 数组和指针

10.1 数组

数组由数据类型相同的一系列元素组成

float candy[365]; /* 内含365个float类型元素的数组 */
char code[12]; /*内含12个char类型元素的数组*/
int states[50]; /*内含50个int类型元素的数组 */
1、初始化数组
int powers[8] = {1,2,4,6,8,16,32,64}; 
/* 从ANSI C开始支持这种初始化 */

用以逗号分隔的值列表(用花括号括起来)来初始化数组, 各值之间用逗号分隔,在逗号和值之间可以使用空格。

2、给数组元素赋值
int counter, evens[SIZE];
for (counter = 0; counter < SIZE; counter++)
	evens[counter] = 2 * counter;
	...
}

这段代码中,使用循环给数组的元素依次赋值。

C 不允许把数组作为 一个单元赋给另一个数组,除初始化以外也不允许使用花括号列表的形式赋值。

int oxen[SIZE] = {5,3,2,8}; /* 初始化没问题 */
int yaks[SIZE];
yaks = oxen; /* 不允许 */
yaks[SIZE] = oxen[SIZE]; /* 数组下标越界 */
3、数组边界

在使用数组时,要防止数组下标超出边界。也就是说,必须确保下标是有效的值。

数组元素的编号从0开始。最好是在声明数组时使用符号常量来表示数组的大小。

4、指定数组的大小

声明数组时只能在方括号中使用整型常量表达式。

整型常量表达式,是由整型常量构成的表达式。

10.2 多维数组
float rain[5][12]; 
// 内含5个数组元素的数组,每个数组元素内含12个
1、初始化二维数组

初始化二维数组是建立在初始化一维数组的基础上。

float rain[YEARS][MONTHS] =
{ {4.3,4.3,4.3,3.0,2.0,1.2,0.2,0.2,0.4,2.4,3.5,6.6},
{8.5,8.2,1.2,1.6,2.4,0.0,5.2,0.9,0.3,0.9,1.4,7.3},
...
};

这个初始化使用了数值列表,每个数值列表都用花括号括起来。

初始化时也可省略内部的花括号,只保留最外面的一对花括号。只要保证初始化的数值个数正确,初始化的效果与上面相同。

2、其他多维数组
int box[10][20][30];

通常,处理三维数组要使用3重嵌套循环,处理四维数组要使用4重嵌套循环。

10.3 指针和数组

如果flizny是一个数组,下面的语句成立:

 flizny == &flizny[0];
// 数组名是该数组首元素的地址

可以把它们赋值给 指针变量,然后可以修改指针变量的值:

int dates[y],*pti;
pti = dates;
//pti = & dates[0];

在指针前面使用*运算符可以得到该指针所指向对象的值。

指针加1,指针的值递增它所指向类型的大小:

dates + 2 == &date[2] // 相同的地址
*(dates + 2) == dates[2] // 相同的值
10.4 函数、数组和指针

以下两种函数原型是等价的:

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

因为数组名是该数组首元素的地址,作为实际参数的数组名要求形式参 数是一个与之匹配的指针

1、使用指针形参

是传递两个指针,第1个指针指明数组的开始处(与 前面用法相同),第2个指针指明数组的结束处:

int main(){
    int marbles[SIZE] = { 20, 10, 5, 39, 4, 16, 19, 26,31, 20 };
	answer = sump(marbles, marbles + SIZE);
    ...
}

int sump(int * start, int * end)
{
	int total = 0;
	while (start < end) //用来结束循环
	{
	total += *start; // 把数组元素的值加起来
	start++; // 让指针指向下一个元素
    //total += *start++; 
    //先把指针指向位置上的值加到total上,然后再递增指针
	}
}

指针start开始指向marbles数组的首元素,然后,表达式start++递增指针变量start,使其指向数组的下一个元素。

2、指针表示法和数组表示法

ar[i]和*(ar+1)这两个表达式都是等价的

但是,只有当ar是指针变量时,才能使用**ar++**这样的表达式

10.5 指针操作
int urn[5] = { 100, 200, 300, 400, 500 };

**赋值:**可以把地址赋给指针

ptr1 = urn; // 把一个地址赋给指针
ptr2 = &urn[1]; // 把一个地址赋给指针

**解引用:**运算符给出指针指向地址上储存的值

printf("ptr1 = %p, *ptr1 =%d, &ptr1 = %p\n", ptr1, *ptr1, &ptr1);

ptr1 = 000000000061FE00, *ptr1 =100, &ptr1 = 000000000061FDF8

**取址:**和所有变量一样,指针变量也有自己的地址和值

**指针与整数相加:**可以使用+运算符把指针与整数相加,或整数与指针相加。

ptr1 + 4 = 000000000061FE10, *(ptr1 + 4) = 500

**递增指针:**递增指向数组元素的指针可以让该指针移动至数组的下一个元素。

ptr1++;

ptr1 = 000000000061FE04, *ptr1 =200, &ptr1 = 000000000061FDF8

指针减去一个整数:可以使用-运算符从一个指针中减去一个整数。

**递减指针:**同递增指针

ptr2 = 000000000061FE00, *ptr2 = 100, &ptr2 = 000000000061FDF0

**指针求差:**可以计算两个指针的差值。

ptr2 = 000000000061FE04, ptr1 = 000000000061FE00, ptr2 - ptr1 = 1

通常,求差的两个指针分别指向 同一个数组的不同元素,通过计算求出两元素之间的距离。差值的单位与数组类型的单位相同。

10.6 保护数组中的数据
1、对形式参数使用const

如果函数的意图不是修改数组中的数据内容,那么在函数原型和函数定义中声明形式参数时应使用关键字const。

int sum(const int ar[], int n); /* 函数原型 */

该函数不能修改ar指向的数组中的内容

2、const的其他内容

虽然用#define指令可以创建类似功能的符号常量,但是const的用法更加灵活。

可以创建const数组、const指针和指向const的指针。

指向const的指针不能用于改变值:

double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5}; 
const double * pd = rates;
// pd指向数组的首元素
10.7 指针和多维数组
10.8 变长数组
10.9 复合字面量
举报

相关推荐

0 条评论