概述
介绍数组和结构、函数。指针,以运算和存储(定义)为主线。这也是计算机这个黑盒子中的关键三环中的两环。(存储、运算、控制)
第四章 数组和结构
4.1数值数组:
二维数组a[i][j],除了行与列的解释,还可以i代表第i个一维数组,j代表一维数组中第j个元素。
4.1.1
运算:(1)二维数组不可以缺省列的个数。int a[][3]={1,2,3,4,5,6}就是可行的赋值的,区别于,函数的形参与实参中,第二维
必须相同int arry[][4]。
(2)对一维数组而言,a是数组名,可视为列指针,a+1就是跳一列。
(3)对二维数组而言,可视为行指针,a+1就是跳一行。a[0]、a[1]这些都应该视为列指针。
(4)a是地址常量,不可以a++或者a=a+2;二维的a[0]、a[1]也是不可以进行赋值操作,都是地址常量。
4.2字符数组
赋值:(1)字符赋值char s[5]={‘a’,‘a’,‘a’,‘a’,‘a’};(2)字符串常量赋值char s[5]=“aaaaa”;或char s[]=“aaaaa”;
未赋值部分默认为’ ’,ASCII为0;
4.3输出输入
注:
(1)gets可以读取空格,但是scanf不可以;puts自带换行功能,输出一个字符串后就会换一行。
(2)改错题中,常出现,为加’ ’作为字符串结束标志。
(3)对于s[]和s[n],可以通过修改’ ’来增加字符数组长度。
(4)对于s[n],只可以放n-1个字符。
4.3.1 字符串函数
注:
(1)结构体中范式没有明确初值的成员,都默认为0;
(2)atoi、atof装换字符串为整数/浮点数
(3)优先级的细节,++p->age,(++p)->age,&p->age,第一个是age++;第二个是p++,第三是age的地址。
4.4 结构体与函数/指针
注:改错题中,会出现结构体定义结尾没有‘;’,do…while();也是!
将一个结构体变量中的数据传递给另一个函数,有下列3种方法:
1、用结构体变量名作参数。一般较少用这种方法。
2、用指向结构体变量的指针作实参,将结构体变量的地址传给形参。
3、用结构体变量的引用变量作函数参数。
- 用结构体变量作函数参数。
#include <iostream>
#include <string>
using namespace std;
struct Student//声明结构体类型Student
{
int num;
char name[20];
float score[3];
};
int main( )
{
void print(Student); //函数声明,形参类型为结构体Student
Student stu; //定义结构体变量
stu.num=12345; //以下5行对结构体变量各成员赋值
stu.name="Li Fung";
stu.score[0]=67.5;
stu.score[1]=89;
stu.score[2]=78.5;
print(stu); //调用print函数,输出stu各成员的值
return 0;
}
void print(Student st)
{
cout<<st.num<<" "<<st.name<<" "<<st.score[0]
<<" " <<st.score[1]<<" "<<st.score[2]<<endl;
}
2)用指向结构体变量的指针作实参在上面程序的基础上稍作修改即可。
#include <iostream>
#include <string>
using namespace std;
struct Student
{
int num; string name; //用string类型定义字符串变量
float score[3];
}stu={12345,"Li Fung",67.5,89,78.5}; //定义结构体student变量stu并赋初值
int main( )
{
void print(Student *); //函数声明,形参为指向Student类型数据的指针变量
Student *pt=&stu; //定义基类型为Student的指针变量pt,并指向stu
print(pt); //实参为指向Student类数据的指针变量
return 0;
}
//定义函数,形参p是基类型为Student的指针变量
void print(Student *p)
{
cout<<p->num<<" "<<p->name<<" "<<p->score[0]<<" " <<
p->score[1]<<" "<<p->score[2]<<endl;
}
- 用结构体变量的引用作函数参数
#include <iostream>
#include <string>
using namespace std;
struct Student
{
int num;
string name;
float score[3];
}stu={12345,"Li Li",67.5,89,78.5};
int main( )
{
void print(Student &);
//函数声明,形参为Student类型变量的引用
print(stu);
//实参为结构体Student变量
return 0;
}
//函数定义,形参为结构体Student变量的引用
void print(Student &stud)
{
cout<<stud.num<<" "<<stud.name<<" "<<stud.score[0]
<<" " <<stud.score[1]<<" "<<stud.score[2]<<endl;
}
程序(1)用结构体变量作实参和形参,程序直观易懂,效率是不高的。
程序(2)采用指针变量作为实参和形参,空间和时间的开销都很小,效率较高。但程序(2)不如程序(1)那样直接。
程序(3)的实参是结构体Student类型变量,而形参用Student类型的引用,虚实结合时传递的是stu的地址,因而效率较高。它兼有(1)和(2)的优点。
4.5 应用:
(1)结构体中含字符的排序会用到strcmp而且最好是直接插入排序,边输入边比较(代码如下)
stu[0]=stu[i];
for(j=i-1;strcmp(stu[0].name,stu[j].name)<0;j--)
stu[j+1]=stu[j];
stu[j+1]=stu[0];//注意是j+1,因为j是比待插入数小,要放在j+1这个位置
详见p174例题4-13
(2)在含字符的结构体中查找,最好用strstr
习题:
4-6
输入一行字符,统计其中包含多少字符组(不含空格),字符组间用空格隔开。【原题是单词,但是给的参答其实是指字符组,对于单词处理还需要对a-z、A-Z进行判断,比较繁琐】
#include<iostream>
#include<stdio.h>
using namespace std;
int main(){
char str[200];
int i;
int space=0;//空格标志,0表示新空格,1表示连续空格
int num=0;
printf("请输入字符串:");
gets(str);
if(str[0]==' ')
space=1;
i=0;
while(str[i])
{
if(str[i]==' ')
{
if(space==0)
{
space=1;
num++;
}
}
else space=0;
i++;
}
if(space==0) //若单词不是以空格结尾,则+1,比如”ABC .“则“.”算一个字符组
num++;
printf("total %d",num);
return 0;
}
4-12
#include<iostream>
#include<stdio.h>
#include<cmath>
using namespace std;
int p[2000],k=0;
int fac(int n)
{
for(int i=2;i<=sqrt(n);i++)
{
if(n%i==0) return 0;
}
return 1;
}
void init()
{
for(int i=2;i<=2000;i++)
if(fac(i)) p[k++]=i;
}
int main(){
init();//初始化素数数组
int goal[2000],j=k-2;
for(int i=k-2;i>=0;i--)
goal[j--]=p[i+1]-p[i]; //构造目标数组
for(int i=0;i<=k-2;i++) //枚举出所有连续子数组组合
{
int sum=goal[i];
int j;
for(j=i+1;j<=k-2 && sum<1898;j++)
sum+=goal[j];
if(sum==1898){
printf("%d-%d ",p[i],p[j]);
printf("n");
}
}
return 0;
}
第五章 指针
5.1指针类型
c程序设计中使用指针:
(1)使程序简洁、紧凑、高效
(2)有效地表示复杂的数据结构
(3)动态分配内存,能直接处理内存地址
(4)得到多于一个函数返回值
5.2
5.2.1 指针的定义与存储
指针变量形式:[存储类型]数据类型 *指针名=初始地址值; 存储大小4B
变量是对程序中数据存储空间的抽象。
(1)指针 * p:指向某一类型的变量。
(2)指针变量p:专门存放地址的变量。
注:(1)存储类型区别!比如不可以用auto变量的地址去初始化static型指针。
重点(2)需要赋初地址值,改错题常考。
(3)数组名是指针常量不可以赋值运算。一个数组名实际上是一个指针常量。
(4)二维数组中,一维数组名就是行指针。
如下:
二维数组中的指针,
行指针:a+1;
列指针:&a[1][0],a[1],*(a+1),(int *)(a+1).
5.2.2
零指针与空类型指针:
零指针:指针变量值为零。
空类型指针:不指定p是指向哪一种类型数据的指针变量。使用时,要进行强制转化,void *p;
5.2.3运算
(1)对指针加1的含义:char类型,当前地址加1个字节;int类型,当前地址加2个字节;float类型,当前地址加4个字节。
重点(2)*p++:与++运算等级一样,运算符的结合顺序是从右往左,先p,再(p++);
比如v=*p++,则先v=*p,p++;
* q++= * p++,先 * q=*p,再p++、q++。
(3)指针的关系运算:
< 或 > 地址大小比较;
==或!= 是否指向统一数据/地址
(4)将一个类型的指针赋给另一个类型的指针需要进行指针间的强制转化。
5.2.4数组指针
指向一维数组的指针变量——地址变量:
定义形式: 数据类型(*指针名)[一维数组维数]
(1)int (* p)[4],叫数组指针,对于二维数组p就是指向列有四个元素的的行指针。
如,* (* p+j)->p[0][j];p[1]+2->*(p+1)+2;
(2)内存空间上,假如p只占2字节,而数组占2 * i * j字节。
(3)对于数组a,a={1,2,3,4}就是错的,p/*p={1,2,3}也是错的。
5.3字符指针
(1)指向字符串常量的指针,可以直接对地址变量赋值:char*p=“China”;但是地址常量char str[20],就不可以str=“China”。有printf(“%s”,p);等效于printf(“%s”,str);
(2)指向字符串常量的指针,又叫字符指针变量,可以反复赋值;char *p=“ASF”; p=“sdasff”;
5.4指针数组
定义:数组中的元素为指针变量。
5.4.1
形式:[存储类型] 数据类型数组名[数组长度];
比如:int * p[4];[]的运算优先级高于,先p[4],再数组与*结合。
int b[2][3],*p[2];
第一种赋值:p[0]=b[0];p[1]=b[1];
第二种赋值:int *p[]={b[0],b[1]};//这里的b[]就代表该行,如果是char就是字符串。
访问方式:
对第i+1个字符串:status[i];
对第i+1个字符串中的第j+1 *(status[i]+j);
5.4.2区别于二维数组:
(1)指针数组元素的作用相当于二维数组的行名
(2)指针数组中元素是指针变量
(3)二维数组的行名是地址常量
char *name[]={“foll”,“basic”,“great”,“for”,“com”};
printf("%s %p %dn",name[0],name[0],name[0]);由于指针数组相当于行名,%s会输出该行整个字符串;%p对应于当前name[]本身的首地址;%d对应于name[]指向的数组的行首地址。
5.4.3 应用
(1)指针数组与指针变量:char *str[];可以用来对字符串进行排序!并用strcmp(str[i],str[j])来比较,意思是比较第i个字符串和第j个字符串。
(2)字符串处理,第一种二维数组,第二种字符型指针数组
例题5-23 通过指针对一段内存进行访问,并输出其内存内容
#include<iostream>
#include<stdio.h>
#include<cstring>
using namespace std;
int main()
{
char *point;//char可以一个字节的对内存进行访问
long int b_add,e_add,i,j;
printf("enter the beginning and the end addrn");
scanf("%lx%lx",&b_add,&e_add);
for(i=b_add;i<e_add;i++)
{
printf("%05lx",i);//当前开始地址
point=(char*)i;
for(j=0;j<16;j++)
{
if(j==8) printf(" ");
printf("%02x",*point);
point++;
}
printf("n");
}
return 0;
}
5.5小结
int *p[3]:指针数组。内存空间2字节
int (p)[3]:数组指针。内存空间23字节
int *p(int):指针函数,返回指针型
int (*p)(int):函数指针,函数返回int型变量。
int (*p[3])(int):函数指针数组,函数返回int型变量。
赋值运算:
(1)把变量地址赋予指针变量 p=&a;
(2)同类型指针变量相互赋值 p=s;
(3)把数组,字符串的首地址赋予指针变量。 p=str;
(4)把函数入口地址赋予指针变量。
int func(int x); // 声明一个函数
int (*f) (int x); // 声明一个函数指针
f=func; // 将func函数的首地址赋给指针f
赋值时函数func不带括号,也不带参数,由于func代表函数的首地址,因此经过赋值以后,指针f就指向函数func(x)的代码的首地址。
习题:
5-3
变式:
int main(){
char *p[]={"one","two","three","four"};
while(*p[2]!='