1.地址与指针
- 变量的地址称为指针(指针是用来存放一个变量在内存的地址)
- 用来存放一个变量地址的变量叫指针变量
2.指针定义
type * identifer =inital_value
指针变量同普通变量一样先定义后使用
类型说明符 *变量名
注意:
此处的 * 不是间接访问运算符,它仅仅是一个标志,他表示定义的是指针变量
- 举例
char *p1;//p1指向char数据类型
int *p2 //同理
float *p3
无论定义哪一种数据类型的变量,他们的地址都是相应的内存单元编号,而编号本身并没有类型,因此上述char *p1中char 并非是指针变量p1的类型,而是p1所指向的对象的数据类型,即p1指向类似于char的数据类型
注意
指针变量数据类型一旦确定,只能指向同类型的数据对象
3.指针变量初始化
- L指针变量存放变量地址,因将变量地址赋给指针变量。
- &取地址运算符。取出变量地址
一.给指针变量赋地址值
int i=3;
int *pi; //定义指针变量pi
pi=&i; //把变量i的地址存入指针变量pi,也称作pi指向变量i
要注意的是
- pi=&i+1或pi=&(i+1)这是错误的,指针p就是用来存放地址的,这两个例子不是
- &必须放在运算对象的左边,而且运算对象的类型必须与指针变量的基类型相同
二.通过指针变量获得地址值
int k,*p,*q;
p=&k; p=q;
也就是说指针变量p和q都指向了变量k
三.给指针变量赋“空”值
定义指针变量时,使用空指针NULL初始化可以避免指针变量指向一个随机的内存位置,它表示指针不指向任何对象
char *pc=NULL;
NULL是取值为0的特殊指针,从技术来看是使pc指向地址为0的存储单元,地址为0的内存单元是系统使用的单元,不允许向该单元赋值。
4.指针变量的引用
int i=10,j,*p=&i;
得出结论p=(&i)=i=10;
如此*p可以出现i能够的任何位置
j=*p; //等价于j=i
(*p)++; //等价于i++
*p=j; //等价于i=j
printf("%d",*p); //等价于 prinf("%d",i)
scanf("%d",p) //等价于scanf("%d",&i)
5.&取地址运算符,*间接访问运算符(有时候直接点解引用运算符,直接去取该地址的值)
int a=3,b;
int *p;//定义指针变量
p=&a;//p指向a,将a地址放入p中
*p=20;//将指向储存单元赋值20,将a赋值为20
b=*p;//把a值给b,即b=a
6.指针变量的使用
一.同上例中5的拓展
int *p,i=10,j;
p=&i;
可以得到 j=*p
我们可以这样理解,j变量的值是指针指向空间的值赋予的。
这里的* p代表取指针p中存放的变量i的地址中的值(所以叫间接访问运算符)这里的*不是乘而是说明指针的说明符。
j=i~~~~~~~j=*(&i)
特别注意
j =*i; 说明j是普通变量,i是指针变量,意思是把i指向的变量中的值赋值给j
j=&i 说明j是指针变量,i是普通变量,取i的地址然后赋值给j,这样j就指向i了
j=*& 你得先确定*和&的优先级和结合性,*&i等价于*(&i),也就是先取i的地址,然后求这个地址所指向的内容,其实就是i,所以这里,j和i都是普通变量,此等式的含义就是把i的值赋值给j
二.举例
#include<iostream>
using namespace std;
int main(void)
{
int i=5,j=10;//定义整形变量i,j
int *pi,*pj;//定义指针变量pi,pj
pi=&i;pj=&j;//pi指向i,pj指向j,变量i的地址放入指针变量pi中
cout<<i<<" "<<j<<" ";//输出i和j值
cout<<*pi<<" "<<*pj;//输出*pi和*pj的值
}
如果有
*p+=1;
++*p;
(*p)++;
这三个表达式的不同。++和 * 两个运算符的优先级相同。但按自右至左的方向结合。因此++*p相当于++(*p)。而在(*p)++中,一对括号不可以少,(p)++代表先取里面的值然后加1.而如果去掉1了成了p++,根据顺序,先++然后再取值.这样就成了先p++再取值.而p++就是指针先向后移动
7.指针的移动
以下内容配合指针和一维数组的关系学习更佳
给你个人博客链接
一.指针移动与一维数组与指针数组和数组指针
二.指针和二维数组
三.void指针和NULL指针
待更新
四.指向指针的指针
待更新
8.指针与函数
请先阅读我的函数思想再来看这个效果更佳
函数思想已经编辑完全,重点看末尾部分
一.值传递
这种方法,调用函数将实参(常数、变量、数组元素或计算的表达式)的值传递到被调用的函数形参设置的临时变量存储单元中,被调用函数形参值的改变对调用函数的实参无影响。调用结束后,形参储存单元被释放,实参仍保持原值不变,调用结束后,形参存储单元被释放
#include<stdio.h>
void sqr(int a,int b);
int main(void)
{
int a=5,b=3;
int c=0;
sqr(a,b);
printf("main c=%d",c);
}
void sqr(int w,int e)
{
int c=0;
c=w*e;
printf("void c=%d\n",c);
}
二.函数之间地址值的传递
函数思想
若实参是指针变量或地址表达式,则调用函数将实参指针变量指向单元的地址或实参的地址传递给被调用参数的临时变量存储单元。此时,相应的形参必须是指针型变量,在被调用函数执行时,也是直接去访问相应的单元,形参的变化直接修改调用函数实参相应的单元.因此,当实参是数组名、指针变量或地址表达式时,实参与形间的传递是双向传递,称为“地址传递”
(1)若函数的形参为指针变量**,调用函数时,对应的实参必须是基类型相同的地址值或者已指向某个存储单元的指针变量。
例子:调用swap函数,交换主函数中变量x和y中的数据
#include <stdio.h>
void swap(int *a,int *b);
int main(void)
{
int x=30,y=20;
printf("(1)x=%d y=%d\n",x,y);
swap(&x,&y);
printf("(4)x=%d y=%d\n",x,y);
}
void swap(int *a,int *b)
{
int t;
printf("(2)a=%d b=%d\n",*a,*b);
t=*a;
*a=*b;
*b=t;
printf("(3)a=%d b=%d\n",*a,*b);
}
//调用swap函数,交换主函数变量x和y的数据
若函数实参为指针变量则如下
void swap(int *a,int *b)
{
int t;
printf("(2)a=%d b=%d\n",*a,*b);
t=*a;
*a=*b;
*b=t;
printf("(3)a=%d b=%d\n",*a,*b);
}
输出结果:
(1)x=30 y=20
(2)a=30 b=20
(3)a=20 b=30
(4)x=20 y=30
(2)若数组名作实参
数组名也可以作为实参传送,但数组名是一个地址值,因此,对应的形参就是一个指针变量,此指针变量的基类型必须与数组的类型一致,这样就可以通过指针变量来调用函数中对应的数组元素,从而达到对调用函数中对应的数组元素进行操作而改变其中的值
先看这个简单例子
#include<iostream>
using namespace std;
//void swap(int a[],int b[]) //形参第3种写法,表示地址传递
void swap(int a[10],int b[100]) //形参第2种写法,表示地址传递
//形式参数写成数组形式,仅仅表示指针变量
//void swap(int *a,int *b) //形参第一种写法
{
int t=*a;
*a=*b;
*b=t;
cout<<*a<<" "<<*b<<endl;
}
int main(void)
{
int x=3,y=5;
swap(&x,&y);
cout<<x<<" "<<y<<endl;
return 0;
}
再看复杂点的
#include<iostream>
using namespace std;
//void sort(int *array,int n); //函数原型
//形式参数写成数组形式,仅仅表示指针变量,编译器会自动转化成指针变量的形式
void sort(int array[],int n);
int main(void)
{
int a[10],i;
cout<<"enter array:"<<endl;
for(i=0;i<10;i++)
cin>>a[i];
sort(a,10);
cout<<"The sorted array:"<<endl;
for(i=0;i<10;i++)
cout<<a[i]<<" ";
cout<<endl;
return 0;
}
//void sort(int array[1000],int n)
//void sort(int *array,int n)
//形式参数写成数组形式,仅仅表示指针变量,编译器会自动转化成指针变量的形式
void sort(int array[],int n)
//实参数组的首地址传递给了形参数组的首地址array(即array+0)
//所以array[i](即*(array+i))访问的是实参数组中的变量
{
int i,j,k,t;
for(i=0;i<n-1;i++)
{
k=i;
for(j=i+1;j<n;j++)
if(array[j]<array[k]) //if(*(array+j)<*(array+k))
k=j;
t=array[k]; //t=*(array+k);
array[k]=array[i]; //*(array+k)=*(array+i);
array[i]=t; //*(array+i)=t;
}
}
(3)引用传递 本来不想复习的,但数据结构要用
引用基础知识
看完再看,大神请忽视
#include<iostream>
using namespace std;
void mySwap01(int a,int b) //值传递 ,形参不会修饰实参
{
int temp=a;
a=b;
b=temp;
cout<<"swap01a="<<a<<endl;
cout<<"swap01b="<<b<<endl;
}
void mySwap02(int *a,int *b) //地址传递
{
int temp=*a;
*a=*b;
*b=temp;
}
void mySwap03(int &a,int &b) //引用传递
{
int temp=a;
a=b;
b=temp;
}
int main(void)
{
int a=10,b=20;
//mySwap01(a,b);
//mySwap02(&a,&b);
mySwap03(a,b); //引用传递,形参会修饰实参
cout<<"a="<<a<<endl;
cout<<"b="<<b<<endl;
}
9.指针变量的两类交换问题
一.仅pi,pj储存地址的改变(指向改变),i,j值不变
//指针变量相关的交换问题
#include<iostream>
using namespace std;
int main(void)
{
int i=5,j=10;//定义整形变量i,j
int *pi,*pj,*p=NULL;//定义指针变量pi,pj
pi=&i;pj=&j;
p=pi;pi=pj;pj=p;//实现地址的交换
cout<<i<<" "<<j<<" ";//输出i和j值
cout<<*pi<<" "<<*pj;//输出*pi和*pj的值
}
运行结果
5 10 10 5
二.pi,pj地址储存的值交换,指向未变i,j值变
//指针变量相关的交换问题
#include<iostream>
using namespace std;
int main(void)
{
int i=5,j=10,k=0;//定义整形变量i,j
int *pi,*pj;//定义指针变量pi,pj
pi=&i;pj=&j;
k=*pi;*pi=*pj;*pj=k;
cout<<i<<" "<<j<<" ";//输出i和j值
cout<<*pi<<" "<<*pj;//输出*pi和*pj的值
}
10.结构体类型指针的定义及使用
一.指向结构体变量的指针
定义一个指向结构体变量的指针指向结构体变量的指针的值是该结构体变量在内存存储区域的首地址。
结构体指针变量定义的一般形式为
struct 结构名 *结构指针变量名
因为是指针,所以具有一维指针的t特性
struct student *p=&stu ;
等价于
struct student *p;
p=&stu;
指针都是先定义赋值后使用
同时
struct student
{
int num;
.....
....
}boy;
p=&boy correct 结构变量
p=& student wrong 结构名只表示一个结构形式
编译系统并不对它分配空间
只有当某变量被说明为这种类型的结构时,才对该变量分配存储空间
其访问的一般形式为:
(*结构指针变量).成员名
或
结构指针变量->成员名
例如
(*p).num
或者
p->num
二.结构指针变量作函数参数
(1)用结构体变量的成员作参数
#include<stdio.h>
#include<string.h>
void print(struct student);
struct student
{
int num;
//char *name;
char name[20];
float score[3];
};
int main(void)
{
struct student stu;
//stu.name="lupeng";
strcpy(stu.name,"lupeng");
//是将常量区中复制到栈区,也就是内存里面有两份 ,这与结构体成员为指针完全不同
stu.score[0]=100;
stu.score[1]=99.5;
stu.score[2]=98;
print(stu);
}
void print(struct student fuck)
{
printf("\tnum :%d\n",fuck.num);
printf("\tname :%s\n",fuck.name);
printf("\tscore_1 : %5.2f\n",fuck.score[0]);
printf("\tscore_2 : %5.2f\n",fuck.score[1]);
printf("\tscore_3 : %5.2f\n",fuck.score[2]);
}
(2)用结构体变量作实参
#include<stdio.h>
#include<string.h>
void print(struct student *);
struct student
{
int num;
char name[20];
float score[3];
};
int main(void)
{
struct student stu;
strcpy(stu.name,"lupeng");
stu.score[0]=100;
stu.score[1]=99.5;
stu.score[2]=98;
print(&stu);
}
void print(struct student *p)
{
printf("\tnum :%d\n",p->num);
printf("\tname :%s\n",p->name);
printf("\tscore_1 : %5.2f\n",p->score[0]);
printf("\tscore_2 : %5.2f\n",p->score[1]);
printf("\tscore_3 : %5.2f\n",p->score[2]);
}
11.结构体数据类型数据的动态存储分配及后续链表
我将在下次更新,请持续更新
为保证阅读体验,我将开一个新章节