当前位置:首页 >> 工学 >>

chap08


Chap 8 指针
8.1 8.2 8.3 8.4 8.5 寻找保险箱密码 狸猫换太子 冒泡排序 加密变换问题 任意个整数求和问题*

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

8.1 寻找保险箱密码
一个关于特工008寻找保险箱密码的故事… 关键点分析 ? 得到线索:0217单元的“虎跑号”寄存箱 ? 提示地址:1976单元 ? 找到目标:“龙井号”寄存箱 ? 取出内容:911

8.1 寻找密码的途径分析
? ? ?

密码911存放在某个寄存箱内,如果我们 知道这个寄存箱的名字,就能够找到密码 如果不知道密码所在的寄存箱名字,知道 该寄存箱的地址也照样能够取出密码 如果寄存箱的地址也不知道,但是有另外 一个地方存放这个寄存箱的地址,就能顺 藤摸瓜,间接找到密码

8.1 密码存放示意图
虎跑号 龙井号

1976
0217

911
1976

名字
地址 内容

虎跑号
0217 1976

龙井号
1976 911

例8-1 利用指针模拟寻找保险箱 密码的过程 The key is: 911
获取密码的两种方法 int main(void) { int key = 911; int *addr = NULL; addr = &key;
If I know the address of the key, I also can get it: 911

/* 变量key存放密码 */ /* 变量addr存放地址 */ /* 将key的地址赋给addr */ /* 通过变量key输出密码值*/ printf("The key is: %d\n", key); /* 通过变量key的地址来输出密码值 */ printf("If I know the address of the key, I also can get it: %d\n", *addr); return 0;

}

8.1.2 地址和指针-指针的概念
内存单元 地址 内容
1000 1002 1004 20 1 155

变量 直接访问:通过变量名访问
x y z

int x = 20, y = 1, z = 155; printf("%d", x;) 间接访问:通过另一个变量访问
把变量的地址放到另一变量中 使用时先找到后者 再从中取出前者的地址

2000 2002

1000

p

地址

指针变量

指针
内存单元 地址 内容
1000 1002 1004 20 1 155

变量
x y z

int x = 20, y = 1, z = 155; printf("%d", x;)

某个变量的地址 指向
指针变量:存放地址的变量

2000 2002

1000

p

地址

指针变量

8.1.3 指针变量的定义
类型名 * 指针变量名
指针变量所指向的变量的类型 指针声明符

int *p;
p 是整型指针,指向整型变量

float *fp;
fp 是浮点型指针,指向浮点型变量

char *cp;
cp 是字符型指针,指向字符型变量

指针变量的定义
类型名 * 指针变量名 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 p a *p &a 3 * 间接访问运算符,访问指针所指向的变量 *p:指针变量 p 所指向的变量

例8-2指针取地址运算和间接访问运算
# include <stdio.h> p int main (void) { int a = 3, *p; &a 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); printf("a=%d, *p=%d\n", a, *p); (*p)++; printf("a=%d, *p=%d\n", a, *p); return 0; }

a 3

*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 (3) &*p 与 &a 相同,是地址

说明

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

*p

例8-3 通过指针改变变量的值
# include <stdio.h> int main (void) { int a = 1, b = 2, t; int *p1, *p2;

p1
&a

a 2 1

*p1

p2
&b

b 1 2

*p2

p1 = &a; p2 = &b; printf ("a=%d, b=%d, *p1=%d, *p2=%d\n", a, b, *p1, *p2); t = *p1; *p1 = *p2; * p2 = t; printf ("a=%d, b=%d, *p1=%d, *p2=%d\n", a, b, *p1, *p2); return 0; a = 1, b = 2, *p1 = 1, *p2 = 2 a = 2, b = 1, *p1 = 2, *p2 = 1 }

2、赋值运算
int a = 3, *p1, *p2; p1 = &a;
把 a 的地址赋给 p1,即 p1 指向 a

p2 = p1;
p2 也指向 a

相同类型的指针才能相互赋值
p1 a 3 *p1 *p2

&a
p2 &a

例8-4 指针赋值
int main (void) { int a, b, c, *p1, *p2; a = 2; b = 4; c = 6;

p1 &c &a

a

2
b 4 c

*p2 *p1 *p2

p2
&b &a

6 p1 = &a; p2 = &b;

*p1

printf ("a=%d, b=%d, c=%d, *p1=%d, *p2=%d\n", a, b, c, *p1, *p2);

p2 = p1; p1 = &c;

a = 2; b = 4; c = 6; *p1 = 2, *p2 = 4

printf ("a=%d, b=%d, c=%d, *p1=%d, *p2=%d\n", a, b, c, *p1, *p2);

return 0;

a = 2; b = 4; c = 6; *p1 = 6, *p2 = 2

}

