C Language Day07
- 地址和内存
- 指针与指针变量
- 定义指针变量
- 数组与指针
- 多维数组与指针
- 内存的分区
- 字符指针变量和字符数组
- 指向指针的函数
- 指针小结
1.地址和指针的概念
内存区的每一个字节有一个编号,这就是"地址"。如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。
对变量的访问有两种方式,直接访问和间接访问。
直接访问如:a=5 系统编译时,已经对变量分配了地址,例如,若变量a分配的地址是2000,则该语句的作用就是把常数5保存到地址为2000的单元。
间接访问:scanf("%d",&a);调用函数时,把变量a的地址传递给函数scanf函数首先把该地址保存到一个单元中,然后把从键盘接收的数据通过所存储的地址保存到a变量中。
指针是一种特殊变量,存放地址的。定义指针变量int *i_pointer用来存放整形变量i的地址。可以通过语句i_pointer=&i;
*取值操作符 &取址操作符
int i=2000;
int *pointer;
pointer=&i;
printf("%d",*pointer);
2.指针与指针变量
知道一个变量的地址,就可以通过这个地址来访问这个变量,因此,又把变量的地址称为该变量的"指针"。
C语言中可以定义一类特殊的变量,这些专门用来存放变量的地址,称为指针变量。
指针变量的值(即指针变量中存放的值)是地址(即指针)。
float *pointer_3 pointer_3是指向float型变量的指针变量;
char *pointer_4 pointer_4是指向字符型变量的指针变量;
可以用赋值语句使一个指针变量得到另一个指针变量的地址,从而使它指向一个该变量;
3.定义指针变量
指针变量前面的*,表示该变量的类型为指针型变量。其一般形式为:类型说明符 *变量名
*表示这是一个指针变量,变量名即为定义的指针变量名,类型说明符表示本指针变量所指向的变量的数据类型。如float *pointer_1,指针变量名是pointer_1,而不是*pointer_1;
在定义指针变量时必须指定基类型。只有整型变量的地址才能放到指向整型变量的指针变量中。
指针变量中只能存放地址,不要将一个整数赋给一个指针变量,否则编译器也会把该值当成一个地址来处理。
地址运算符&来表示变量的地址
其一般形式:&变量名; 如&a表示变量a的地址,&b表示变量b的地址。
例题:通过指针变量访问整型变量
#include <stdio.h>
void main()
{
int a,b;
int *p_1,*p_2;
a=100;b=10;
p_1=&a;
p_2=&b;
printf("%d %d\n",a,b);
printf("%d %d\n",*p_1,*p_2);
}
&和*运算符的说明
如果已执行语句 pointer_1=&a;
(1)&*pointer_1的含义
(2)&和*两个运算符优先级相同,但按照自右向左结合;
&*pointer_1与&a相同,即变量a的地址。
如果pointer_2=&*pointer_1;
它的作用是将&a赋给pointer_2,如果pointer_2原来指向b,经过重新赋值后它已不再指向b了,而指向a。
*&a和*pointer_1的作用一样的,它们都等价于变量a。即*&a与a等价。
(3)(*pointer_1)++相当于a++
例题:输入a,b,通过指针输出最大和最小。
#include <stdio.h>
void main()
{
int *p1,*p2,*p,a,b;
scanf("%d %d",&a,&b);
p1=&a;
p2=&b;
if(a<b)
{
p=p1;
p1=p2;
p2=p;
}
printf("a=%d,b=%d\n",a,b);
printf("max=%d,min=%d\n",*p1,*p2);
}
#include <stdio.h>
void swap(int *p1,int *p2);
void main()
{
int a,b;
int *po_1,*po_2;
scanf("%d %d",&a,&b);
po_1=&a;
po_2=&b;
if(a<b)
{
swap(po_1,po_2);
}
printf("%d>%d\n",a,b);
}
void swap(int *p1,int *p2)
{
int temp;
printf("swapping...\n");
temp=*p1;
*p1=*p2;
*p2=temp;
}
例题:输入a,b,c 3个整数,按大小顺序输出。
#include <stdio.h>
void main()
{
void exchange(int *p1,int *p2,int *p3); //使得a>b>c
int a,b,c,*p1,*p2,*p3;
scanf("%d %d %d",&a,&b,&c);
p1=&a;
p2=&b;
p3=&c;
exchange(p1,p2,p3);
printf("%d %d %d\n",a,b,c);
}
void exchange(int *p1,int *p2,int *p3)
{
void swap(int *p1,int *p2);
if(*p1<*p2)
{
swap(p1,p2);
}
if(*p1<*p3)
{
swap(p1,p3);
}
if(*p2<*p3)
{
swap(p2,p3);
}
}
void swap(int *p1,int *p2)
{
int temp;
temp=*p1;
*p1=*p2;
*p2=temp;
}
4.数组与指针
一个变量有地址,一个数组包含若干个元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。
指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中)
所谓数组元素的指针就是数组元素的地址。
int a[10];
int *p;
p=&a[3];
引用一个数组元素,可以用
(1)下标法 a[i]形式
(2)指针法 *(a+i)或*(p+i)
其中的a是数组名,p是指向数组元素的指针变量,其初值p=a等价于p=&a[0];
数组名即翻译成数组的第一个元素的地址。
例题:输出数组中的全部元素;
(1)下标法
(2)通过数组名计算数组元素地址,找出元素的值。
(3)用指针变量指向数组元素。
//下标法
#include <stdio.h>
void main()
{
int a[10]={10,9,8,7,6,5,4,3,2,1};
int i;
for(i=0;i<10;i++)
{
printf("%d\n",a[i]);
}
}
#include <stdio.h>
void main()
{
int a[10];
int i;
for(i=0;i<10;i++)
{
scanf("%d",&a[i]);
}
printf("\n");
for(i=0;i<10;i++)
{
printf("%d\n",*(a+i));
}
}
#include <stdio.h>
void main()
{
int a[10];
int i;
int *p;
for(i=0;i<10;i++)
{
scanf("%d",&a[i]);
}
printf("\n");
for(p=a;p<(a+10);p++)
{
printf("%d",*p);
}
}
数组名作函数参数
当用变量作为函数参数时传递的是变量的值,当数组名作为函数参数时,由于数组名代表的是数组首元素的地址,因此传递的值是地址,所以要求形参为指针变量。
f(int arr[ ],int n) 可以写成 f(int *arr,int n)
例题:将数组a中n个整数按相反顺序存放。
#include <stdio.h> //数组名作参数
void reverse(int x[],int n);
void main()
{
int i,a[]={3,7,9,11,0,6,7,5,4,2};
printf("the original array:\n");
for(i=0;i<10;i++)
{
printf("%d ",a[i]);
}
printf("\n");
reverse(a,10);
printf("the array has been inverted:\n");
for(i=0;i<10;i++)
{
printf("%d ",a[i]);
}
printf("\n");
}
void reverse(int x[],int n)
{
int temp,i,j,m;
m=(n-1)/2;
for(i=0;i<=m;i++)
{
j=n-1-i;
temp=x[i];
x[i]=x[j];
x[j]=temp;
}
}
如果有一个实参数组,想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况:
(1)形参和实参都用数组名;
void main()
{
int a[10];
f(a,10);
}
void f(int x[],int n)
{
.......
}
(2)实参用数组名,形参用指针变量;
void main()
{
int a[10];
f(a,10);
}
f(int *a,int n)
{
......
}
(3)实参形参都用指针变量;
void main()
{
int a[10],int *p=a;
f(p,10);
}
void f(int *x,int n)
{
.......
}
(4)实参为指针变量,形参为数组名;
void main()
{
int a[10],*p=a;
f(p,10);
}
f(int x[],int n)
{
.....
}
例题:对数组中10个数从大到小排序;
#include <stdio.h>
void sort(int x[],int n);
void main()
{
int *p,i,a[]={3,7,9,11,0,6,7,5,4,2};
printf("the original array:\n");
for(i=0;i<10;i++)
{
printf("%d ",a[i]);
}
printf("\n");
p=a;
sort(p,10);
printf("the sort result:\n");
for(i=0;i<10;i++)
{
printf("%d ",a[i]);
}
}
void sort(int x[],int n)
{
int i,j,k,t;
for(i=0;i<n-1;i++)
{
k=i;
for(j=i+1;j<n;j++)
{
if(x[j]>x[k])
{
t=x[j];
x[j]=x[k];
x[k]=t;
}
}
}
}
5.多维数组与指针
例题:通过输入指定行数和列数打印出二维数组对应任一行任一列元素的值。
#include <stdio.h>
void main()
{
int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int (*p)[4],i,j;
p=a;
printf("i= ");
scanf("%d",&i);
while(i>2||i<0)
{
printf("i= ");
scanf("%d",&i);
}
printf("j= ");
scanf("%d",&j);
while(j>3||i<0)
{
printf("j= ");
scanf("%d",&j);
}
printf("a[%d,%d]=%d\n",i,j,*(*(p+i)+j));
}
例题:用字符数组存放一个字符串,然后输出该字符串。
#include <stdio.h>
void main()
{
char string[]="i love the world";
printf("%s\n",string);
}
对字符串中字符的存取,可以用下标法,也可以用指针法。
下标法:将字符串a复制为字符串b
#include <stdio.h>
void main()
{
char a[]="i love the world",b[40];
int i;
for(i=0;*(a+i)!='\0';i++)
{
*(b+i)=*(a+i);
}
*(b+i)='\0';
printf("string a is:%s\n",a);
printf("string b is:");
for(i=0;b[i]!='\0';i++)
{
printf("%c",b[i]);
}
printf("\n");
}
指针法:将字符串a复制为字符串b
#include <stdio.h>
void main()
{
char a[]="i love the world",b[40],*p1,*p2;
int i;
p1=a;
p2=b;
for(;*p1!='\0';p1++,p2++)
{
*p2=*p1;
}
*p2='\0';
printf("string a is:%s\n",a);
printf("string b is:");
for(i=0;b[i]!='\0';i++)
{
printf("%c",b[i]);
}
printf("\n");
}
用函数调用实现字符串的复制
(1)用字符数组作参数
#include <stdio.h>
void main()
{
void copy_string(char from[],char to[]);
char a[]="i love the world";
char b[]="the world is best";
printf("string a=%s\n string b=%s\n",a,b);
printf("copy string a to string b:\n");
copy_string(a,b);
printf("\nstring a=%s\n string b=%s\n",a,b);
}
void copy_string(char from[],char to[])
{
int i=0;
while(from[i]!='\0')
{
to[i]=from[i];
i++;
}
to[i]='\0';
}
(2)形参用字符指针变量
6.内存分区
在C\C++中,内存分成5个区,分别是堆,栈,自由存储区,全局/静态存储区和常量存储区;
栈:是那些由编译器在需要的时候分配,在不需要的时候自动清除的变量存储区。里面的变量通常是局部变量、函数参数等。
堆:那些由new分配的内存块,他们的释放编译器不去管,由应用程序去控制,一般一个new就要对应一个delete。如果程序员没有释放资源,那么在程序结束后,操作系统会自动回收。
自由存储区:那些由malloc等分配的内存块,和堆十分相似,不过它是用free来结束自己的生命。
全局\静态存储区:全局变量和静态变量被分配到同一块内存中,在以前C语言中,全局变量又分为初始化和未初始化,在C++里面没有这个区别了,他们共同占用同一块内存区。
常量存储区:这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改(当然,你要通过非正当手段也可以修改,而且方法很多)。
7.字符指针变量和字符数组
(1)字符数组由若干个元素组成,每个元素中存放一个字符,而字符指针变量中存放的是地址,绝不是将字符串放到字符指针变量中。
(2)赋值方式,对字符数组只能对各个元素赋值,不能用以下办法对字符数组赋值;
char str[20];str="i love the world";
对字符指针变量,可以采用下面方法赋值:
char *a;a="i love the world";
但注意赋给a的不是字符,而是字符串第一个元素的地址。
(3)对字符指针变量赋初值:
char *a="i love the world";等于 char *a;a="i love the world";
对数组的初始化:
char str[20]={"i love the world"};不能等价于 char str[20];str[]="i love the world";
(4)如果定义了一个字符数组,在编译时为它分配内存单元,它有确定的地址。而定义一个字符指针变量时,给指针变量分配内存单元,在其中可以放一个字符变量的地址也就是说,该指针变量可以指向一个字符型数据,但如果未对它赋予一个地址值,则它并未具体指向一个确定的字符数据。
如:char str[10];scanf("%s",str);
(5)指针变量的值是可以改变的;另外,若定义了一个指针变量,并使它指向一个字符,就可以用下标形式引用指针变量所指向的字符串中的字符;
8.指向函数的指针
用函数指针变量调用函数
可以用指针变量指向整型变量、字符串、数组,也可以指向一个函数。一个函数在编译时被分配给一个入口地址。这个函数的入口地址就称为函数的指针。
指向函数的指针作为函数参数
指向函数的指针也可以作为参数,以实现函数地址的传递,这样就能够在被调用的函数中使用实参函数。
实参函数名 f1 f2
void sub (int (*x1)(int),int(*x2)(int,int))
{
int a,b,i,j;
a=(*x1)(i); //调用f1函数
b=(*x2)(i,j); //调用f2函数
.....
}
返回指针值的函数
这种带回指针值的函数,一般形式为
类型名 *函数名(参数列表);
如:int *a(int x,int y);
指针函数和函数指针
(1)指针函数是指带指针的函数,即本质是一个函数;
(2)函数指针是指向函数的指针变量,因而函数指针本身首先应是指针变量,只不过该指针变量指向函数。
指针数组和指向指针的指针
指针数组:一个数组,若其元素均为指针类型数据,称为指针数组,也就是说,指针数组中的每一个元素都相当于一个指针变量。一维指针数组的定义形式
类型名 数组名[数组长度];
如:int *name[4];
#include <stdio.h>
void main()
{
int a[5]={1,3,5,7,9};
int *name[5]={&a[0],&a[1],&a[2],&a[3],&a[4]};
int i;
for(i=0;i<5;i++)
{
printf("%d ",*name[i]);
}
printf("\n");
}
指向指针的指针
char **p; p前面有两个*号,*运算符的结合是从右到左,因此**p相当于*(*p),显然*p是指针变量的定义形式。如果没有最前面的*,那就是定义了一个指向字符数据的指针变量。
#include <stdio.h>
void main()
{
char *name[]={"hello","world","whoami"};
char **p;
int i;
for(i=0;i<3;i++)
{
p=name+i;
printf("%s\n",*p);
}
}
指针数组作main函数的形参
实际上main函数可以有参数
void main(int argc,char *argv[ ])
argc和argv就是main函数的形参
#include <stdio.h>
void main(int argc,int *argv[])
{
while(argc>1)
{
++argv;
printf("%s\n",argv);
--argc;
}
}
#include <stdio.h>
#include <stdlib.h>
void main(int argc,char *argv[])
{
int i;
printf("the number of string is:%d\n",argc-1);
for(i=1;i<argc;i++)
{
printf("the string %d is:%s\n",i,argv[i]);
}
}
9.指针小结
指针变量赋值
将一个变量地址赋给一个指针变量。如:
p=&a;将变量a的地址赋给p
p=array;将数组array首元素地址赋给p
p=&array[i];将数组array第i个元素的地址赋给p
两个指针变量可以相减
如果两个指针变量都指向同一个数组中的元素,则两个指针变量值之差是两个指针之间的元素个数。
两个指针变量比较
若两个指针指向同一个数组的元素,则可以进行比较。指向前面的元素的指针变量“小于”指向后面元素的指针变量。
关于void类型
作用:(1)对函数返回的限定;(2)对函数参数的限定;
如:void abc(void);
void指针类型:即不指定它是指向哪一种类型数据的指针变量;
void *p;表示指针变量p不指向一个确定的类型数据,它的作用仅仅是用来存放一个地址;