Chap 8 指针 8.1 寻找保险箱密码 8.2 角色互换 8.3 冒泡排序 8.4 电码加密 8.5 任意个整数求和*

Slides:



Advertisements
Similar presentations
电子成绩单项目实现.
Advertisements

Loops.
第九章 字串 (String).
補充: Input from a text file
第九章 指针 目录 指针与指针变量的概念 变量的指针和指向变量的指针变量 数组的指针和指向数组的指针变量
第8章 字元與字串處理 8-1 C語言的字元檢查函數 8-2 C語言的字串 8-3 字串的輸入與輸出 8-4 指標與字串
C语言程序设计 第八章 函数.
第九章 系 统 安 全 性 9.1 结构体 9.2 结构体型数组  9.3 结构体型指针 9.4 内存的动态分配 9.5 共用体
第一章 程序设计入门.
第六章 数 组 主讲教师 贾月乐 联系电话:
补充内容 结构体 概述 定义结构体类型和定义结构体变量 结构体变量的引用 结构体变量的初始化 指针与结构体 用typedef定义类型的别名.
Introduction to the C Programming Language
程序设计II 第三讲 字符串处理.
C 程式設計— 指標.
Chap 10 函数与程序结构 10.1 函数的组织 10.2 递归函数 10.3 宏定义 10.4 编译预处理.
项目六 用指针优化学生成绩排名 项目要求 项目分析
C 程式設計— 語言簡介 台大資訊工程學系 資訊系統訓練班.
Chap 2 用C语言编写程序 2.1 在屏幕上显示 Hello World! 2.2 求华氏温度 100°F 对应的摄氏温度
Introduction to the C Programming Language
目录 第八章 数组 1 简单学生成绩管理系统的开发 2 一维数组 3 多维数组 4 字符数组 5 数组作函数参数.
C++ 程式設計— 語言簡介 台大資訊工程學系 資訊系統訓練班.
STRUCTURE 授課:ANT 日期:2010/5/12.
计算概论 第十八讲 C语言高级编程 结构与习题课 北京大学信息学院.
Introduction to the C Programming Language
Introduction to the C Programming Language
Function.
C语言程序设计 李祥.
Chap 8 指针 8.1 寻找保险箱密码 8.2 狸猫换太子 8.3 冒泡排序 8.4 加密变换问题 8.5 任意个整数求和问题*
作弊是否很有诱惑性? 上堂课已经讲了 作业不一定在两个小时里都能完成 答疑没有一个人? 作弊是有记录的 心理系很多同学集体作弊,让人震惊
Chap 3 分支结构 3.1 简单的猜数游戏 3.2 四则运算 3.3 查询自动售货机中商品的价格.
第四章 C 语言中的输入和输出.
C语言 程序设计基础与试验 刘新国、2012年秋.
THE C PROGRAMMING LANGUAGE
字符串和字符数组 字符串的输入和输出 字符串的基本操作
第八章 使用指针.
Introduction to the C Programming Language
第十章 指针.
第5讲 结构化程序设计(Part II) 周水庚 2018年10月11日.
第七章 函数及变量存贮类型 7.1 函数基础与C程序结构 7.2 函数的定义和声明 7.3 函数的调用 7.4 函数的嵌套与递归
第4章 顺序程序设计.
数组 梁春燕 华电信息管理教研室.
C语言概述 第一章.
資料結構與C++程式設計進階 排序與搜尋 講師:林業峻 CSIE, NTU 6/ 14, 2010.
C语言复习3----指针.
C语言大学实用教程 第6章 数组 西南财经大学经济信息工程学院 刘家芬
函式庫補充資料.
第八章 指標 (Pointer).
C语言的特点 1. C程序由许多函数组成 2. C程序必须有且只有一个主函数main( ) 3. 函数用“{”和“}”表示起点和终点
指標
Chap 5 函数 5.1 计算圆柱体积 5.2 使用函数编写程序 5.3 变量与函数.
Chap 5 函数 5.1 计算圆柱体积 5.2 数字金字塔 5.3 复数运算.
C程序设计.
第4章 数 组.
第九章 指针.
第2章 基本数据及其运算 本章学习的目标: 1、掌握基本数据的各种表示,基本数据常数的书写方法;
第二章 类型、对象、运算符和表达式.
C程序设计.
Introduction to the C Programming Language
C/C++基礎程式設計班 字元與字串 講師:林業峻 CSIE, NTU 3/14, 2015.
Chap 7 数 组 7.1 排序问题 7.2 找出矩阵中最大值所在的位置 7.3 进制转换.
Introduction to the C Programming Language
Introduction to the C Programming Language
C/C++基礎程式設計班 C語言入門、變數、基本處理與輸入輸出 講師:林業峻 CSIE, NTU 3/7, 2015.
C/C++基礎程式設計班 陣列 講師:林業峻 CSIE, NTU 3/14, 2015.
C 程式設計— 字元與字串 台大資訊工程學系 資訊系統訓練班.
字串 第10章 part I 8/30/2019.
台大資訊工程學系 資料系統訓練班 第119期 吳晉賢
C语言基础学习 从外行到入门.
C++语言程序设计 C++语言程序设计 第二章 基本数据类型与表达式 第十一组 C++语言程序设计.
Presentation transcript:

Chap 8 指针 8.1 寻找保险箱密码 8.2 角色互换 8.3 冒泡排序 8.4 电码加密 8.5 任意个整数求和*

本章要点 变量、内存单元和地址之间是什么关系? 如何定义指针变量,怎样才能使用指针变量? 什么是指针变量的初始化? 指针变量的基本运算有哪些?如何使用指针操作所指向的变量? 指针作为函数参数的作用是什么? 如何使用指针实现函数调用返回多个值? 如何利用指针实现内存的动态分配?

8.1 寻找保险箱密码 一个关于国安局特工寻找保险箱密码的故事… 关键点分析 得到线索:地址为2170的房间内有线索 提示地址:1976 找到目标:地址为1976的房间 取出内容:911

8.1 寻找密码的途径分析 密码存放需要一定的存储空间作为存放地,每个存放地都会有地址 8.1 寻找密码的途径分析 密码存放需要一定的存储空间作为存放地,每个存放地都会有地址 如果知道了存放地的名字,当然能够找到密码。但并不是每个存储空间都有名字 如果不知道存放地的名字,知道该存放地的地址也能够取出密码 如果连存放地的地址也不知道,但是有另外一个地方存放了该密码存放地的地址,那么找到这个地方,就能顺藤摸瓜,间接找到密码

8.1 密码存放示意图 911 1976 P 2170 名字 P 地址 2170 1976 内容 911

例8-1 利用指针模拟寻找保险箱密码的过程 If I know the name of the variable, I can get it’s value by name: 911 If I know the address of the variable is:12ff7c, then I also can get it’s value by address: 911 获取密码的两种方法 int main(void) { int key = 911; /* 变量key用于存放密码值911 */ int *p_addr = NULL; /* 变量p_addr是整型指针变量 */   p_addr = &key; /* 将key的地址赋给p_addr */ /* 通过变量key输出密码值*/ printf("The key is: %d\n", key); /* 通过变量名key输出密码值*/ printf("If I know the name of the variable, I can get it’s value by name: %d\n ", key); /* 通过变量key的地址来输出密码值 */ printf("If I know the address of the variable is: %x, then I also can get it’s value by address: %d\n",p_addr, *p_addr);   return 0; }

8.1.2 地址和指针-指针的概念 内存单元 地址 内容 变量 直接访问:通过变量名访问 8.1.2 地址和指针-指针的概念 内存单元 地址 内容 变量 直接访问:通过变量名访问 1000 20 x 1002 1 y 1004 155 z int x = 20, y = 1, z = 155; printf("%d", x;) 间接访问:通过另一个变量访问 把变量的地址放到另一变量中 使用时先找到后者 再从中取出前者的地址 2000 1000 p 2002 地址 指针变量

指针 内存单元 int x = 20, y = 1, z = 155; 地址 内容 变量 printf("%d", x;) 某个变量的地址 地址 内容 变量 int x = 20, y = 1, z = 155; printf("%d", x;) 某个变量的地址 1000 20 x 1002 1 y 1004 155 z 指向 指针变量:存放地址的变量 2000 1000 p 2002 地址 指针变量

8.1.3 指针变量的定义 类型名 * 指针变量名 int *p; float *fp; char *cp; 8.1.3 指针变量的定义 类型名 * 指针变量名 指针声明符 指针变量所指向的变量的类型 int *p; p 是整型指针,指向整型变量 float *fp; fp 是浮点型指针,指向浮点型变量 char *cp; cp 是字符型指针,指向字符型变量

指针变量的定义 类型名 * 指针变量名 int * p; 指针变量名是 p,不是*p * 是指针声明符 int k, *p1, *p2; 类型名 * 指针变量名 int * p; 指针变量名是 p,不是*p * 是指针声明符 int k, *p1, *p2; 等价于: int k; int *p1; int *p2; 定义多个指针变量时,每一个指针变量前面都必须加上*