例8-5 int main (void ) a = 1; b = 2; *p1 = 1, *p2 = 2 { int a=1, b=2; a = 1; b = 2; *p1 = 2, *p2 = 1 int *p1 = &a, *p2 = &b, *pt; printf ("a=%d, b=%d, *p1=%d, *p2=%d\n", a, b, *p1, *p2); pt = p1; p1 = p2; p2 = pt; printf (“a=%d, b=%d, *p1=%d, *p2=%d\n”, a, b, *p1, *p2); return 0; } p1 a p1 a *p2 &b 1 *p1 &a 1 p2 b p2 b *p1 &a 2 *p2 &b 2 pt pt &a

8.1.5 指针变量的初始化

对指针的操作 / 对指针所指向变量的操作
*p1 和 *p2 的值都由 1 和 2 变成了 2 和 1 (1) 直接改变指针的值 (2) 改变指针所指变量的值
p1 &a p2 &b p1 &b p2 &a b 2 *p1 a 1 *p2 p2 &b p1 &a b 1 b 2 *p2 a 2 *p1 a 1 *p1

*p2

8.2 狸猫换太子
一个关于发生在北宋年间变量替换的故事… 关键点分析 ? 真宗和李玉未出生的孩子:小王子 ? 刘娥的阴谋:用狸猫替换掉孩子 ? 筹备工作:设计两套替换行动方案 ? 实施结果:行动一失败,行动二成功

before change, baby is 王子 例8-6 指针作为函数参数模拟狸猫换太子 after first action, baby is王子 int main(void) after 刚出生时是王子 */ { int baby = PRINCE; /* baby代表孩子, second action, baby is狸猫 printf("before change, baby is "); display(baby);

replace1(baby); /* 实施狸猫换太子第一次行动*/ printf("\n"); printf("after first action, baby is "); display(baby);

replace2(&baby); /* 实施狸猫换太子第二次行动*/ printf("\n"); printf("after second action, baby is "); display(baby); return 0;
} #define CIVET 0 定义狸猫值为0 void replace1(int baby) void display(int who) { if (who == CIVET) printf("狸猫"); else if (who == PRINCE) printf("王子"); }

{ baby = CIVET; }

#define PRINCE 1 void replace2(int *baby) { *baby = CIVET; } 定义王子值为1

8.2.2 指针作为函数的参数
函数参数包括实参和形参,两者的类型要一致,可以是指针类型。 如果实参是某个变量的地址,相应的形参就是指针。
int main (void) { int a = 1, b = 2; int *pa = &a, *pb = &b;

调用哪个函数,可以交换main () 中变量a和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; }

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

2
y 2 1

x 1 2

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

a 2 1
px

b
1 2

py

值传递,地址未变, 但存放的变量的值改变了

例8-7 swap3()
swap3 (&a, &b); void swap3 (int *px, int *py) { int *pt; pt = px; px = py; py = pt; }

a 1
px

b
2

py

值传递,形参指针的改变 不会影响实参

swap2 (&a, &b); 指针作为函数参数的应用 b a void swap2 (int *px, int *py) 1 2 { int t; 2 1 t = *px; *px = *py; py px After calling swap1: a=1, b=2 *py = t; After calling swap2: a=2, b=1 } After calling swap3: a=1, b=2 要通过函数调用来改变主调函数中某个变量的值:

(1) 在主调函数中,将该变量的地址或者指向该变量的 指针作为实参
(2) 在被调函数中,用指针类型形参接受该变量的地址 (3) 在被调函数中,改变形参所指向变量的值

通过指针实现函数调用返回多个值
例8-8 输入年和天数,输出对应的年、月、日。
例如:输入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-8
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 -= tab [leap][k]; *pmonth = k; *pday = yearday; } input year and yearday: 2000 61 2000-3-1

month

day

3

1

pmonth

pday

8.3 冒泡排序程序解析

