A. 数组:同一类型的多个元素集合;在使用数组前必须先声明以开辟需要的内存空间
(1)为每个数据分配存储空间;
(2)能对每个数据进行读写和查找;
注:数组占据的内存空间是连续的
如:一个大小为N,类型为short的数组,其占据的内存大小为:N*sizeof(short)=N*2
如果第一个地址为p,那么第M(M<=N)在内存中的地址可表示为:p+(M-1)*2,这充分体现了数组的有序性。
例子:
#include<stdio.h>
#include<conio.h>
void main(void)
{
int score[6]; //声明一个int型数组,大小为6
int sum=0,i=0;
double averagescore=sum;//计算平均成绩
printf("请依次输入6名学生的成绩\n");
for(i=0;i<6;i++)
{
scanf("%d",&score[i]);//读取操作
sum+=score[i]; //总成绩
}
averagescore=sum/6.0;
printf("平均成绩:%.1f",averagescore);
getch();
}
其中,数组可以放6个元素,范围是0-5,“%d.1f”为了只输出一个小数。
数组的初始化:没有初始化的数组,存在安全隐患,不好查错。可以只初始化其中一部分,如果都初始化为零,可以直接等于零、
二维数组: int num[2][3]={{1,0,0},{2,0,0}};
B. 指针
内存是由按顺序编号的一系列存储单元组成的,在内存中,每个存储单元都有唯一的地址,通过地址可以方便的在内存单元中存取信息。内存的数据要靠供电来维持。
内存地址是连续的,相邻内存单元间的地址相差1,相当于一个平坦连续的一维空间。
计算机中,信息以二进制数据存放,每个内存单元容量是1B=8bit(8个0、1的二进制位)
中央处理器(CPU)执行程序时,即是对复杂的内存载入的过程:
(1)程序所要进行操作的对应代码被装载到代码区;
(2)全局和静态数据等装载到数据区;
(3)开辟堆栈,供临时变量等使用。
内存中的数据:可以是数据、程序。
指针变量:该变量的值是一个地址(无符号的整形数)
如:int a=10,x,*p=&a;称指针变量p指向整形变量a
注:*为指针标识符
地址就是指针
指针:存储的内容是地址的量。(指针变量占据一定的内存空间)注:指针是个量,对应着一块内存区域;指针存储的信息是某个内存单元的地址。
声明一个指针变量(开辟内存空间):(1)指针的类型;(2)指针变量名;
形式:类型* 指针变量名;
指针的值不确定,即取决于指针所占内存区域的值,是随机的,故指针变量需要初始化,如果不知道指向何处,需置“”,关键字NULL;如:int* a=NULL;
指针变量的值:地址,不是一般的数值;如:“一个指针的值是A”指该指针指向了以A为首地址的一片内存区域;
取地址操作符&:返回实体的地址信息;如:
int num=0;
int* p=#
&num返回的是变量num在内存中的地址信息,可以直接将此地址赋值给同类型的指针p。
指针变量占据一定的内存空间(32位系统都是占据4字节内存空间)
例子:
#include<stdio.h>
#include<conio.h>
void main()
{
int a=0;
short b=0;
double c=0;
int* d=&a;
short* e=&b;
double* f=&c;
printf("int型占据的内存字节数:%d\n",sizeof(a)); //4
printf("指针int型占据的内存字节数:%d\n",sizeof(d)); //4
printf("short型占据的内存字节数:%d\n",sizeof(b)); //2
printf("指针short型占据的内存字节数:%d\n",sizeof(e)); //4
printf("double型占据的内存字节数:%d\n",sizeof(c)); //8
printf("指针double型占据的内存字节数:%d\n",sizeof(f));//4
getch();
}
指针变量所占内存不变,指向的变量所占据内存与其无关。
指向指针的指针:指针变量也是变量,占据内存空间,有地址,因此也可以用一个指针指向它,即二级指针,用“**”声明,如:
double num;
double*a=#
double**b=&a;
“*”的作用:“类型*”是一种复合类型,用来声明某种类型的变量,另一作用:间接引用,即通过指针访问其指向的内存区域。
形式:
double num=9;
double* a;
a=#
注:
(1)num:double类型的变量;
(2)a:指向num的指针变量,其值是num的地址;
(3)&num:返回变量num的地址,与a等价;
(4)*a:a所指的变量,间接访问方式,与num等价;
(5)&(*a):与&num(即a)等价,即num的地址;
(6)*(&num):与*a(即num)等价,变量num。
例子:
#include<stdio.h>
#include<conio.h>
void main(void)
{
int num=9;
int* a=#
printf("指针变量a的值:%x\n",a);//18ff44
printf("指针变量a指向的内存区域为:%d\n",*a);//9
*a=10;
printf("num变量的值为:%d\n",num); //10
}
18ff44是int型变量num的地址
注:指针类型与指针所指向的类型可以使相同的,也可以是不同的。
(1)同类型指针的赋值
例子:
#include<stdio.h>
#include<conio.h>
void main(void)
{
int num=9;
int* a=#
int* b=a;
*a=10; //通过指针改写其指向的内存区域
printf("num变量的值为:%d\n",num); //10
*b=11; //等价于*a=11
printf("num变量的值为:%d\n",num); //11
getch();
}
*a与*b与num是等价的。
(2)指针的类型和指针所指向的类型不同
A.指向内存的字节数大于指针类型占据的字节数
doublenum;
int* a=# //*a为int型,而num是double型
或
double num;
double*a=#
int* b=a;
B.指向内存的字节数小于指针类型占据的字节数
short num;
double* a=#
或
short num;
short* a=#
double* b=a;
注:指针的类型决定可通过指针间接访问的区域;对于A中,尽管double型变量占据8个字节,但指针是int型,故只能管理前4个字节;对于B中,short型数据占2个字节,儿double型指针管理的是8个字节,这时,short型数据后边的6个字节会被指针改写,这种“越权”会给程序带来致命的错误。
(3)不同类型但占据相同字节
会出现奇怪的结果。
指针的运算:
主要是加减算术运算和特殊运算;
两个有关的运算符:(&与*)
&:任意变量 //取地址运算符
*:指针变量 //指针运算符
&a 表示变量a所占据的内存空间的首地址
*p 表示指针变量p所指向的内存中的数据
x=*p;取内容,相当于x=a
*p=100;存内容
例子:用指针交换两个单元中的内容
#include<stdio.h>
void main(void)
{
inta,b,c,*p1=&a,*p2=&b;
scanf("%d%d",p1,p2);
c=*p1;
*p1=*p2;
*p2=c;
printf("a=%d,b=%d\n",*p1,*p2);
}
输出交换以后的a和b
一、指向简单变量的指针
int a,b,c,*p1=&a,*p2=&b;
注意区分:
(1)p1=p2与*p1=*p2;
(2)(*p1)++与*p1++;
(3)&*p等价于p;*&a等价于a
解释如下:
(1)P1=P2 :把P2的地址赋给P1,此后P1与P2都指向b;(原来是P1指向a);
*P1=*P2:把P2所指向的内容放到P1所指向的单元,此后a和b都等于b的值;
(2)(*P1)++ : *P1=a,故(*P1)++相当于a++;
*P1++ :如果a的地址为1000,则*P1++指向1001,即指向a后边的变量(a后边的变量不知道是什么,故*P1++没有意义);
(3)&*P=P *&a=a :其中*P=a &a=P;注意:*后边一定是指针变量(因为指针变量才能进行指针运算);&后边一定是普通的变量.
二、指向数组的指针
(1) int a[10], *p=a; &&p指向a数组的首地址 等价于: p=&a[0]
数组名a就表示数组的首地址,故不需要&
(2)数组元素的地址: &a[i] a+i p+i
解释如下:a表示数组首地址;故其他的可以直接用a+i
(3)数组元素的内容:a[i] *(a+i) *(p+i)
(4)指向数组的指针p的运算:p+/-I p-qp++p—p>q
注:p+/-i表示p是指向数组,数组是一个空间,p可以在数组中移动i;
p-q表示p和q都是指向同一个数组,则p-q表示他们之间相差的个数;
p++指向p后边一个元素,p—往前移;
p>q p和q指向同一个数组,p指向的元素在q后边。
a+i ;p-a;但 a++和a—是错的,因为a不是一个变量。
(5)指针与函数参数:
形参是指针变量时,实参可以是数组名、指针变量
三、字符串指针变量
char *sp=“abcdef”,str[10], *PP=str;
char表示指针变量是指向字符型数据
即sp指向字符串的首地址
注:字符串指针变量和字符数组的区别(谭浩强P238页)
例子:将字符串倒序存放’
方法一:
#include<stdio.h>
#include<string.h>
void main()
{
char c,str[100];
int i=0,j;
gets(str); //输入字符串
j=strlen(str); //计算数组长度
j--;
while(i<j)
{
c=str[i];
str[i]=str[j];
str[j]=c;
i++;
j--;
}
printf("string=%s\n",str); //%s字符串输出
}
方法二:(用指针变量做)
#include<stdio.h>
#include<string.h>
void main()
{
charc,str[100],*p=str,*q;
gets(p);
q=p+strlen(p)-1;
while(p<q)
{
c=*p;
*p=*q;
*q=c;
p++;
q--;
}
printf("string=%s\n",str);
}
注释:
q=p+strlen(p)-1;
Strlen(p)取长度,(如果输入“China”,则长度为5),因为下标从零开始,故需要减1;
故有:p指向首地址,q指向最后一个元素;如下图:
while(p<q)
{
c=*p;
*p=*q;
*q=c;
p++;
q--;
}
当p<q时,不需要交换了
printf("string=%s\n",str);此时不能输出 p,因为p已经移动而变化了。
例子:编写一个函数:将字符串src中的字符复制到dest中。
函数说明:int a(char *dest,char *src)或int a(char dest[],char src[])
方法一:
Voida(char dest[],char src[])
{
int j =0;
while((dest[j++] = src[j]!=’\0’));
}
判断是否结束标记“\0”,最后结束标记也已过去了
方法二:
void a(char dest[],char src[])
{
char*p=src;
char*q=dest;
while(*q++=*p++);
}
方法三:这是错的(因为数组名dest不是变量,故不能自加运算)
void a(char dest[],char src[])
{
while(*dest++=*src++);
}
方法四:
void a(char *dest,char *src)
{
intj=0;
while((dest[j++]=src[j])!=’\0’);
}
指针变量也可以用下标
方法五:
void a(char *dest,char *src)
{
char*p=src;
char *q=dest;
while(*q++=*p++);
}
方法六:
void a(char *dest,char *src)
{
while(*dest++=*src++);
}
数字名不能自加,这里是指针变量,是变量,指向后面的元素,故可以
例子:求字符C在字符串中首次出现的位置(用指针、函数方法做)
//用指针做
#include<stdio.h>
#include<string.h>
int a(char *p,char x)
{
inti=0,k=-1;
for(;*p!='\0';p++,i++)
if(*p==x)
{
k=i;
returnk;
}
returnk;
}
void main()
{
chars[100],y;
intb;
gets(s);
//y=getchar(); //输入要判断的字符
y='c'; //判断c首次出现的位置
b=a(s,y);
if(b>=0)
printf("position=%d\n",b);
else
printf("notexit\n");
}
注:
输入一个字符串用gets(s); 输入一个字符用y=getchar();s为是第一个出现的地址。