亲宝软件园·资讯

展开

C语言指针

ChampLixxx 人气:0

在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就是一个三级指针

加载全部内容

相关教程
猜你喜欢
用户评论