数组、指针和动态内存分配是编程的必备技术,一定要掌握它们的正确使用方法。纵观一下,需要深入理解并正确使用的有如下几点:
(1)越界。一定要注意防止在使用时产生越界行为。
(2)限制。限制程序脱离控制以避免对系统造成伤害。
这两点的意思是:不但自己用好,还不要去干扰系统,否则可能造成系统崩溃。
对字符串数组要特别注意的是赋值问题。字符串和指针本身都可以有空格,关键是赋值。不能使用scanf语句读入数据,因为它不允许读入空格。
要注意中文的特点,避免不必要的错误。
【例18.15】分析下面程序的输出结果。
#include <stdio.h> int main ( ) { int i ; char s=\" 数 学\" ,*cp=s ; printf (\"%sn\" ,cp ); printf (\"%cn\" ,*cp ); for (i=0 ;i<8 ;i++ ) printf (\"%c\" ,* (cp+i )); printf (\"n\" ); return 0 ; }
这是对中文字符编码理解不清引起的错误。一个中文字符相当于两个英文字符,如果对中文输出一个字符,就不是所要的输出信息。for循环语句超过边界,会输出无用的字符。字符串有效的字符是6个。如果要输出单个中文“数”字,下面两种方式是等效的。
printf (\"%c%cn\" ,cp[0] ,cp[1] ); printf (\"%c%cn\" ,*cp ,* (cp+1 ));
修改后的程序如下:
#include <stdio.h> int main ( ) { int i ; char s=\" 数 学\" ,*cp=s ; printf (\"%sn\" ,cp ); printf (\"%c%cn\" ,cp[0] ,cp[1] ); for (i=0 ;i<6 ;i++ ) printf (\"%c\" ,* (cp+i )); printf (\"n\" ); return 0 ; }
程序运行输出结果如下:
数 学 数 数 学
【例18.16】在下面的程序中,要求输入“See you tomorrow!”,输出也是同样内容。程序能达到要求吗?
#include <stdio.h> void main ( ) { char st[18] ; scanf (\"%s\" ,st ); printf (st ); printf (\"n\" ); }
不是的。输出是“See”。scanf语句的读入是以空格结束的,所以它只取第1个连续字符作为输入。要想得到正确的结果,必须放弃scanf函数。例如使用gets函数。下面是正确的程序。
#include <stdio.h> void main ( ) { char st[18] ; printf (\" 输入:\" ); gets (st ); printf (\" 输出:%sn\" ,st ); }
运行示范如下:
输入: See you tomorrow ! 输出:See you tomorrow !
【例18.17】下面的程序能实现将输入字符串的内容复制到指针变量t中吗?
#include <stdio.h> #include <malloc.h> void main ( ) { int i=0 ; char a[100] , *t ; gets (a ); while ( (t[i]= a[i] )!=\'\0\' ) i++ ; printf ( \"%sn\" , t ); }
不能。因为指针变量没有初始化。可以为它申请一块内存来完成初始化,而且要判断是否申请成功,使用完之后也要及时释放。
// 完整的程序 #include <stdio.h> #include <malloc.h> #include <stdlib.h> int main ( ) { int i=0 ; char a[100] , *t ; t= (char* )malloc (100*sizeof (char )); if (t== NULL ) { printf ( \" 内存分配错误!n\" ); exit (1 ); } gets (a ); while ( (t[i]= a[i] )!=\'\0\' ) i++ ; printf ( \"%sn\" , t ); free (t ); return 0 ; }
【例18.18】假设给定班级各科考试平均成绩的原始资料如下:
数学:75
物理:80
外语:83
政治:85
体育:86
人数:30
要求统计出全班学期总平均成绩以及得分最低的科目和该科目的成绩。
要求的输出结果如下:
原始信息如下:
数学:75
物理:80
外语:83
政治:85
体育:86
人数:30
平均成绩:0
最低分数科目的成绩:0
最低分数的科目:
全班各科平均成绩如下:
数学:75
物理:80
外语:83
政治:85
体育:86
统计结果如下:
人数:30
平均成绩:81
最低分数科目的成绩:75
最低分数的科目:数学
这里不使用数组而使用动态内存,这种非数组的内存区域可以起到数组的作用。为此设计一个字符串指针数组,为每一个字符串指针数组申请一块内存空间,存放所提供的字符串。例如科目的名称,可以声明指针数组“char*pcn[6];”,使用
for (i=0 ;i<6 ;i++ ) pcn[i]= (char * )malloc (4*sizeof (char ));
循环语句分别为每个指针申请内存,用以存储“数学”和“物理”等名称。
(1)使用整型指针申请内存来存放数学、物理、外语、政治、体育、人数、平均成绩、最低分数科目的成绩。
(2)使用字符串指针数组pcn申请内存来存放数学、物理、外语、政治、体育、最低分数的科目。
(3)使用字符串指针数组pcm申请内存来存放人数、平均成绩、最低分数科目的成绩、最低分数的科目。
// 全部使用动态内存的程序 #include<stdio.h> #include<stdlib.h> #include<string.h> int main ( ) { int i ,*p ,min=100 ,sum=0 ; int a[8]={75 ,80 ,83 ,85 ,86 ,30} ; char *pcn[6] , *pcm[4] ; // 为科目名称申请内存 for (i=0 ;i<6 ;i++ ) { pcn[i]= (char * )malloc (4*sizeof (char )); if (pcn[i]==NULL ) { printf (\" 内存分配错误\" ); exit (1 ); } } // 为科目赋值 strcpy (pcn[0] ,\" 数学\" ); strcpy (pcn[1] ,\" 物理\" ); strcpy (pcn[2] ,\" 外语\" ); strcpy (pcn[3] ,\" 政治\" ); strcpy (pcn[4] ,\" 体育\" ); // 为栏目申请内存 for (i=0 ;i<4 ;i++ ) { pcm[i]= (char * )malloc (20 ); if (pcm[i]==NULL ) { printf (\" 内存分配错误\" ); exit (1 ); } } // 为栏目赋值 strcpy (pcm[0] ,\" 人数\" ); strcpy (pcm[1] ,\" 平均成绩\" ); strcpy (pcm[2] ,\" 最低分数科目的成绩\" ); strcpy (pcm[3] ,\" 最低分数的科目\" ); // 为分数分配内存地址 p= (int * )malloc (8*sizeof (int )); if (p==NULL ) { printf (\" 内存分配错误\" ); exit (1 ); } // 存储初始值 p[0]=75 ; p[1]=80 ; p[2]=83 ; p[3]=85 ; p[4]=86 ; p[5]=30 ; p[6]=0 ; p[7]=0 ; // 输出原始信息 printf (\" 原始信息如下:n\" ); for (i=0 ;i<5 ;i++ ) printf (\"%s :%dn\" ,pcn[i] ,* (p+i )); for (i=0 ;i<3 ;i++ ){ printf (\"%s :%dn\" ,pcm[i] ,p[i+5] ); } printf (pcm[3] ); printf (\"n\" ); // 计算最小值和平均值 for (i=0 ;i<5 ;i++ ) { sum=sum+p[i] ; if (min>p[i] ) min=p[i] ; } p[6]=sum/5 ; p[7]=min ; // 填写最低分数的科目名称 for (i=0 ;i<5 ;i++ ) if (min==* (p+i )) { strcpy (pcn[5] ,pcn[i] ); break ; } printf (\"n 全班各科平均成绩如下:n\" ); for (i=0 ;i<5 ;i++ ) printf (\"%s :%dn\" ,pcn[i] ,p[i] ); // 输出信息 printf (\"n 统计结果如下:n\" ); for (i=0 ;i<3 ;i++ ) { printf (\"%s :%dn\" ,pcm[i] ,* (p+i+5 )); } printf (\"%s :%sn\" ,pcm[3] ,pcn[5] ); free (p ); return 0 ; }
【例18.19】使用二级字符串指针和指向二维字符串的一维字符指针实现上述程序。
(1)使用整型数组a存放数学、物理、外语、政治、体育、人数、平均成绩、最低分数科目的成绩。
(2)使用字符串数组name存放数学、物理、外语、政治、体育、最低分数的科目。
(3)使用字符串数组MeanLow存放人数、平均成绩、最低分数科目的成绩、最低分数的科目。
(4)为MeanLow设计一个二级指针:
char **pcm ;
让它指向MeanLow,就可很方便地使用指针pcm来实现存取。
pcm=MeanLow ; for (i=0 ;i<3 ;i++ ) printf (\"%s :%dn\" ,pcm[i] ,* (p+i+5 ));
(5)定义一个指向一维数组的指针变量来实现对数组name的存取。语句
char (*pcn )[5] ;
定义指针变量pcn,因为指向name的一维数组,所以不能使用name[0],而应使用“p=name;”语句初始化。
pcn=name ; for (i=0 ;i<5 ;i++ ) printf (\"%s :%dn\" ,pcn[i] ,* (p+i )); // 完整的程序 #include<stdio.h> #include<stdlib.h> int main ( ) { int i ,sum=0 ,min=100 ,*p ; char **pcm ; char (*pcn )[5] ; int a[8]={75 ,80 ,83 ,85 ,86 ,30} ; char *MeanLow[4]={\" 人数\" ,\" 平均成绩\" ,\" 最低分数科目的成绩\" , \" 最低分数的科目\"} ; char name[5][5]={\" 数学\" ,\" 物理\" ,\" 外语\" ,\" 政治\" ,\" 体育\"} ; for (i=0 ;i<5 ;i++ ) printf (\"%s :%dn\" ,name[i] ,a[i] ); for (i=0 ;i<3 ;i++ ) printf (\"%s :%dn\" ,MeanLow[i] ,a[i+5] ); printf (\"%s :n\" ,MeanLow[i] ); // 计算 p=a ; for (i=0 ;i<5 ;i++ ) { sum=sum+p[i] ; if (min>p[i] ) min=p[i] ; } p[6]=sum/5 ; p[7]=min ; pcm=MeanLow ; pcn=name ; // 输出信息 printf (\"n 全班各科平均成绩如下:n\" ); for (i=0 ;i<5 ;i++ ) printf (\"%s :%dn\" ,pcn[i] ,* (p+i )); printf (\"n 统计结果如下:n\" ); for (i=0 ;i<3 ;i++ ) printf (\"%s :%dn\" ,pcm[i] ,* (p+i+5 )); return 0 ; }