8.1.4 指针的基本运算 如果指针的值是某个变量的地址,通过指针就能间接访问那个变量。 1、取地址运算和间接访问运算 & 取地址运算符,给出变量的地址 int *p, a = 3; p = &a; 把 a 的地址赋给 p,即 p 指向 a 指针变量的类型和它所指向变量的类型相同 a 3 &a p *p * 间接访问运算符,访问指针所指向的变量 *p:指针变量 p 所指向的变量

例8-2指针取地址运算和间接访问运算 a 3 &a p *p # include <stdio.h> int main (void) { int a = 3, *p; p = &a; printf (“a=%d, *p=%d\n”, a, *p); *p = 10; printf("a=%d, *p=%d\n", a, *p); printf("Enter a: "); scanf("%d", &a); (*p)++;   return 0; } a 3 &a p *p a = 3, *p = 3 a = 10, *p = 10 Enter a: 5 a = 5, *p = 5 a = 6, *p = 6

说明 (1) 当 p = &a 后,*p 与 a 相同 (2) int *p; 定义指针变量 p *p =10; 指针p所指向的变量,即a *&a 与 a 相同,是变量 (4) (*p)++ 等价于 a++ 将 p 所指向的变量值加1 *p++ 等价于 *(p++) 先取 *p,然后 p 自加,此时p不再指向a int a = 1, x, *p; p = &a; x = *p++; a 3 &a p *p

2、赋值运算 int a = 3, *p1, *p2; p1 = &a; p2 = p1; 把 a 的地址赋给 p1,即 p1 指向 a 相同类型的指针才能相互赋值 a 3 &a p1 *p1 *p2 &a p2

8.1.5 指针变量的初始化 1) 指针变量在定义后也要先赋值再引用 2) 在定义指针变量时,可以同时对它赋初值 int a; int *p1 = &a; int *p2 = p1; 3) 不能用数值作为指针变量的初值,但可以将一 个指针变量初始化为一个空指针 int *p=1000; p = 0; p = NULL; p = (int*)1732; 使用强制类型转换 (int*) 来避免编译错误,不提倡

8.2 角色互换 如何通过函数调用实现代表2个角色的变量互相… 三套方案 swap1() swap2() swap3() 哪个方案能成功?

例8-3 指针作为函数参数模拟角色互换 调用哪个函数,可以交换main ()中变量a和b的值? int main (void) { int a = 1, b = 2; int *pa = &a, *pb = &b; void swap1(int x, int y), swap2( int *px, int *py ), swap3 (int *px, int *py); swap1 (a, b); printf (“After calling swap1: a=%d b=%d\n”, a, b);   a = 1; b = 2; swap2(pa, pb); printf (“After calling swap2: a=%d b=%d\n”, a, b); a = 1; b = 2; swap3(pa, pb); printf (“After calling swap3: a=%d b=%d\n”, a, b); return 0; } 调用哪个函数,可以交换main ()中变量a和b的值?

例8-3 swap1() swap1 (a, b); void swap1 (int x, int y) { int t; t = x; x = y; y = t; }

例8-3 swap2() swap2 (&a, &b); void swap2 (int *px, int *py) { int t; *px = *py; *py = t; }

例8-3 swap3() swap3 (&a, &b); void swap3 (int *px, int *py) { int *pt; pt = px; px = py; py = pt; } After calling swap1: a=1, b=2 After calling swap2: a=2, b=1 After calling swap3: a=1, b=2

8.2.2 指针作为函数参数 函数参数包括实参和形参,两者的类型要一致,可以是指针类型 如果实参是某个变量的地址,相应的形参就是指针 在C语言中实参和形参之间的数据传递是单向的“值传递”方式

例8-3 swap1() a 1 2 b swap1 (a, b); void swap1 (int x, int y) { int t; x = y; y = t; } x 1 2 y 2 1 在swap1()函数中改变了形参x,y的值 但不会反过来影响到实参的值 swap1()不能改变main()函数中实参a和b的值

例8-3 swap2() 值传递,地址未变, 但存放的变量的值改变了 swap2 (&a, &b); void swap2 (int *px, int *py) { int t; t = *px; *px = *py; *py = t; } a b px py 1 2 2 1 在swap2()函数中交换*px和*py的值,主调函数中a和b的值也相应交换了 值传递,地址未变, 但存放的变量的值改变了

