[学习笔记]C语言中关于指针的详解1

什么是指针

操作系统给每个存储单元分(即一个字节)配了一个编号,从0x00 00 00 00 到 0xff ff ff ff
这个编号称之为地址
指针就是这个地址
c语言中 占多个多字节的变量 存储单元编号最小的那个编号 是多字节变量的地址编号

指针变量

指针变量是一个只用来存储地址的变量 但不可随意赋值一个0x00 00 00 01之类的值
因为这个地址可能还没有开辟内存空间 指针变量只能保存开辟好空间的地址
在32位的系统下 地址总线是32位的 所以地址编号是32位的
所以指针变量是32位的 即4个字节

指针的类型

无论什么类型的指针变量 存储的都是地址 且都是4个字节(32位系统)
对应类型的指针变量 只能存放对应类型的变量的地址
1.字符指针 char *p
2.短整形指针 short *p
3.整形指针 int p
4.长整形指针 long
p
5.浮点型指针 float *p
6.double型指针 double *p
7.函数指针
8.结构体指针
9.数组指针
10.指针的指针

指针的定义

数据类型 * 变量名;
例: int *p;
定义变量的时候 *用来修饰变量 代表p是一个指针变量

指针的运算符

在定义时 * 标识这是一个指针变量  在其他时候表示取值
&  表示取一个变量的地址
例: 
int a = 10int *p; 
定义一个整形指针变量 p 和一个 整形变量a 值为10
p = &a;    
将a的地址给p
cout<<*p;
此时输出p的值 为10
		

数组的指针

c语言规定 数字的名字就是数组的首地址 即第0个元素的地址,是个常量
    int a[10];
    a[0]=1;
    a[1]=2;
    int *p=a;
    printf("数组地址:%p 值地址:%p 值%dn",a,&a[1],a[1]);
    printf("数组地址:%p 值地址:%p 值%dn",p,&p[1],p[1]);
输出结果:
	数组地址:0x7ff7bce31940 值地址:0x7ff7bce319442
	数组地址:0x7ff7bce31940 值地址:0x7ff7bce319442

注意 p和a不同, p是指针变量 而a是一个常量 是数组的首地址
例如 指针变量p可以进行 p++ 自增运算 但数组名a就不可以
a是数组的名字,是a[0]的地址,p=a即p也保存了a[0]的地址,即a和p都指向a[0] 所以在引用数组元素的时候a和p等价
不同的是 a是一个常量 存储的是a[0]的地址 而p是一个变量 所以a不可以用=赋值 p可以赋值
对a取地址和对p取地址 结果不同 对a取地址 结果为数组指针 而对p取地址 结果是指针的指针

指针的运算

指针可以加一个整数,结果还是个地址
    int a[10] = {1,2,3,4,5};
    int *p=&a[1];   //将数组a中第1个元素的地址给p   此时p的值为2
    printf("数组地址:%p 值地址:%p 值%dn",p,&p[0],*p);
    p++;     //将指针自增1 指向下一个地址   即a[2]
    printf("数组地址:%p 值地址:%p 值%dn",p,&p[0],*p);
输出结果:
	数组地址:0x7ff7bbd0b944 值地址:0x7ff7bbd0b9442
	数组地址:0x7ff7bbd0b948 值地址:0x7ff7bbd0b9483
	
   字符数组也可以这样操作
    char a[10]= "hello";
    char *p = a;
    printf("数组地址:%p 值地址:%p 值: %cn",p,&p[0],*p);
    p += 2;    //指向后面第二个地址 即a[2]
    printf("数组地址:%p 值地址:%p 值: %cn",p,&p[0],*p);
输出结果:
	数组地址:0x7ff7bc3e095e 值地址:0x7ff7bc3e095e: h
	数组地址:0x7ff7bc3e0960 值地址:0x7ff7bc3e0960: l

指针之前可以比较大小 前提是两个相同类型的指针指向同一个数组的元素的时候,比较大小才有意义。
指向前面元素的指针 小于 指向后面元素的指针
两个相同类型的指针可以相互赋值 (void *类型的除外)

指针数组

一个数组中所有的元素都是类型相同的指针变量,这个数组就是指针数组
指针数组的定义:
	int *p[10];     //定义一个有10个元素的数组指针 每个元素都是int类型的指针变量
	和普通指针变量类似 数组指针也有不同类型 例如float double long 结构体等指针数组
	一般情况下 指针数组用来保存多个字符串
	    char *p[3] = {"How","dare","you"};
	    int i = 3;
	    for(i=0;i<3;i++){
	        printf("%s ",p[i]);
	    }
	输出结果:
		How dare you 

指针的指针

指针的指针 即指针的地址
一个指针变量本身占4个字节 指针变量也有地址编号
int a;  //定义一个变量a
int *p = &a; //定义一个指针变量p 将a的地址给p
那么此时 *p就等于a
int **p2 = &p; // 定义一个指针变量p2(二级指针) 将p的地址给p2
那么此时 *p2就等于p  **p2就等于a
int ***p3 = &p2 定义一个指针变量p3(三级指针)同理 ***p3等价于a
p p2 p3都是指针变量 都占4个字节
上代码帮助理解:
    int a = 10;
    int *p = &a;
    int **p2 = &p;
    int ***p3 = &p2;
    printf("a=%dn*p=%dn**p2=%dn***p3=%dn",a,*p,**p2,***p3);
    printf("&a=%pn&p=%pn&p2=%pn&p3=%pn",&a,p,p2,p3);
    printf("&a=%pn&p=%pn&*p2=%pn&**p3=%pn",&a,p,*p2,**p3);