void bubble (int a[ ], int n) { int i, j;

for( i = 1; i < n; i++ ) void swap2 (int *, int *); for (j = 0; j < n-i; j++ ) void bubble (int a[ ], int n); if (a[j] > a[j+1]) int main(void) swap2 (&a[j], &a[j+1]); { int n, a[8]; int i; } printf("Enter n (n<=8): "); void swap2 (int *px, int *py) scanf("%d", &n); { int t; printf("Enter a[%d] : ",n); for (i=0; i<n;i++) t = *px; *px = *py; *py = t; scanf("%d",&a[i]); } bubble(a, n); printf("After sorted, a[%d] = ", n); for (i=0; i<n; i++) Enter n (n<=8): 8 printf("%3d",a[i]); Enter a[8] : 7 3 66 3 -5 22 -77 2 return 0; After sorted, a[8] = -77 -5 2 3 3 7 22 66 }

8.3.2 数组和地址间的关系
int a[100], *p; 数组名代表一个地 址,它的值是数 组首元素的地址 (基地址) a+i 是距数组a的 基地址的第i个偏 移
地址 a a+1 3000 3002 内容 数组元素 a[0] a[1]

a+i
a+99 3198

a[i]
a[99] *(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];
p p+1 a a+1 3000 3002 a[0] a[1]

p+i

a+i
3198

a[i]
a[99]

p+99 a+99

p = a; sum = 0; for(i = 0; i < 100; i++) sum = sum + p[i];

&a[i] a+i
p+i &p[i]

a[i] 等价 *(a+i)
*(p+i) 等价 p[i]

用指针完成对数组的操作
int a[100], *p; 移动指针
地址 p p a a+1 3000 3002 内容 数组元素 a[0] a[1]

p

a+i
3198

a[i]
a[99]

p a+99

sum = 0; for(p = a; p <= &a[99]; p++) sum = sum + *p;

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

指针的算术运算和比较运算
地址 内容 数组元素
a[0] a[1]

double *p, *q; ? q-p

p q

a a+1

3000 3008

两个相同类型的指针相减,表示它们之间相隔的存储单元 的数目
? ? ?

p + 1 / p-1
指向下一个存储单元 / 指向上一个存储单元

其他操作都是非法的
指针相加、相乘和相除,或指针加上和减去一个浮点数

p<q
两个相同类型指针可以用关系运算符比较大小

例8-11 使用指针计算数组元素之和
地址 内容 数组元素

# include <stdio.h> p a 3000 a[0] int main(void) p a+1 3002 a[1] { int i, a[10], *p; long sum = 0; p a+i a[i] printf("Enter 10 integers: "); for(i = 0; i < 10; i++) p a+9 3018 a[9] scanf("%d", &a[i]); for(p = a; p <= a+9; p++) Enter 10 integers: 10 9 8 7 6 5 sum = sum + *p; 4321 printf("sum=%ld \n", sum); sum=55 return 0; }

8.3.3 数组名作为函数的参数
数组元素作为函数实参时,函数形参为变量
与变量作为函数实参相同,值传递
double fact (int n); int main(void ) { int a[5]={1, 4, 5, 7, 9}; int i, n = 5; double sum; sum = 0; for(i = 1; i <= n; i++ ) fact(a[i-1]); 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 ; } 1!+4!+5!+7!+9!

数组名作为函数的参数
? ?

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

int array[ ]

int sum (int *array, int n) { int i, s = 0; for(i=0; i<n; i++) s += array[i];
*(array+i) return(s);

}

