【指针】c语言笔记

当变量在内存中存储时,内存会被分为一块一块的去容纳不同的变量。每一块都有一个特有的编号。这个编号就是该内存块的地址,我们可以通过这个地址导航到特定的内存块。而指针就是储存这个地址的特殊变量。
所以指针不仅作为变量本身占据内存区,它还联系(指向)了其他内存区,于是我们要搞清指针的四个要素
指针的类型
指针指向的类型(即内存区里的变量的类型)
指针本身占据的内存区

指针的值(即所指向的内存区的地址)

1 . 指针要素

  • 指针运算符*
    数据类型 *指针名
  • 取址运算符
    &所指向的变量名
int *a;//a与*结合,证明a是一个指针,指向类型是一个int变量
>char *b;//b与*结合,证明b是一个指针,指向类型是一个char变量
>int c;
>char d;
>a=&c;//指针a的值是c的地址,故a指向c
>b=&d;//指针b的值是d的地址,故b指向d

& 与* 是互逆的,所以x==*&x;

可以看到我们在定义一个指针时也定义了指针所指的变量类型,int 变量的指针需要用 int 类型的指针存储,char 变量的指针需要用 char类型的指针存储。如何使int类型的指针存储char类型的变量呢,要用到强制类型转换

(类型名) (表达式) 或者 (类型名) 变量名
char a=‘a’;
int * b=(int*)&a;
强制类型转换不改变原来变量和表达式的类型属性

  • 指针是可以相互赋值的

int a=1;
int *b=&a;
int *c;
c=b;

指针的安全问题

在32 位程序中,shar占一个字节,int 类型占四个字节。最后一条语句不但改变了a 所占的一个字节,还把和a相临的高地址方向的三个字节也改变了。这三个字节里也许存储了非常重要的数据,如果你此时对指针b进行改变(*b =1111),这三个字节的值就也被改变了
故:
在指针的强制类型转换:b=(TYPE *)a 中,如果sizeof(a的类型)大于sizeof(b 的类型),那么在使用指针b 来访问a所指向的存储区时是安全的。如果sizeof(a的类型) 小于sizeof(b的类型),那么在使用指针b来访问a所指向的存储区时是不安全的。

指针类型与所指类型

从语法上看,你只要把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。这是指针本身所具有的类型。
从语法上看,你只须把指针声明语句中的指针名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。

要注意,即使出现*,定义的也不一定是指针
[ ]的优先级是大于*的,看下例

int **n;//n与*结合,n是一个指针,类型是int**,所指的类型是int*,即指向另一个指针,该指针指向一个int变量 
int *n[3];//n先与[ ]结合,故n是一个数组,数组中每个单元是一个int* 指针, 这些指针都指向一些int变量 
int (*n)[3];//()中n先与*结合,故n是一个指针, 该指针指向一个三单元数组,数组储存的变量为int 
int *(*n)[3];// ()中n先与*结合,故n是一个指针, ()外[]优先级高于*,故该指针指向一个三单元数组,
//每一单元都是一个指向int的指针 

指针本身占据的内存区与指针的值

  • 指针本身占据的内存区
    指针本身占了多大的内存?你只要用函数sizeof(指针的类型)测一下就知道了。在32 位平台里,指针本身占据了4 个字节的长度。在64 位平台里,指针本身占据了8个字节的长度。
  • 指针的值(所指向的内存区的地址)
    指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。指针的值是该内存区的首地址。
  • 指针与其指向的内存区是一一对应的,或者说与该内存区的变量名是一一对应的,当变量a,b储存的内容一样时,a,b所占据的内存块并不一致,他们的地址并不相同

2 . 指针的运算

算数运算

 (1)int a[10];
    int *b=&a;//指针的值为a的首地址,即a[0]的地址 
    printf("%d",*b);//*b的值为a[0]的值 
    b++;//指针所指的地址向高地址增加了一个单元,即指向a[1] 
    printf("%d",*b);//*b的值为a[1]的值