输出结果:
	a=10
	*p=10
	**p2=10
	***p3=10
	&a=0x7ff7bc836968
	&p=0x7ff7bc836968
	&p2=0x7ff7bc836960
	&p3=0x7ff7bc836958
	&a=0x7ff7bc836968
	&p=0x7ff7bc836968
	&*p2=0x7ff7bc836968
	&**p3=0x7ff7bc836968

字符串和指针

字符串的概念: 字符串就是以''为结尾的若干字符的集合
字符串储存的形式有:数组、字符串指针、堆
字符串数组: 
	char str[10] = "hello";
	定义一个字符数组 str  字符串hello存放在变量str中 可按下标取值
字符串指针:
	char *str = "hello";
	定义一个字符串指针变量 str  字符串指针变量只能存放字符地址编号
	即 hello这个字符串不存放在str中  str只存放了 h 的地址编号 hello存放在文字常量区
堆:
	char *str = (char*)malloc(10*sizeof(char));
	动态申请了10个字节的空间,首地址给str赋值

1.栈和全局内存中的内容是可以修改的 例如字符串数组 str[0]=“p”; 可以修改
2.文字常量区的内容是不可修改的 字符串指针 *str = “haha”;则不可以修改
3.堆区的内容是可以修改的 堆 *str =“haha”; 则可以修改
字符数组,指针定义的str可以在定义时直接初始化赋值,堆中存放的字符串不能初始化,只能通过strcpy,scanf赋值

数组指针

数组指针的概念: 本身是一个指针,指向一个数组,+1跳一个数组,即指向下一个数组
数组指针的作用就是可以保存二维数组的首地址
数组指针的定方法:指向的数组的类型+*指针变量名)[指向的数组的元素个数]
	int(*p)[5];   //定义了一个数组指针变量p  p指向的是有5个整型元素的数组
数组指针对于二维数组的应用:
	int a[3][5];  //定义一个3行5列的二维数组
    int (*p)[5];  //定义一个数组指针 p+1会指向下一个有5个整型元素的数组
    printf("a[0]的地址: %p  a[1]的地址: %pn",&a[0],&a[1]);
    p=a;
    printf("p的地址: %p  p+1的地址: %p",p,p+1);   //p+1指向下一个元素 即a[1]
输出结果:
	a[0]的地址: 0x7ff7b1cfd920  a[1]的地址: 0x7ff7b1cfd934
	p的地址: 0x7ff7b1cfd920  p+1的地址: 0x7ff7b1cfd934
数组指针可以将二维数组的首地址传递到另一个函数中,此时函数的形参就要定义为数组指针
一维数组指针就是二维数组的首地址 二维数组指针就是三维数组的首地址 以此类推
多维数组的数组名取*代表指针降级

指针与函数的关系

函数的形参有int long char float等,除此之外也可以给函数传递一个地址
函数的传参方式:
	1.复制传参
	2.地址传参
	3.全局传参(几乎用不到 全局参数可以直接使用)
定义两个函数: f1为值传递   f2为指针传递 都进行重新赋值操作
	void f1(int a){
	   int b = 10;
	    a = b;
	    printf("%p,%dn",&a,a);
	}
	void f2(int *a){
	    int b = 10;
	    *a = b;
	    printf("%p,%dn",a,*a);
	}
运行定义好的两个函数:
	int a = 100;
    f1(a);
    printf("%p,%dn",&a,a);       //传值
    int a2 = 100;
    f2(&a2);
    printf("%p,%dn",&a2,a2);     //传地址
输出结果:
	0x7ff7b315e94c,10
	0x7ff7b315e968,100
	0x7ff7b315e964,10
	0x7ff7b315e964,10
值传递 在函数内对形参的任何操作 不会影响实参 
地址传参:将实参的地址传递给形参,形参对保存的地址的内容进行操作,实参的值也会跟着变
地址传参若要改变参数的值 必须通过 *+地址去赋值 无论这个变量是什么类型
若要传递的实参本身是一个地址,例如字符串指针,则要传递指针的地址
例:
	int *str = "hello";
	f2(**p);

传递数组:
	传一维数组,由于数组名本身是一个首地址,所以传递时直接使用地址传递
	int a[10];
	f2(int*a);
	传二维数组,可以使用数组指针来传递
	int a[3][5];
	f2(int (*a)[5]);  
	传指针数组,可传指针数组的首地址,即传一个二级指针;
	char *p[3] = {"what","the","fuck"};
	f2(char **p);  
数组传参本质都是地址传递,所以在函数内部操作形参 都会改变实参

准备转行嵌入式,这阵子都是拿手在记笔记,忽然发觉手记的万一有改动不太好弄,且日后也不太好找出来看,文章都是个人心血来潮入坑嵌入式的学习笔记,下一篇c语言中关于指针的详解2 会继续记录指针与函数的关系以及结构体指针,文件指针
今天累了 先溜了~

在这里插入图片描述

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>