目录
- 对象的访问方式
- 什么是指针
- 指针变量
- 与指针相关的运算符
- 指针变量作为函数参数
- 数组与指针
- 多维数组与指针
- 指针常量 和 常量指针
- 指针数组 与 数组指针
- 字符串与指针
- 函数指针
- 二级指针 与 多级指针
在C语言中,任何一个变量,都有两层含义:
(1) 代表该变量的存储单元的地址;变量的地址 左值 lvalue
(2) 代表该变量的值;右值 rvalue
对于一个变量的访问,只有两种情况:
一是把一个值写到变量的地址中去 (lvalue)
二是从变量的地址中取变量的值 (rvalue)
对象的访问方式
直接访问 : 通过对象名去访问
如:
int a; a = 1024; b = a;
注:直接访问 受到作用域的限制
间接访问 :
通过对象的地址去访问,指针访问。
只要你知道了对象的地址,就可以在任何地方去访问它。
不受作用域的限制。
什么是指针
存储单元(如: memory 内存)的地址:
分配给每个对象的内存单元都有一个编号,这个编号就是我们所说
存储单元的地址。并且存储单元按照字节来编号。
在C语言中,指针的概念与地址差不多的,你可以直接认为指针就是一个地址。
一个变量的地址,我们也称为变量的“指针”
& 取地执符
单目运算符, “取xxx对象的地址”
通过一个对象的指针去访问它,首先要解决对象的指针(地址) 的保存问题。
需要定义另外的一个变量去保存它的地址,这种变量我们称为指针变量。
指针变量
指针变量也是一个变量,也是用来保存数据,只不过指针变量保存的数据是其他对象的地址。
指针变量的定义:
指向的对象的类型 * 指针变量名;
“指向的对象的类型” :
指针变量指向的对象的类型,而不是指针的类型。
如:
int *p; int a=10; p=&a;//p保存了对象a的地址 // p 指向 a
与指针相关的运算符
& : 取地址符
int a;
&a : 表示取对象a的地址
对象a的地址,其实就是对象a的指针。
* : 指向运算符 ,单目运算符
*地址(*指针): 地址对应的那个变量(对象)。
如
int a=10; int *p=&a; int b=*&a;//b=10,int b=a
所以*(&a) <=> a
*& 可以直接约掉 (编译原理状态机的词法分析)
指针变量作为函数参数
传递的还是值!! 只不过这个“实参的值”可能是某个对象的地址!
C语言中,函数参数的传递只能是"值传递"
形参 = 实参的值
野指针:
void XXX2(int * m, int * n) { int * t;//定义了一个指针变量t, 但是t没有被赋值 //t没有赋值,但是不代表它没有值,相反它会有一个相对应的 //不知道这个值是多少 undefine *t = *m; //操作野指针,可能导致 段错误(对一个未知的空间进行写操作) *m = *n; *n = *t; //操作野指针,可能导致 段错误(对一个未知的空间进行读操作)
*t 放置到 ‘=’ 符号的左边,代表t指向对象的左值
对t的指向的对象进行写的操作
*t 放置到 ‘=’ 符号的右边,代表t指向对象的右值
对t的指向的对象进行read的操作
所以t是一个“野指针”,我们也不知道 t指向的内存空间是否可读可写,
如果不可读或者不可写,那么后续对*t操作,就会导致内存的非法访问 => 段错误!!
空指针:
NULL 0
在计算机中地址为0的存储空间是不存在的
如果一个指针的值,指向空(NULL)的指针,称之为空指针。
int *p=NULL; *p=1024;//操作空指针 int b=*p;//操作空指针
我们去使用空指针,一定会 造成内存的非法访问 => 段错误
数组与指针
数组元素与普通变量是一样的,数组元素也有自己的地址。
数组元素也有左值和右值,并且数组元素间的地址是相邻的。
数组名可以代表首元素的地址。
a => &a[0], 数组名a当做指针来看
如
int a[]={1,2,3}; int *p=a;//a是数组名当指针来看,==&a[0]
指针作加减的问题:
能不能通过指针p去访问a[1]?
p=p+1;//&a[0]+1==&a[1]
p + i (p是一个指针,i是一个整数值)
注:不是简单地加减数值,而是加减i个指针指向单元的长度。
p + 1 => 往后面挪了一个int单元的长度 ,&a[1]
int a[10]; int * p = a; // p = &a[0] a[1] <=> *&a[1] <=> *(&a[0] + 1) <=> *(a + 1) <=> a[1] a[1] <=> *&a[1] <=> *(&a[0] + 1) <=> *(p + 1) <=> p[1]
数组名a,在代码中有两层含义:
int a[10];
(1) 数组名代表整个数组
&a : 取整个数组a的地址。
&a + 1 : 往后面挪了1个(int[10])单元长度。
(2) 在合适情况下面,数组名可以当作指针来看
a <=> &a[0]
a + 1 : 当作指针来看
=> &a[0] + 1
=> &a[1]
多维数组与指针
在C语言中,所有的数组其实都是一维数组!
int a[3][4]; //int[4] a[3];
表达式 表达式的含义 表达式的值
a 数组名:
(1) 当作指针 &a[0] 取整个一维数组a[0]的地址
(2) 当作整个数组来看
a[0] 数组名:
(1) 当作指针 &a[0][0] 取数组元素a[0][0]的地址
(2) 当作整个一维数组a[0]来看
a[0][0] 数组元素a[0][0] a[0][0] (左值/右值)
a + 1 a是数组名,在此处当作指针 ==&a[0]+1==&a[1]
a[0] + 1 a[0] 是数组名,在此处当作指针 ==&a[0][0]+1==&a[0][1]
a[0][0] + 1 a[0][0]是二维数组a中的一个int类型的元素 ==a[0][0]+1
a[1] + 2 a[1]是数组名,此处当作指针 ==&a[1][0]+2==&a[1][2]
*(a + 1) + 2 a是数组名,在此处当作指针 ==*(&a[0]+1)+2==*(&a[1])+2==a[1]+2==&a[1][2]
指针常量 和 常量指针
他们都是指针,只不过他们之间的属性有一点区别。
指针常量:
指针本身不能改变(指针的指向不能变),但是
它所指向的内存空间里面的内容是可以改变的。
最具有代表性例子,数组名!
定义方式:
指向对象的类型 * const 指针变量名;
int a , b; int * const p = &a; p = &b ; //error
常量指针:
是一个指向常量的地址。指针指向的对象是常量,
那么指针本身的指向是可以改变的,但是这个指针指向
内存空间中的内容是不能够改变。
最具有代表性例子,字符串
char * p = "abcde"; //"abcde" <=> &'a'
定义方式:
const 类型 * 变量名;
or
类型 const * 变量名;
指针数组 与 数组指针
(1) 指针数组
指针数组是一个数组,只不过它里面的每一个元素都是指针罢了!
定义数组:
数组元素类型 数组名[元素个数];
“数组元素类型” : C语言中任意合法的类型都可以。
int * p[4]; //定义了一个指针数组,数组名为p,里面含有4个int*类型的指针。
(2) 数组指针
数组指针是一个指针,只不过这个指针是用来指向一个数组罢了!!
int (*p)[4] ; //定义了一个数组指针,指针变量名为p,用来指向一个含有4个int类型元素的数组的。
如
int a[3][4]; int (*p)[4]; p = a; //&a[0] *(*p + 1) // *(a[0]+1)==*(&a[0][1])==a[0][1] *(*(p + 2)) // *(*(&a[2]))==*(&a[2][0])==a[2][0] *(*(p+3) + 3)// *(*(&a[0]+3)+3)==*(*(&a[3])+3)==*(&a[3][0]+3)==a[3][3]
字符串与指针
字符串就是一串字符。在C语言中,没有字符串这个类型。
C语言字符串是通过 char *(字符指针)来实现。
C语言中的字符串,是用""引起来的一串字符来表示,并且字符串后面默认会加一个\0,\0(ASCII为0的那个字符)字符串结束的标志。
如
"abc" //字符串
"\n" //字符串
"" //字符串 => 空串
'\123' //字符
'\n' //字符
注:
我们只需要保存字符串的首字符地址就可以了,从首字符地址开始找到第一个\0,前面的这些字符就是字符串里面的字符。
在C语言中的字符串(如: “ssssssabcd”)是保存在一个叫做 .rodata(只读数据)的内存区域,字符串代表是这个内存空间的首地址。
"ssssssabcd" => 表达式的值 => &'s' char *p="12345" ==&'1' int *p=&'1'; p+1=&'2';
字符数组:
char s[5] = {'a', 'b', 'c', 'd', 'e'}; //字符数组,与普通数组是一样的,保存在一个.data/栈空间 //数组区域是可读可写的 sizeof(s) == 5 strlen(s) >= 5 //没有'\0' 长度不确定
char s[] = {"abcde"}; //自动生成'\0' <=> char s[] = {'a', 'b', 'c', 'd', 'e', '\0'}; //s字符数组,与普通数组是一样的,保存在一个.data/栈空间 //数组区域是可读可写的 sizeof(s) == 6 strlen(s) == 5
char s[] = {"abcde"}; s[1] = 'B'; //OK char ch = s[1]; //OK *(s + 1) = 'B'; //OK ch = *(s + 1); //OK s = s + 1; //ERROR //s是数组名,数组名当作指针,指针常量 //s的指向 是不能够改变的,但是s指向的内存空间 //里面的内容是可变的 printf("%s\n", s); //abcde %s : char* 地址 //把后面的那个地址当作是一个字符串的首地址,一个一个字符进行输出,直到遇到\0才会结束!
函数指针
函数也有地址 ,那么咱们可以定义一个指针,去存放一个函数的地址,像这样的指针,称为函数指针。
函数指针如何定义
int * abc(int a, float b) { }
描述函数abc的类型: int (int , float ) : 是一个返回值为int类型,带有一个Int类型和float类型的参数的函数类型
指向函数的返回值的类型 (*指针变量名) (指向函数的形参类型列表);
int (* p)(int, int) ;
定义了一个指针变量p,p是一个函数指针,是用来指向一个返回值为int类型,并且带有两个int类型参数的函数的。
函数指针如何赋值
&函数名
or
函数名 : 在C语言中,函数名本身就可以代表函数的地址
通过函数指针去调用指向的函数,有如下两种方案:
p为函数的指针。
(1) (*p)(实参列表)
(2) p(实参列表)
int *p=abc//函数名 p(a,b);//调用函数
二级指针 与 多级指针
int a,b;
可以定义一个指针变量p1,来保存变量a的地址:
int *p1=&a; b=*p1=*&a=a;//通过指针p1访问a变量
可以定义一个指针变量p2,来保存变量p1的地址:
int *p2=&p1 int b=**p2=*p1=a//通过指针p2访问a变量
p2保存的是一个一级指针p1的地址,所以说,
p2指向一个一级指针,p2就是一个二级指针
可以定义一个指针变量p3,来保存变量p2的地址:
int *p3=&p2; int b=***p3=**p2=*p1=a;
p3保存的是一个二级指针p2的地址,所以说,
p3指向一个二级指针,p3就是一个三级指针
到此这篇关于C语言简析指针用途的文章就介绍到这了,更多相关C语言指针内容请搜索自由互联以前的文章或继续浏览下面的相关文章希望大家以后多多支持自由互联!