当类型转换时,要注意:
所增加的单元是指针类型的对应类型的单元

 (2)char a[10];
    int * b=(int*)&a;
    printf("%d",*b);//*b的值为a[0]的值 
    b++;//指针所指的地址向高地址增加了一个int单元,一个int是4个字节,即指向a[4] 
    printf("%d",*b);//*b的值为a[4]的值 
  • 指针与指针
  • 相加:两个指针不能进行加法运算,这是非法操作,因为进行加法后,得到的结果指向一个不知所向的地方。
  • 相减
  • 两个指针可以进行减法操作,但一定要指向同一个数组,相减结果是两个指针之间的元素的数目,而不是两个指针之间相差的字节数
int a[4] = {10,12,15,20 };
int * c,*b;
c = &a[1];
b = &a[3];
printf("%d\n",b-c);//结果为a[3]-a[1]=8 

关系运算

px > py // px 指向的存储地址是否大于 py 指向的地址
px == py // px 和 py 值是否相同(指向同一内存区)
px == 0 和 px != 0 //px 是否为空指针

3 . 指针与数组

int a[10];
int *b;

数组的数组名其实可以看作一个指针。一个数组的首地址即为数组名a,有

b=a;

或者第一个元素的首地址也是数组的首地址,有

b=&a[0];

对数组a,有

*a==a[0];
*(a+1)==a[1];
//以此类推

故,当声明了一个数组,则数组名称a 就有了两重含义:
第一,它代表整个数组
第二,它是一个常量指针,该指针指向的内存区就是数组第0 号单元,该指针自己占有单独的内存区,它和数组第0 号单元占据的内存区是不同的。
注意:该指针的值是不能修改的,即类似a++的表达式是错误的
当然,数组名不等价于指针变量,指针变量可以进行 p++和&操作,而这些操作对于数组名是非法的。数组名在编译时是确定的,故数组名所代表的指针也是确定的,所以我们说它是一个常量指针

与多级数组

对多级数组int a[10][10][10],它的首地址是a[0][0][0]的地址
即在数值上,有a == a[0] == a[0][0] == &a[0][0][0];

下面我们尝试对指向二级数组的指针进行算术运算

int a[2][2] = {
	{1, 2},
	{3, 4}
};//定义一个二维数组


int *b[2] = {a[0], a[1]};//我们可以用指针数组 p 操作一个二维数组

//b 为数组 a的首地址,b[0] == a[0] == *b,**b == a[0][0]

//b+1 移动到 a[1]的地址,*(b +1) == a[1],则**(b + 1) == a[1][0]


//先有*b==a[0],*b + 1 == &a[0][1],注意a[0]是一个一维数组,该一位数组向高位走一个int即为该数组的第1个单元(a[0][0]是第0个单元 
//所以*(*b+1)==a[0][1]; 

4 . 指针与结构体

结构体与指针查找

5 . 指针与函数

函数参数可以为 int、char、float 等,但是在操作时,这些参数只作为形参,所有操作都只在函数体内有效。而指针表达式可以作为实参做出对main同样有效的改变

void summ(int *a, int *b);
int main(){
	int a = 20, b = 10;
	swap(&a, &b);
	printf("a = %d, b = %d", a ,b);//结果a=10,b=20
}
void summ(int *a, int *b){
	int c;
	t = *a;
	*a = *b;
	*b = c;
}

6 . 多级指针

前面指针类型的笔记中的多级指针

int **n;//n与*结合,n是一个指针,类型是int**,所指的类型是int*,即指向另一个指针,该指针指向一个int变量 
int *(*n)[3];// ()中n先与*结合,故n是一个指针, ()外[]优先级高于*,故该指针指向一个三单元数组,

多级指针即指针指向的是另一个指针,另一个指针指向的也可以是又另一个指针
以耳二级指针为例,有int **n,指针n的类型为int **,n指向的是另一个指针变量(此假定为指针p),p指向一个int变量(此假定为int a),则n的值为指针p的地址(p的地址为a的地址)

p=&a;
n=&p;

能否用n=&&a;
不能,因为&a是一个具体数值而非一个指针,n的类型决定了它只能取指针的地址

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值