C语言学习笔记——指针

目录

1、指针是什么

2、指针和指针类型

    2.1 指针变量的大小 

    2.2 指针类型的意义

    2.3 void*指针

    2.4 const修饰指针

3、指针的运算

4、野指针

    4.1 野指针的成因

    4.2 如何规避野指针

5、指针和数组

6、二级指针

7、字符指针

8、指针数组

9、数组指针

10、数组传参和指针传参

    10.1 传一维数组

    10.2 传二维数组

    10.3 一级指针作形参

    10.4 二级指针作形参

11、函数指针

    11.1 函数指针数组

    11.2 指向函数指针数组的指针


1、指针是什么

补充:计算机中的单位

bit  比特位  二进制中一个比特位中存放0或1

Byte  字节  1 Byte = 8 bit

KB             1 KB = 1024 Byte

MB             1 MB = 1024 KB

GB             1 GB = 1024 MB

TB              1 TB = 1024 GB

......

  • 内存是电脑上的存储设备,它被划分为一个个的内存单元,每一个内存单元占一个字节,并且每一个内存单元都有一个编号,这个编号在生活中就相当于地址,通过这个地址我们可以很方便地找到任意一个内存单元,而在c语言中,我们又把这个编号(地址)叫做指针。所以,内存单元的编号=地址=指针
  1. 指针是内存中一个最小单元的编号,也就是地址
  2. 平时口头上说的指针,通常指的是指针变量,是用来存放内存地址的变量
  • 总结:指针就是地址,地址就是指针,口头上说的指针通常指的是指针变量。

指针变量

通过用&(取地址操作符)取出变量的内存起始地址,把地址存放到一个变量中,这个变量就是指针变量。

2、指针和指针类型

    2.1 指针变量的大小 

  • 对于32位的机器,假设有32根地址线,而每根地址线能产生高电压(1)和低电压(0),那么32根地址线就能产生2^32个0/1二进制序列。每个编号占用32个比特位,即4个字节。
  • 指针变量的大小在32位平台是4个字节,在64位平台是8个字节

    2.2 指针类型的意义

  1. 指针的解引用:指针的类型决定了指针在解引用时访问的权限有多大(一次能操作几个字节)。例如:char*的指针解引用访问1个字节,float*的指针解引用访问4个字节。
  2. 指针+-整数:指针的类型决定了指针向前向后走一步有多大(+/-整数时跨越几个字节)。

    2.3 void*指针

  • 无具体类型指针(泛型指针),可以接受任意类型的地址。
  • 局限性:void*类型的指针不能直接进行解引用和指针+-整数的运算。
  • 用途:一般使用在函数参数的部分,用来接收不同类型数据的地址,这样的设计可实现泛型编程的效果,使用一个函数来处理多种类型的数据。

    2.4 const修饰指针

int *p;// 无const修饰

int const * p;

  • const放在*的左边,修饰的是指针指向的内容,使指针指向的内容不能通过指针来改变,但是指针变量本身的内容可以变,即指针变量存放的地址是可以改变的。

int * const p;

  • const放在*的右边,修饰的是指针变量本身,使指针变量的内容不能改变,即该指针变量只能存放一个固定变量的地址,但是指针指向的内容,可以通过指针来改变。

int const * const p;

  • *的左右两边都有const,此时指针变量的内容不能改变,指针变量指向的内容也不能改变。

3、指针的运算

  • 指针+-整数
  • 指针-指针
  • 指针的关系运算(指针的大小比较)

4、野指针

    4.1 野指针的成因

  1. 指针未初始化
  2. 指针越界访问
  3. 指针指向的空间释放

    4.2 如何规避野指针

  1. 指针初始化:如果明确知道指针指向哪里就直接赋值地址,如果不知道可赋值NULL。
  2. 小心指针越界。
  3. 指针变量不再使用时,及时置NULL,指针使用之前检查有效性。(只要是NULL指针就不去访问,同时使用指针之前可以判断指针是否为NULL)
  4. 避免返回局部变量的地址。

5、指针和数组

  • 数组名表示的是数组首元素的地址。
  • p+i 计算的是数组arr[ i ]的地址。因此可以通过指针来访问数组。

int arr [ 10 ] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

int * p = arr;

  • 指针p存放数组arr首元素的地址,通过指针+-整数就可以实现对数组每个元素的访问了。

6、二级指针

凡是变量就有地址,而指针变量也是变量,因此也有地址,这个地址可以用二级指针来存放,即存放指针变量的地址的二级指针变量。

  • int a = 0;
  • int* pa = &a;
  • int* *ppa = &p;