int sum (int *array, int n) { int i, s = 0; for(i=0; i<n; i++) s += array[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
array

b[0]

sum(b, 5) sum(b, 3) sum(b+1, 3)

b[0]+b[1]+...+b[4] b[0]+b[1]+b[2] b[1]+b[2]+b[3] b[2]+b[3]+b[4]

b[5]

sum(&b[2], 3)

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

?

?

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

a[5]

指针实现
#include <stdio.h> int main(void) { int i, a[10],n; void reverse(int p[ ], int n);

数组元素 a[0] a[1]

内容

指针 p

printf("Enter n: "); a[9] pj scanf("%d", &n); printf("Enter %d integers: ", n); for(i = 0; i < n; i++) void reverse(int *p, int n) scanf("%d", &a[i]); { int *pj, t; reverse(a, n); for(pj=p+n-1; p<pj; p++, pj--){ for(i = 0; i < n; i++) printf("%d\t", a[i]); t=*p; *p=*pj; *pj=t; return 0; } } }

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

9 8 8 8 8 8
8 9 5 5 5 5

5
4

4
5

4
0

0
4

5 5 9 4 4 4
4 4 4 9 6 6

6
0

0
6

5

6 6 6 6 9 0
0 0 0 0 0 9

8

9 8 5 4 6 0

8 9 5 4 6 0

8 5 9 4 6 0

8 5 4 9 6 0

8 5 4 6 9 0

8 5 4 6 0 9

5 4 6 0 8

4 5 0 6

4 0 0 4 5 i=5 i=4

i=3 j=0 to 2

i=2 j=0 to 3 j=0 to 6-1-i

i=1 j=0 to 4

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

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

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

8.4.1 程序解析-加密
# define MAXLINE 100 void encrypt(char *s); int main (void) { char line [MAXLINE]; printf ("Input the string: "); gets(line); encrypt (line); return 0; }

void encrypt ( char *s) { for ( ; *s != '\0'; s++) if (*s == 'z') *s = 'a'; else *s = *s+1; }

printf (“After being encrypted: %s\n", line);

Input the string:hello hangzhou After being encrypted: ifmmp!ibohaipv

8.4.2 字符串和字符指针
?

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

char sa[ ] = "array"; char *sp = "point";
printf("%s ", sa); printf("%s ", sp);

printf("%s ", sa+2); printf("%s ", sp+3); printf("%s\n", string"+1);

printf("%s\n", "string");
array point string

ray nt tring

数组名sa、指针sp和字符串 "string" 的值都是 地址

字符数组与字符指针的重要区别
char sa[ ] = "This is a string"; char *sp = "This is a string";
sa T h i s sp i s Thi s a s t r i n g \0 i s a s t r i 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; scanf(“%s”, s);

定义指针时,先将它的初值置为空 char *s = NULL

加密函数的两种实现
void encrypt ( char *s) { for ( ; *s != '\0'; s++) if (*s == 'z') *s = 'a'; else *s = *s+1; } 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; }

8.4.3 常用的字符串处理函数
?

函数原型在 stdio.h 或 string.h 中给出

1、字符串的输入和输出
? 输入字符串:scanf(

)或gets( ) ? 输出字符串:printf( )或puts( ) ? stdio.h

字符串的输入
char str[80]; i = 0; while((str[i] = getchar( )) != '\n') i++; str[i] = '\0';

'\n' '' '\t'

(1) scanf("%s", str) 输入参数:字符数组名,不加地址符 遇回车或空格输入结束,并自动将输入的一串字符和 ‘\0’ 送入数组中

(2) gets(str) 遇回车输入结束,自动将输入的一串字符和 ‘\0’ 送入数 组中

字符串的输出
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-15 字符串输入输出函数示例
#include <stdio.h> #include <stdio.h> int main( ) int main( ) { char str[80]; { char str[80]; gets(str); scanf("%s", str); printf("%s", str); puts(str); printf("%s", "Hello"); puts("Hello"); return 0; return 0; } } Programming Programming ProgrammingHello

Programming
Programming is fun! ProgrammingHello Hello

Programming is fun!

Programming is fun!
Hello

2、字符串的复制、连接、比较、 求字符串长度
?字符串复制:strcpy(str1,

str2) ?字符串连接:strcat(str1, str2) ?字符串比较:strcmp(str1, str2) ?求字符串长度:strlen(str)
? string.h

字符串复制函数strcpy()
strcpy(str1, str2); 将字符串 str2 复制到 str1 中

\0
static char str1[20]; static char str2[20] = “happy”; strcpy(str1, str2); strcpy(str1, “world”);

h a p p y \0

str1中 h a p p y \0 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

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.

字符串比较函数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");
正整数 负整数 0

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 5 -4

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

strcmp(str1, str2) > 0 strcmp(str1, “hello”) < 0 strcmp(str1, str2) == 0
比较字符串的内容

字符串长度函数strlen
?

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

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

例8-16 求最小字符串 #include <string.h>
int main( ) 2 8 -1 99 0 { int i; min is –1 int x, min; scanf("%d", &x); min = x; for(i = 1; i < 5; i++){ scanf("%d", &x); if(x < min) min = x; }
printf("min is %d\n", min);

int main( ) { int i; char sx[80], smin[80]; scanf("%s", sx); strcpy(smin,sx); for(i = 1; i < 5; i++){ scanf("%s", sx); if(strcmp(sx, smin)<0) strcpy(smin,sx); }
printf("min is %s\n", smin);

return 0; }

return 0;
} tool key about zoo sea min is about

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

8.5.1 程序解析
Enter n: 10 int main ( ) { int n, sum, i, *p; Enter 10 integers: 3 7 12 54 2 –19 8 –1 0 15 printf("Enter n: "); The sum is 81 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; for (i = 0; i < n; i++) sum = sum + *(p+i); printf("The sum is %d \n",sum); free(p); return 0; }

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

动态存储管理
?

不是由编译系统分配的,而是由用户在程 序中通过动态分配获取。 使用动态内存分配能有效地使用内存
? 使用时申请 ? 用完就释放

?

同一段内存可以有不同的用途

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

动态存储分配函数malloc()
void *malloc(unsigned size)
在内存的动态存储区中分配一连续空间,其长度为 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)
释放由动态存储分配函数申请到的整块内存空间, ptr为指向要释放空间的首地址。

当某个动态分配的存储块不再用时,要及时 将它释放

分配调整函数realloc
void *realloc(void *ptr, unsigned size)
更改以前的存储分配 ? ptr必须是以前通过动态存储分配得到的指针 ? 参数size为现在需要的空间大小
? 如果调整失败,返回NULL,同时原来ptr指向存储块的

内容不变。 ? 如果调整成功,返回一片能存放大小为size的区块,并 保证该块的内容与原块的一致。如果size小于原块的大 小,则内容为原块前size范围内的数据;如果新块更大, 则原有数据存在新块的前一部分。 ? 如果分配成功,原存储块的内容就可能改变了,因此不 允许再通过ptr去使用它。


相关文章:
chap08-图块、外部参照与设计中心.ppt
chap08-图块、外部参照与设计中心_工学_高等教育_教育专区。AutoCAD
CHAP08信号的运算与处理电路_图文.ppt
CHAP08信号的运算与处理电路 - 8.1 基本运算电路 8.2 实际运放电路
Chap08_图文.ppt
Chap08 - 系统分析与设计方法(第七版) 英文ppt 高等教育出版社... Chap08_管理学_高等教育_教育专区。系统分析与设计方法(第七版) 英文ppt 高等教育出版社 ...
chap08_图文.ppt
chap08 - 第八章 可靠性与检验方法 第八章 1 可靠性原理 可靠性与检验
Chap08磁介质_图文.ppt
Chap08磁介质_理学_高等教育_教育专区。第八章 磁介质 8.1 介质的磁化
Chap08_图像理解(上).pdf
Chap08_图像理解(上) 图像分析与处理图像分析与处理隐藏>>
Chap08_形状表示与描述(下)_图文.pdf
Chap08_形状表示与描述(下) - §8.3 基于区域的形状表示与描述 ?
chap08--指针_图文.ppt
chap08--指针 - Chap 8 指针 8.1 8.2 8.3 8.4 8
chap08 指针_图文.ppt
chap08 指针 - c语言课件 何钦铭主编的配套教材... chap08
chap08ppt_图文.ppt
chap08ppt - Chapter 8 1 Based on annual
Chap08S_图文.ppt
Chap08S_工学_高等教育_教育专区。核辐射探测学清华大学课件 第八章
chap08-2_图文.ppt
chap08-2 - Chap 8 指针 指针(2) 万丽莉 计算机与信息技术学
Chap08_图像理解(下).pdf
Chap08_图像理解(下) 图像分析与处理图像分析与处理隐藏>>
chap08 数据备份和还原_图文.ppt
chap08 数据备份和还原 - Windows server 2003的网络管理... chap08 数据备份和还原_工学_高等教育_教育专区。Windows server 2003的网络管理 第8章 数据备份和还原...
CHAP08_图文.ppt
CHAP08 - 中文版AutoCAD 2008电气设计 清华大学出版社 第8章
CHAP08 可编程逻辑器件_图文.ppt
CHAP08 可编程逻辑器件 - 数电地方反复反复反复 若非规范行业u飞听见 反贪局... CHAP08 可编程逻辑器件_理学_高等教育_教育专区。数电地方反复反复反复 若非规范...
chap08 stata与模型修正剖析_图文.ppt
chap08 stata与模型修正剖析 - 第八章 stata与模型修正 主要内
Chap08_图像理解(中).pdf
Chap05_图像分割(上) Chap05_图像分割(下) Chap05_图像分割(中) Chap06_形状表示与描述(上... Chap06_形状表示与描述(下... Chap08_图像理解(上) Chap08...
chap08 stata与模型修正_图文.ppt
chap08 stata与模型修正_计算机软件及应用_IT/计算机_专业资料。第
chap08sdf_图文.ppt
chap08sdf - CHAPTER 8 Economic Growth II