c指针

228 阅读6分钟

1.认识指针

变量有四要素:变量类型、变量名、变量值、变量地址。

指针就是地址,指针存放的是变量的地址;同样的,指针也有地址,指针的指针存放的就是指针的地址。

1677124705279.jpg

如上,变量i的值是3,地址是2000;指针i_pointer的值是2000,地址是3020;说明指针存放的是变量的地址。

2.指针的表示方法

整型变量是存放整型数的变量;字符变量是存放字符型数据的变量;

因此,指针变量就是存放指针/地址的变量。

#include <stdio.h>
int main(){
    int a=0;
    int *p;//这里的*是一个标识符,告诉系统,这是一个指针变量,是用来保存别人地址的,和下方的运算符*不同
    p = &a;//p是地址
}

注意:*只产生在指针变量定义或声明的时候,其他情况下是取值运算符或者乘法运算符

3.利用指针实现变量的访问

变量的访问有两种方式,最常见的就是通过变量名访问(也称直接访问),还有一种是指针访问(也称间接访问)。

#include <stdio.h>

int main(){
    int a=0;
    int *p;
    p = &a;

    printf("a的值是%d\n",a);//访问a
    printf("a的地址是0x%p\n",&a);//利用取地址符&访问a的地址
    printf("利用取值运算符通过指针访问a的值是%d\n",*(&a));//利用取值运算符通过指针访问a
    printf("指针变量的方式访问a=%d",*p);//p是地址 
    return 0;
}

输出结果为:

a的值是0
a的地址是0x000000000061FE14
利用取值运算符通过指针访问a的值是0
指针变量的方式访问a=0

4.指针变量区分类型

指针变量的类型决定了指向空间的大小和增量。

int main(){
	int a=1234;
	int *p=&a;
	char *m=&a;
	
	printf("p的值是%p\n",p);//p的值是000000000061FE0C
	printf("m的值是%p\n",m);//m的值是000000000061FE0C
	
	printf("a的值是%d\n",*p);//a的值是1234
	printf("a的值是%d\n",*m);//a的值是-46
        //取值的时候出现问题,取值运算符会根据指针变量类型访问不同的大小空间

	printf("++p的值是%p\n",++p);//++p的值是000000000061FE10
	printf("++m的值是%p\n",++m);//++m的值是000000000061FE0D
        //自增之后发现int型增加4字节,char型增加1字节
	
	return 0;
}

5.利用指针实现数据交换

#include <stdio.h>
int tmp;
void swap(int data1,int data2){
	
	tmp=data1;
	data1=data2;
	data2=tmp;
	printf("交换后,1的值为%d,2的值为%d\n",data1,data2);
}

int main(){
	int data1=10;
	int data2=20;
	printf("交换前,1的值为%d,2的值为%d\n",data1,data2);
	swap(data1,data2);
	printf("交换后,1的值为%d,2的值为%d\n",data1,data2);
}
交换前,1的值为10,2的值为20
交换后,1的值为20,2的值为10
交换后,1的值为10,2的值为20

原因分析: 虽然函数和主函数中的变量名相同,但指向的内存空间不同。实际上仅在函数内部实现了交换。

此处需补充三种方式下,内存空间变化的图(不封装、函数封装、指针)

改进: 利用指针,间接实现变量的交换。 函数直接对指针进行修改。

int tmp;
void swap(int *p,int *q){
	tmp=*p;
	*p=*q;
	*q=tmp;
	printf("交换后,1的值为%d,2的值为%d\n",*p,*q);
}
int main(){
	int data1=10;
	int data2=20;
	printf("交换前,1的值为%d,2的值为%d\n",data1,data2);
	swap(&data1,&data2);
	printf("交换后,1的值为%d,2的值为%d\n",data1,data2);
}
交换前,1的值为10,2的值为20
交换后,1的值为20,2的值为10
交换后,1的值为20,2的值为10

需要注意:在利用函数进行指针传参时,使用的是&data而不是*p;

× swap(*p,*q);
√ swap(&data1,&data2);

6.使用给定地址确定指针

#include <stdio.h>
int main(){
	int data1=10;
	printf("data1的地址是%p\n",&data1);
	volatile unsigned int *p=(volatile unsigned int *)0x000000000061FE0C;//令指针指向给定的内存地址
	printf("p=0x%p\n",p);	
}

volatile是防止给定的内存地址被优化成其他的,造成误差

data1的地址是000000000061FE14
p=0x000000000061FE0C

7.数组指针

可以用一个指针变量指向数组a的第一个元素,或者是指向数组a,二者结果相同并且等价。

#include <stdio.h>
int main(){
	int arr[10]={1,2,3,4,5,6,7,8,9,10};
	int *p;
	int *q;
	p=&arr[0];
	q=arr;
	printf("p的值是:0x%p\n",p);//数组第一个元素的地址就是数组的首地址
	printf("q的值是:0x%p\n",q);//数组名就是数组的首地址
        printf("数组的首元素是:%d",*p);
        
}
p的值是:0x000000000061FDE0
q的值是:0x000000000061FDE0
数组的首元素是:1

8.指针偏移遍历数组

p+i中的i为偏移量,而非对p进行加1,此时与指针p的变量类型有关。自增不等同于具体地址加1

*(p+i)指向第i个偏移元素

#include <stdio.h>
int main(){
	int arr[10]={1,2,3,4,5,6,7,8,9,10};
	int *p;
	p=arr;
	for(int i=0;i<10;i++){
		printf("数组arr的第%d个元素是:%d\n",i,*(p+i));
	}
}
上述代码中的for循环也可改成:
for(int i=0;i<10;i++){
		printf("数组arr的第%d个元素是:%d\n",i,*p);
                p++;//但要注意,此时指针p发生改变,后续再用的时候需要将其修改为数组初值
	}
数组arr的第0个元素是:1
数组arr的第1个元素是:2
数组arr的第2个元素是:3
数组arr的第3个元素是:4
数组arr的第4个元素是:5
数组arr的第5个元素是:6
数组arr的第6个元素是:7
数组arr的第7个元素是:8
数组arr的第8个元素是:9
数组arr的第9个元素是:10

9.常量指针与指针变量

arr指向的是数组的首地址,是确定且不可更改的,被视为常量指针;*p为指针变量。

指针常量不可以自增,如arr++;

指针变量可以自增,如*p++;

#include <stdio.h>
int main(){
	int arr[3]={1,2,3};
	int *p=arr;
	printf("sizeof arr is %d\n",sizeof(arr));
	printf("sizeof p is %d\n",sizeof(p));
	printf("sizeof int is %d\n",sizeof(int));
        printf("sizeof pointer is %d\n",sizeof(int *));	
        printf("sizeof pointer is %d\n",sizeof(char *));	
	for(int i=0;i<3;i++){
		printf("%d ",p[i]);//1 2 3
	}
	puts("");
	for(int i=0;i<3;i++){
		printf("%d ",*(arr+i));//1 2 3
	}
	puts("");
	for(int i=0;i<3;i++){
		printf("%d ",*p++);//1 2 3
	}
	puts("");	 
}
sizeof arr is 12//3*4=12,一个元素占4个字节,数组中共有3个元素,数组元素为int
sizeof p is 8//os用8个字节表示一个地址
sizeof int is 4//一个整型变量占4个字节
sizeof pointer is 8//os用8个字节表示一个地址
sizeof pointer is 8//os用8个字节表示一个地址

由上可知,指针视为地址,指针大小与指向变量的类型无关,恒为8个字节