对二级指针解引用:

解引用谁的地址就访问谁

  • * ppa 通过对 ppa 中存放的地址(即 pa 的地址)解引用,访问 pa
  • * *ppa  通过对 *ppa 中存放的地址(对ppa解引用得到pa的内容,而pa的内容是a的地址)解引用,访问 a 。即先通过 *ppa 找到 pa,然后对pa解引用,* pa,找到a。

7、字符指针

两种使用方式:

1、char ch = 'a';

     char *pc = &ch;

2、const char *pc = "hello world"; // 常量字符串直接赋值给字符指针变量,常量字符串的       printf("%s\n",pc);                   值不能被改变,因此加一个const更好

                                                          // 直接把一个常量字符串放到存放到字符指针中,实质                                                              上是将字符串hello world首字符的地址存放到字符指                                                               针pc中

8、指针数组

指针数组是存放指针的数组

  • int arr1 [ 5 ];//存放了5个整型变量的数组
  • char arr2 [ 5 ];//存放了5个字符型变量的数组
  • int* arr3 [ 5 ];//存放了5个指针变量的数组

9、数组指针

数组指针存放数组的地址的指针,指向的是数组。

int arr[ 5 ];

int(*p)[ 5 ]= &arr

区别指针数组和数组指针:

  • 指针数组:是一个数组,类型为数组存放的元素的类型。

int* arr[ 5 ]; // arr[ 5 ]是一个数组,数组的类型为int* [ 5 ],数组的元素是指针,而且是指向整型的指针变量。

  • 数组指针:是一个指针,指针指向的内容是一个数组。

int arr[ 10 ]; 

int(*p)[ 10 ] = &arr;数组指针的定义用根据数组的类型去定义。

辨别:

  • int arr[ 5 ];// arr是整型数组,数组有5个元素
  • int *parr1[ 10 ];// parr1是指针数组,数组有10个元素,每个元素是int*类型的
  • int (*parr2)[ 10 ];// parr2是数组指针,该指针指向的是一个数组,该数组有10个元素,每个元素是int类型的
  • int (*parr3[ 10 ])[ 5 ];// parr3是一个指针数组,该数组有10个元素,每个元素是指针类型的,而且是数组指针(指向含有5个元素的数组的指针),具体类型为:int(*)[ 5 ] 的数组指针类型。
//数组指针的应用
#include<stdio.h>

void print(int(*p)[4], int r, int c)
{
	int i = 0;
	for (i = 0; i < r; i++)
	{
		int j = 0;
		for (j = 0; j < c; j++)
		{
			printf("%d ", (*(p + i))[j]); //p[i][j]
		}
		printf("\n");
	}
}

int main()
{
	int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };
	print(arr, 3, 4);
	return 0;
}

10、数组传参和指针传参

    10.1 传一维数组

函数形参可以为数组指针

int test(int arr [ 5 ] )//形参为数组

int test(int arr [  ] )//数组大小可省略

int test(int *arr )//形参为指针

int main()

{

     int arr[ 5 ];

     test(arr);
}

    10.2 传二维数组

函数形参可以为二维数组数组指针

int test(int arr [ 4 ][ 5 ] )//形参为二维数组

int test(int arr [  ][ 5 ] )//二维数组的行可以省略,但列不能省

int test(int (*arr)[ 5 ] )//形参为数组指针,指向的是有五个元素的数组,可表示二维数组中的列

int main()

{

     int arr[ 4 ][ 5 ];

     test(arr);
}

    10.3 一级指针作形参

实参可以为指针变量取地址数组名。

int test(int *pc)

int main()

{

     int *p;

     test(p);

     int a;

     test(&a);

     int arr[ 5 ];

     test(arr);
}

    10.4 二级指针作形参

实参可以为二级指针一级指针的地址指针数组名。

int test(int **pc)

int main()

{

     int **p;

     test(p);

     int *q;

     test(&q);

     int * parr[ 5 ];

     test(parr);
}

11、函数指针

&函数名和函数名都表示函数的地址

用函数指针存放函数的地址:

int Add (int x,int y)

{

      return x+y;

}

int(*p)(int,int) =  &Add;

int(*p)(int,int) =  Add;

// p是一个存放函数地址的指针变量——函数指针。

    11.1 函数指针数组

  • 存放函数指针的数组。

    11.2 指向函数指针数组的指针

  • 本质上是一个数组指针,即该指针变量指向一个数组,而指向的这个数组存放的元素是函数指针类型。

  • 30
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值