1指针相关概念
1.1地址与指针
地址是变量在内存中的位置,指针就是地址。
在程序中定义一个变量,编译时程序会给变量在内存中分配一个地址,通过访问这个地址,就可以找到该变量,这个地址就成为该变量的指针。
1.2变量与指针
变量的地址是变量与指针的纽带。所谓“指向”就是把一个变量的地址赋给了一个指针。当把变量x的地址存到指针p中时,就说指针p指向变量x
1.3指针变量
指针变量:这个变量专门存放别的变量的地址
1.指针变量的一般形式: 类型名 * 变量名 如 int *p或 int* p 两种形式一样
2.指针变量的赋值
(1)
int a;
int *p = &a;
“&”是取地址符号,&a表示a的地址
(2)
int a;
int *p;
p=&a;
注意:不能直接把数据赋给指针
3.指针变量的引用
比如
int a=5;
int *p=&a;
printf("%d",*p); //引用指针变量
用的时候就直接 “ *指针变量 ”
4.& 与 *
&叫取地址运算符,是取地址符号,返回变量的地址。
*叫指针运算符,是取数据的符号,返回变量的值。
它俩的运算符优先级别相同,且都是从右到左。
所以 &*p是先算*,*p是a;再算&,取*p(也就是a)的地址,表示取a的地址
*&a 先算&,取a的地址,再算*,取a所在地址上的数据。
1.4指针的运算
指针是地址,所以指针的自加自减不是简单的在数值上+1或-1,而是+1个地址或-1个地址
比如
int *p;
p++;
p--;
p是int 类型的,int内存大小是4字节,所以p++是p的值增加了1*4=4;p--是p的值减了1*4=4.
当两个指针p1,p2都指向同一数组中的元素,p2-p1=(p2地址-p1地址)/数组长度;如p2指向a[5]地址为2020,p1指向a[3]地址为2012,两指针都是int 类型,p2-p1=(2020-2012)/4=2.
其意义是p2所指元素与p1所指元素之间相差2个元素。
这样可以直接算出两元素的相对位置。
2.数组与指针
2.1 一维数组与指针
(1).
int *p,a[10];
p=a;
这样指针p就指向了数组a,因为在数组中,数组名称a就是数组在内存中的首地址,所以p=a中不需要“&”。
int *p;a[10];
p=&a[0]
将数组a的首个元素的地址赋给p,这与前面的等价,p=a,p指向的也是数组a的首个元素
对于数组中其他元素
*(p+i)等价于*(a+i)等价于a[i]
p+i与a+i都可以表示数组中各元素的地址
(2).
可以先让指针p指向数组中的一个元素,之后想让p指向下一个元素时,直接用“p++”(同理,指向上一个元素可以用“p--")
如:
#include<stdio.h>
int main ()
{
int a[5]={1,2,3,4,5};
int *p;
for(p = a;p < (a+5);p ++)
printf("%d ",*p);
return 0;
}
但是,如果不用p而用数组名a的变化去指向不同元素是不行的
for(p = a;p < (a+5);a ++)
printf("%d ",*a);
因为数组名a代表数组首个元素的地址,它是个指针型常量,在程序运行的过程中是固定不变的,因此a++无法实现。
注意,在使用这种方法时要注意指针的当前位置。
错误案例:
#include<stdio.h>
int main ()
{
int a[5],i,*p;
p=a; //p指向a[0]
for(i = 0;i < 5;i ++)
scanf("%d",p++); //输入整数给a[0]~a[4]
for(i = 0;i < 5;i++,p++)
printf("%d\n",*p); //想输出a[0]~a[4]
return 0;
}
这是因为在第一个for循环结束时,p指向的是数组中的最后一个元素a[4],当第二个for循环时,p的起始值不是&a[0]了,所以执行p++时,p指向的是数组a后面的存储单元,但这些存储单元的值是不知道的。
解决办法是在两个for循环中间加一句 p=a 使p重新指向&a[0]
正确案例:
#include<stdio.h>
int main ()
{
int a[5],i,*p;
p=a; //p指向a[0]
for(i = 0;i < 5;i ++)
scanf("%d",p++); //输入整数给a[0]~a[4]
p=a; //p重新指向a[0]
for(i = 0;i < 5;i++,p++)
printf("%d\n",*p); //输出a[0]~a[4]
return 0;
}
2.2二维数组和指针
表示二维数组地址的方法:
1). &a[0][0]可以看作数组第0行第0列的首地址,也可以看做二维数组的首地址。&a[m][n]是第m行第n列的元素的地址。
2). a[0]+n表示第0行第n个元素的地址
3). &a[0]是第0行的首地址,同理,&a[m]是第m行的首地址。
4).a+n表示第n行的首地址
用指针引用二维数组中的元素:
1). *(*(a+n)+m)第n行第m列的元素
2). *(*a[n]+m)第n行第m列元素
2.3字符串与指针
访问字符串的方式有两种,一种是用数组存放字符串;另一种就是用指针。
#include<stdio.h>
int main ()
{
char *str="hello";
printf("%s",str); //输出字符串
}
这里定义了一个字符型指针变量 str,用字符串常量 hello 赋值给它。不过这里不是把 hello 都存到了str里,只是把 hello 的第一个字符的地址赋给str。
char *str="hello";
等价于
char *str;
str="hello";
2.4字符串数组
对于字符串数组可以用二维数组,也可以用一维指针数组。
如:二维数组
char country[5][20]=
{
"China",
"Japan",
"Russia",
"Germany",
"Switzeriand"
}
这个数组5行20列,5个字符串,给每个字符串分配了20个字节空间,但每个字符串长度都小于20个字节,会造成空间上的浪费。
指针数组:一个数组,其元素都相当于指针变量。
定义形式: 类型名 数组名[数组长度] (如果在定义数组时直接进行初始化,将数据存到了数组里,数组长度可以像下面例子那样不写)
#include<stdio.h>
int main ()
{
int i;
char *country[]=
{
"China",
"Japan",
"Russia",
"Germany",
"Switzeriand"
};
for(i = 0;i < 5;i ++)
printf("%s\n",country[i]);
return 0;
}
3.指针变量作为函数参数
在C编译中,函数中的形参数组名是被当作指针变量来处理的
int fun(int arr[ ], int n)
等价于
int fun(int *arr, int n)
另外,C语言中实参和形参的数据传递是单向的“值传递”,只能将数据从实参传到形参。
指针变量传递的是实参数组首元素的地址;可以通过改变实参指针变量所指变量的值来改变实参数组的值
比如:
错误案例:交换x,y的值
#include<stdio.h>
void swap(int a,int b)
{
int tmp;
tmp=a;
a=b;
b=tmp;
}
int main()
{
int x=1,y=2;
swap(x,y);
printf("%d %d",x,y);
}
因为单向传递,所以 没能成功
正解:
#include<stdio.h>
void swap(int *a,int * b)//&x对应*a &y对应*b
{
int tmp; //改变实参指针变量所指的变量的值
tmp=*a; // 交换*a,*b
*a = *b;
*b=tmp;
}
int main()
{
int x=1,y=2;
swap(&x,&y);
printf("%d %d",x,y);
}
地址没变,变得是地址上的值。
4指针函数
返回值类型为指针类型的函数
定义形式: 类型名 *函数名(参数表列)
如:int *per(int a,int b);
个人感觉没什么讲的,直接上例子:输入长方形的长和宽,求周长
#include<stdio.h>
int *per(int a,int b); ——定义了一个返回指针值的函数
int Perimeter;
void main ()
{
int iwidth,ilength,*iresult;
printf("please input long:\n");
scanf("%d",&ilength);
printf("please input wide:\n");
scanf("%d",&iwidth);
iresult=per(iwidth,ilength);
printf("长方形的周长是:");
printf("%d\n", * iresult);
}
int *per(int a,int b){
int *p;
p=&Perimeter;
Perimeter=(a+b) *2;
return p; ——将存放周长的指针变量返回
}