例8-3 swap3() 值传递,形参指针的改变不会影响实参 swap3 (&a, &b); void swap3 (int *px, int *py) { int *pt; pt = px; px = py; py = pt; } a b px py 1 2 swap3()中直接交换了形参指针px和py的值 值传递,形参指针的改变不会影响实参

指针作为函数参数的应用 swap2 (&a, &b); void swap2 (int *px, int *py) { int t; *px = *py; *py = t; } a b px py 1 2 2 1 After calling swap1: a=1, b=2 After calling swap2: a=2, b=1 After calling swap3: a=1, b=2 要通过函数调用来改变主调函数中某个变量的值: (1) 在主调函数中,将该变量的地址或者指向该变量的指针作为实参 (2) 在被调函数中,用指针类型形参接受该变量的地址 (3) 在被调函数中,改变形参所指向变量的值

通过指针实现函数调用返回多个值 例8-4 输入年和天数,输出对应的年、月、日。 例8-4 输入年和天数,输出对应的年、月、日。 例如:输入2000和61,输出2000-3-1。 定义函数month_day(year, yearday, *pmonth, *pday) 用2个指针作为函数的参数,带回2个结果 int main (void) { int day, month, year, yearday; void month_day(int year,int yearday, int *pmonth,int *pday); printf(“input year and yearday: ”); scanf ("%d%d", &year, &yearday ); month_day (year, yearday, &month, &day ); printf ("%d-%d-%d \n", year, month, day ); return 0; }

例8-4 3 1 day month pmonth pday void month_day ( int year, int yearday, int * pmonth, int * pday) { int k, leap; int tab [2][13] = { {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, };   /* 建立闰年判别条件leap */ leap = (year%4 == 0 && year%100 != 0) || year%400 == 0; for ( k = 1; yearday > tab[leap][k]; k++) yearday = yearday-tab [leap][k]; *pmonth = k; *pday = yearday; } month day pmonth pday 3 1 input year and yearday: 2000 61 2000-3-1

8.3 冒泡排序-程序解析 void bubble (int a[ ], int n) { int i, j; for( i = 1; i < n; i++ ) for (j = 0; j < n-i; j++ ) if (a[j] > a[j+1]){ t=a[j]; a[j]=a[j+1]; a[j+1]=t; } void bubble (int a[ ], int n); int main(void) { int n, a[8]; int i; printf("Enter n (n<=8): "); scanf("%d", &n); printf("Enter a[%d] : ",n); for (i=0; i<n;i++) scanf("%d",&a[i]); bubble(a, n);   printf("After sorted, a[%d] = ", n); for (i=0; i<n; i++) printf("%3d",a[i]);  return 0; } Enter n (n<=8): 8 Enter a[8] : 7 3 66 3 -5 22 -77 2 After sorted, a[8] = -77 -5 2 3 3 7 22 66

8.3.2 数组和地址间的关系 int a[100], *p; 数组名代表一个地址,它的值是数组首元素的地址(基地址) a+i 是距数组a的基地址的第i个偏移 3000 a[0] 地址 内容 数组元素 3002 a[1] 3198 a[99] a[i] a a+1 a+99 a+i &a[i] *(a+i) sum = 0; for(i = 0; i < 100; i++) sum = sum + a[i] ; 下标运算符[ ]的含义 *(a+i)

指针和数组的关系 任何由数组下标来实现的操作都能用指针来完成 int a[100], *p; p = a; 或 p = &a[0]; 地址 内容 数组元素 3002 a[1] 3198 a[99] a[i] p p+1 p+99 p+i a a+1 a+99 a+i &a[i] a[i] a+i *(a+i) p = a; sum = 0; for(i = 0; i < 100; i++) sum = sum + p[i]; 等价 p+i *(p+i) &p[i] p[i] 等价

用指针完成对数组的操作 int a[100], *p; 移动指针 sum = 0; 地址 内容 数组元素 3002 a[1] 3198 a[99] a[i] p a a+1 a+99 a+i p p p sum = 0; for(p = a; p <= &a[99]; p++) sum = sum + *p;

例8-6 使用指针计算数组元素个数和数组元素的存储单元数 p q 3000 a[0] 地址 内容 数组元素 3008 a[1] a a+1 # include <stdio.h> int main (void) { double a[2], *p, *q; p = &a[0]; q = p + 1; printf ("%d\n", q - p); printf ("%d\n", (int) q - (int) p); return 0; } 1 8 指针p和q之间元素的个数 指针p和q之间的字节数 地址值

指针的算术运算和比较运算 double *p, *q; q - p p + 1 / p-1 其他操作都是非法的 p < q p q 3000 a[0] 地址 内容 数组元素 3008 a[1] a a+1 double *p, *q; q - p 两个相同类型的指针相减,表示它们之间相隔的存储单元的数目 p + 1 / p-1 指向下一个存储单元 / 指向上一个存储单元 其他操作都是非法的 指针相加、相乘和相除,或指针加上和减去一个浮点数 p < q 两个相同类型指针可以用关系运算符比较大小

例8-7 分别使用数组和指针计算数组元素之和 sum=0; 3000 a[0] 地址 内容 数组元素 3002 a[1] 3018 a[9] int main(void) { int i, a[10], *p; long sum = 0; printf("Enter 10 integers: ");   for(i = 0; i < 10; i++) scanf("%d", &a[i]); for ( i = 0; i < 10; i++) sum = sum + a[i]; printf("calculated by array, sum=%ld \n", sum); sum=0; for(p = a; p <= a+9; p++) sum = sum + *p; printf("calculated by pointer, sum=%ld \n", sum); return 0; } 3000 a[0] 地址 内容 数组元素 3002 a[1] 3018 a[9] a[i] a a+1 a+9 a+i p p p p Enter 10 integers: 10 9 8 7 6 5 4 3 2 1 calculated by array, sum=55 calculated by pointer, sum=55

8.3.3 数组名作为函数的参数 数组元素作为函数实参时,函数形参为变量 与变量作为函数实参相同,值传递 double fact (int n); int main(void ) { int i, n = 5; double sum; sum = 0; for(i = 1; i <= n; i++ ) sum = sum + fact (i); printf("sum = %e\n", sum); return 0; } double fact (int n) { int i; double result = 1; for (i = 1; i <= n; i++) result = result * i ; return result ; } int a[5]={1, 4, 5, 7, 9}; fact(a[i-1]); 1!+4!+5!+7!+9!

数组名作为函数的参数 数组名是指针常量,相当于指针作为函数的参数 数组名做为实参,形参是指针变量(数组) (1) 实参是数组名 (2) 形参是指针变量 可以写成数组形式 int a[ ] int sum (int *a, int n) { int i, s = 0; for(i=0; i<n; i++) s += a[i]; return(s); } 例 int main(void ) { int i; int b[5] = {1, 4, 5, 7, 9}; printf("%d\n", sum(b, 5)); return 0; } *(a+i)

b a int sum (int *a, int n) { int i, s = 0; for(i=0; i<n; i++) s += a[i]; return(s); } int main(void ) { int i; int b[5] = {1, 4, 5, 7, 9}; printf("%d\n", sum(b, 5)); return 0; } b b[0] b[5] sum(b, 5) b[0]+b[1]+...+b[4] a sum(b, 3) b[0]+b[1]+b[2] sum(b+1, 3) b[1]+b[2]+b[3] sum(&b[2], 3) b[2]+b[3]+b[4]

例8-8 将数组元素逆序存放 #include <stdio.h> int main(void) { int i, a[10],n; void reverse(int p[ ], int n); printf("Enter n: "); scanf("%d", &n); printf("Enter %d integers: ", n); for(i = 0; i < n; i++) scanf("%d", &a[i]); reverse(a, n); printf("%d\t", a[i]); return 0; } Enter n:10 Enter 10 integers: 10 9 8 7 6 5 4 3 2 1 1 2 3 4 5 6 7 8 9 10 void reverse(int p[ ], int n) { int i, j, t; for(i=0, j=n-1; i<j; i++, j--){ t = p[i]; p[i] = p[j]; p[j] = t; }

数组名做为函数的参数,在函数调用时,将实参数组首元素的地址传给形参(指针变量),因此,形参也指向实参数组的首元素。如果改变形参所指向单元的值,就是改变实参数组首元素的值。 或:形参数组和实参数组共用同一段存贮空间,如果形参数组中元素的值发生变化,实参数组中元素的值也同时发生变化。 a a[0] a[5] p

指针实现 数组元素 内容 指针 a[0] p #include <stdio.h> int main(void) 数组元素 内容 指针 a[1] a[9] pj #include <stdio.h> int main(void) { int i, a[10],n; void reverse(int p[ ], int n); printf("Enter n: "); scanf("%d", &n); printf("Enter %d integers: ", n); for(i = 0; i < n; i++) scanf("%d", &a[i]); reverse(a, n); printf("%d\t", a[i]); return 0; } void reverse(int *p, int n) { int *pj, t; for(pj=p+n-1; p<pj; p++, pj--){ t=*p; *p=*pj; *pj=t; }

8.3.4 冒泡排序算法分析 相邻两个数比较,小的调到前面,大的调到后面 9 8 8 8 8 8 5 4 4 0 8 9 5 5 5 5 4 5 0 4 5 5 9 4 4 4 6 0 5 4 4 4 9 6 6 0 6 6 6 6 6 9 0 8 0 0 0 0 0 9

9 8 8 8 8 8 5 4 4 0 8 9 5 5 5 5 4 5 0 4 5 5 9 4 4 4 6 0 5 4 4 4 9 6 6 0 6 6 6 6 6 9 0 8 0 0 0 0 0 9 i=1 i=2 i=3 i=4 i=5 j=0 to 2 j=0 to 3 j=0 to 4 j=0 to 6-1-i a[j]>a[j+1]

int main(void ) { int i, j, n, t, a[10]; n = 6; for(i = 0; i < n; i++) scanf("%d", &a[i]); for(i = 1; i < n; i++) for(j = 0; j < n-i; j++) if(a[j] > a[j+1]) { t = a[j]; a[j] = a[j+1]; a[j+1] = t; } return 0; 9 8 5 4 6 0 i=1 j=0: 8 9 5 4 6 0 j=1: 8 5 9 4 6 0 j=2: 8 5 4 9 6 0 j=3: 8 5 4 6 9 0 j=4: 8 5 4 6 0 9

void sort(int *array, int n) { int i, j, t; for(i=1; i<n; i++) for(j=0; j<n-i; j++) if(array[j]>array[j+1]){ t = array[j]; array[j] = array[j+1]; array[j+1] = t; } int main(void ) { int i, a[10]; for(i=0; i<10; i++) scanf("%d", &a[i]); sort(a, 10); printf("%d ", a[i]); printf("\n"); return 0; }

8.4 电码加密 字符串:字符数组 字符指针 8.4.1 程序解析 8.4.2 字符串和字符指针 8.4.3 常用的字符串处理函数

8.4.1 程序解析-加密 void encrypt ( char *s) { for ( ; *s != '\0'; s++) if (*s == 'z') *s = 'a'; else *s = *s+1; } # define MAXLINE 100 void encrypt(char *); int main (void) { char line [MAXLINE]; printf ("Input the string: "); gets(line); encrypt (line); printf (“%s%s\n”,“After being encrypted: ”, line); return 0; } Input the string:hello hangzhou After being encrypted: ifmmp!ibohaipv

8.4.2 字符串和字符指针 字符串常量 "array" "point" 用一对双引号括起来的字符序列 被看做一个特殊的一维字符数组,在内存中连续存放 实质上是一个指向该字符串首字符的指针常量 char sa[ ] = "array"; char *sp = "point";

数组名sa、指针sp和字符串 "string" 的值都是地址 char sa[ ] = "array"; char *sp = "point"; printf("%s ", sa); printf("%s ", sp); printf("%s\n", "string"); printf("%s ", sa+2); printf("%s ", sp+3); printf("%s\n", string"+1); ray nt tring array point string 数组名sa、指针sp和字符串 "string" 的值都是地址

字符数组与字符指针的重要区别 char sa[ ] = "This is a string"; char *sp = "This is a string"; sa T h i s a t r n g \0 sp T h i s a t r n g \0 如果要改变数组sa所代表的字符串,只能改变数组元素的内容 如果要改变指针sp所代表的字符串,通常直接改变指针的值,让它指向新的字符串

示例 char sa[ ] = "This is a string"; char *sp = "This is a string"; strcpy (sa, "Hello"); sp = "Hello"; sa = “Hello”; 非法 数组名是常量,不能对它赋值

字符指针-先赋值,后引用 定义字符指针后,如果没有对它赋值,指针的值不确定。 char *s ; scanf(“%s”, s); char *s, str[20]; s = str; 定义指针时,先将它的初值置为空 char *s = NULL 不要引用未赋值的指针

加密函数的两种实现 void encrypt (char s[ ]) void encrypt ( char *s) { { int i; for(i = 0; s[i] != '\0'; i++) if (s[i] == 'z') s[i] = 'a'; else s[i] = s[i]+1; } void encrypt ( char *s) { for ( ; *s != '\0'; s++) if (*s == 'z') *s = 'a'; else *s = *s+1; }

8.4.3 常用的字符串处理函数 函数原型在 stdio.h 或 string.h 中给出 1、字符串的输入和输出 输入字符串:scanf( )或gets( ) 输出字符串:printf( )或puts( ) stdio.h

字符串的输入 '\n' ' ' '\t' char str[80]; i = 0; while((str[i] = getchar( )) != '\n') i++; str[i] = '\0'; (1) scanf("%s", str) 输入参数:字符数组名,不加地址符 遇回车或空格输入结束,并自动将输入的一串字符和 ‘\0’ 送入数组中 (2) gets(str) 遇回车输入结束,自动将输入的一串字符和 ‘\0’ 送入数组中 '\n' ' ' '\t'

字符串的输出 char str[80]; for(i = 0; str[i] != ‘\0 ’; i++) putchar(str[i]); (3) printf("%s", str) printf("%s", "hello"); (4) puts(str) puts("hello"); 输出字符串后自动换行 输出参数可以是字符数组名或字符串常量,输出遇 '\0' 结束

例8-10 字符串输入输出函数示例 #include <stdio.h> #include <stdio.h> int main( ) { char str[80]; gets(str); puts(str); puts("Hello"); return 0; } #include <stdio.h> int main( ) { char str[80]; scanf("%s", str); printf("%s", str); printf("%s", "Hello"); return 0; } Programming ProgrammingHello Programming Hello Programming is fun! Hello Programming is fun! ProgrammingHello

2、字符串的复制、连接、比较、 求字符串长度 字符串复制:strcpy(str1, str2) 字符串连接:strcat(str1, str2) 字符串比较:strcmp(str1, str2) 求字符串长度:strlen(str) string.h

字符串复制函数strcpy() \0 h a p p y \0 h a p p y \0 w o r l d \0 strcpy(str1, str2); 将字符串 str2 复制到 str1 中 static char str1[20]; static char str2[20] = “happy”; \0 h a p p y \0 strcpy(str1, str2); str1中 h a p p y \0 strcpy(str1, “world”); str1中: w o r l d \0

strcpy() 示例 # include "stdio.h" # include "string.h" int main(void ) { char str1[20], str2[20]; gets(str2); strcpy(str1,str2); puts(str1); return 0; } 1234

字符串连接函数strcat strcat(str1, str2); 连接两个字符串str1和str2, 并将结果放入str1中 # include "stdio.h" # include "string.h" int main(void) { char str1[80], str2[20]; gets(str1); gets(str2); strcat(str1, str2); puts(str1); return 0; } str1中: Let us \0 str2中:go.\0 str1中: Let us go.\0 str2中:go.\0 Let us go. Let us go. str1=str1+str2 非法!

字符串比较函数strcmp strcmp(str1, str2) 比较 两个字符串str1和str2的大小。 规则:按字典序(ASCII码序) 如果 str1 和 str2 相等,返回 0; 如果 str1 大于 str2 ,返回一个正整数; 如果 str1 小于 str2 ,返回一个负整数; static char s1[20] = "sea"; strcmp(s1, "Sea"); strcmp("Sea", "Sea "); strcmp("Sea", "Sea"); 正整数 负整数

strcmp() 示例 # include "stdio.h" # include "string.h" int main(void ) { int res; char s1[20], s2[20]; gets(s1); gets(s2); res = strcmp(s1, s2); printf("%d", res); return 0; } 1234 2 -1

用strcmp()比较字符串 利用字符串比较函数比较字符串的大小 strcmp(str1, str2); 为什么定义这样的函数? strcmp(str1, "hello") < 0 strcmp(str1, str2) == 0 str1 > str2 str1 < "hello" str1 == str2 比较字符串首元素的地址 比较字符串的内容

字符串长度函数strlen strlen(str) 计算字符串的有效长度,不包括 ‘\0’。 static char str[20]="How are you?" strlen ("hello") 的值是: strlen(str) 的值是: 5 12

字符串处理函数小结 函数 功能 头文件 puts(str) 输出字符串 stdio.h gets(str) 输入字符串(回车间隔) 函数 功能 头文件 puts(str) 输出字符串 stdio.h gets(str) 输入字符串(回车间隔) strcpy(s1,s2) s2 ==> s1 strcat(s1,s2) s1 “+” s2 ==> s1 若 s1“==”s2, 函数值为0 strcmp(s1,s2) 若 s1 “>” s2, 函数值 >0 string.h 若 s1 “<” s2, 函数值<0 计算字符串的有效长度, strlen(str) 不包括 ‘\0’

例8-11 求最小字符串 #include <string.h> int main( ) { int i; char sx[80], smin[80]; scanf("%s", sx); strcpy(smin,sx); for(i = 1; i < 5; i++){ if(strcmp(sx, smin)<0) } printf("min is %s\n", smin); return 0; int main( ) { int i; int x, min; scanf("%d", &x); min = x; for(i = 1; i < 5; i++){ if(x < min) } printf("min is %d\n", min); return 0; 2 8 -1 99 0 min is –1 tool key about zoo sea min is about

8.5 任意个整数求和 * 例8-12 先输入一个正整数n,再输入任意n个整数,计算并输出这n个整数的和。 8.5 任意个整数求和 * 例8-12 先输入一个正整数n,再输入任意n个整数,计算并输出这n个整数的和。 要求使用动态内存分配方法为这n个整数分配空间。

8.5.1 程序解析 int main ( ) { int n, sum, i, *p; printf("Enter n: "); scanf("%d", &n); if ((p = (int *) calloc (n, sizeof(int))) == NULL) { printf("Not able to allocate memory. \n"); exit(1); } printf("Enter %d integers: ", n); for (i = 0; i < n; i++) scanf("%d", p+i); sum = 0; sum = sum + *(p+i); printf("The sum is %d \n",sum); free(p);   return 0; Enter n: 10 Enter 10 integers: 3 7 12 54 2 –19 8 –1 0 15 The sum is 81

8.5.2 用指针实现内存动态分配 变量在使用前必须被定义且安排好存储空间 8.5.2 用指针实现内存动态分配 变量在使用前必须被定义且安排好存储空间 全局变量、静态局部变量的存储是在编译时确定,在程序开始执行前完成。 自动变量,在执行进入变量定义所在的复合语句时为它们分配存储,变量的大小也是静态确定的。 一般情况下,运行中的很多存储要求在写程序时无法确定。

动态存储管理 不是由编译系统分配的,而是由用户在程序中通过动态分配获取。 使用动态内存分配能有效地使用内存 同一段内存可以有不同的用途 使用时申请 用完就释放 同一段内存可以有不同的用途

动态内存分配的步骤 (1)了解需要多少内存空间 (2)利用C语言提供的动态分配函数来分配所需要的存储空间。 (3)使指针指向获得的内存空间,以便用指针在该空间内实施运算或操作。 (4)当使用完毕内存后,释放这一空间。

动态存储分配函数malloc() void *malloc(unsigned size) 若申请成功,则返回一个指向所分配内存空间的起始地址的指针 若申请内存空间不成功,则返回NULL(值为0) 返回值类型:(void *) 通用指针的一个重要用途 将malloc的返回值转换到特定指针类型,赋给一个指针

malloc()示例 /* 动态分配n个整数类型大小的空间 */ if ((p = (int *)malloc(n*sizeof(int))) == NULL) { printf(“Not able to allocate memory. \n”); exit(1); } 调用malloc时,用 sizeof 计算存储块大小 每次动态分配都要检查是否成功,考虑例外情况处理 虽然存储块是动态分配的,但它的大小在分配后也是确定的,不要越界使用。

计数动态存储分配函数calloc () void *calloc( unsigned n, unsigned size) 在内存的动态存储区中分配n个连续空间,每一存储空间的长度为size,并且分配后还把存储块里全部初始化为0 若申请成功,则返回一个指向被分配内存空间的起始地址的指针 若申请内存空间不成功,则返回NULL malloc对所分配的存储块不做任何事情 calloc对整个区域进行初始化

动态存储释放函数free void free(void *ptr) 当某个动态分配的存储块不再用时,要及时将它释放

分配调整函数realloc void *realloc(void *ptr, unsigned size) 更改以前的存储分配 如果调整失败,返回NULL,同时原来ptr指向存储块的内容不变。 如果调整成功,返回一片能存放大小为size的区块,并保证该块的内容与原块的一致。如果size小于原块的大小,则内容为原块前size范围内的数据;如果新块更大,则原有数据存在新块的前一部分。 如果分配成功,原存储块的内容就可能改变了,因此不允许再通过ptr去使用它。

本章要点小结 变量、内存单元和地址之间是什么关系? 如何定义指针变量,怎样才能使用指针变量? 什么是指针变量的初始化? 指针变量的基本运算有哪些?如何使用指针操作所指向的变量? 指针作为函数参数的作用是什么? 如何使用指针实现函数调用返回多个值? 如何利用指针实现内存的动态分配?