C语言指针知识点总结
1.指针的使用和本质分析
(1)初学指针使用注意事项
1)指针一定要初始化,否则容易产生野指针(后面会详细说明);
2)指针只保存同类型变量的地址,不同类型指针也不要相互赋值;
3)只有当两个指针指向同一个数组中的元素时,才能进行指针间的运算和比较操作;
4)指针只能进行减法运算,结果为同一个数组中所指元素的下表差值。
(2)指针的本质分析
①指针是变量,指针*的意义:
1)在声明时,*号表示所声明的变量为指针。
例如:int n = 1; int* p = n;
这里,变量p保存着n的地址,即p—n,*p—n
2)在使用时,*号表示取指针所指向变量的地址值。
例如:int m = *p;
②如果一个函数需要改变实参的值,则需要使用指针作为函数参数(传址调用),如果函数的参数数据类型很复杂,可使用指针代替。
最常见的就是交换变量函数void swap(int* a, int* b)
③指针运算符*和操作运算符的优先级相同
例如:int m = *p++;
等价于:int m= *p; p++;
2.指针和数组
(1)指针、数组、数组名
如果存在一个数组 int m[3] = {1,2,3};
定义指针变量p,int *p = m(这里m的类型为int*,a[0]==int*)
这里,
其中,m为数组的地址,m为数组0元素的地址,两者相等,但意义不同,例如:
m+1 = (unsigned int)m + sizeof(*m)
m+1= (unsigned int)(m) + sizeof(*m)
=(unsigned int)(m) + sizeof(m)
m+1表示数组的第1号元素,m+1指向数组a的下一个地址,即数组元素“3”之后的地址。
等价操作:
m[i]←→*(m+i)←→*(i+m)←→i[m]←→*(p+i)←→p[i]
实例测试如下:
1 #includestdio.h
2
3 int main()
4 {
5 int m[3] = { 1,2,3 };
6 int *p = m;
7
8 printf(" m = %p\n", m);
9 printf(" m = %p\n", m);
10 printf("\n");
11
12 printf(" m+1 = %p\n", m + 1);
13 printf(" m[2] = %p\n", m[2]);
14 printf(" m+1 = %p\n", m + 1);
15 printf("\n");
16
17 printf(" m[1] = %d\n", m[1]);
18 printf(" *(m+1) = %d\n", *(m + 1));
19 printf(" *(1+m) = %d\n", *(1 + m));
20 printf(" 1[m] = %d\n", 1[m]);
21 printf(" *(p+1) = %d\n", *(p + 1));
22 printf(" p[1] = %d\n", p[1]);
23
24 return 0;
25 }
输出结果为:
(2)数组名注意事项
1)数组名跟数组长度无关;
2)数组名可以看作一个常量指针;所以表达式中数组名只能作为右值使用;
3)在以下情况数组名不能看作常量指针:
- 数组名作为sizeof操作符的参数
-数组名作为运算符的参数
(3)指针和二维数组
一维数组的指针类型是 Type*,二维数组的类型的指针类型是Type*[n]
(4)数组指针和指针数组
①数组指针
1)数组指针是一个指针,用于指向一个对应类型的数组;
2)数组指针的定义方式如下所示:
int (*p)[3] = m;
②指针数组
1)指针数组是一个数组,该数组里每一个元素为一个指针;
2)指针数组的定义方式如下所示:
int* p[5];
3.指针和函数
(1)函数指针
函数的本质是一段内存中的代码,函数的类型有返回类型和参数列表,函数名就是函数代码的起始地址(函数入口地址),通过函数名调用函数,本质为指定具体地址的跳转执行,因此,可定义指针,保存函数入口地址,如下所示:
int funcname(int a, int b);
int(*p)(int a, int b) = funcname;
上式中,函数指针p只能指向类型为int(int,int)的函数
(2)函数指针参数
对于函数int funcname(int a, int b);
普通函数调用int funcname(int, int),只能调用函数int func(int, int)
函数指针调用 intname(*func)(int,int),可以调用任意int(int,int)类型的函数,从而利用相同代码实现不同功能,
实例测试如下,假设有两个相同类型的函数func1和func2:
1 int func1(int a, int b, int c)
2 {
3 return a + b + c;
4 }
5
6 int func2(int a, int b, int c)
7 {
8 return a - b - c;
9 }
普通函数调用和函数指针调用方式及结果如下所示
1 printf("普通函数调用\n");
2 printf("func1 = %d\n", func1(100, 10, 1));
3 printf("func2 = %d\n", func2(100, 10, 1));
4 printf("\n");
5
6 printf("函数指针调用\n");
7 int(*p)(int, int, int) = NULL;
8 p = func1;
9 printf("p = %d\n", p(100, 10, 1));
10 p = func2;
11 printf("p = %d\n", p(100, 10, 1));
12 printf("\n");
需要注意的是,数组作为函数参数的时候,会变为函数指针参数,即:
int funcname( int m[] )——int funcname ( int* m );
调用函数时,传递的是数组名,即
funcname(m);
(3)回调函数
利用函数指针,可以实现一种特殊的调用机制——回调函数。回调函数是指把需要调用的函数指针作为参数传递给另一个函数,这个指针会在适当的时机被用来调用其所指向的函数,该函数指针指向的函数可能有多个,这种机制具有非常大的灵活性。
所谓的回调指的是,上层调用底层,底层又回来调用上层,回调函数的机制:
1)调用者不知道需要调用的具体函数
2)被调用的函数不知道何时会被调用
3)在满足特定条件时,调用者通过函数指针调用被调用的函数
一个简单的回调函数的例子如下所示:
1 #includestdio.h
2
3 int func1(int a, int b)
4 {
5 int num = 2 * a * b;
6 printf("func1的num = %d\n", num);
7 return num;
8 }
9
10 int func2(int a, int b)
11 {
12 int num = 4 * a * b;
13 printf("func2的num = %d\n", num);
14 return num;
15 }
16
17 //回调函数
18 int callback(int(*p)(int, int))
19 {
20 int a = 3, b = 2;
21 return p(a, b);
22 }
23
24 int main()
25 {
26 callback(func1);
27 callback(func2);
28 }
运行结果如图所示:
可以看出,如果不使用函数指针,而使用普通函数调用,则无法使用callback函数来调用func1和func2两个函数。
4.指针和常量
int const* p ; (指针p本身是变量,p指向的数据为常量)
int* const p;(指针p本身是常量,p指向的数据为变量)
const int* p;(指针p本身是变量,p指向的数据为常量)
const int* const p;(指针p和p指向的数据都是常量)
C语言指针知识点总结 相关文章
MySQL知识点
iwehdio的博客园:https://www.cnblogs.com/iwehdio/ 1、SQL MySQL(一)——CRUD语句 查询: select 查询列表 from 表名 select distinct 查询列表 from 表名 select 查询列表 from 表名 where 筛选条件 select 查询列表 from 表名 order by 排序列表 ASC/D
Leetcode 88. 合并两个有序数组 双指针
地址https://leetcode-cn.com/problems/merge-sorted-array/ 给你两个有序整数数组nums1 和 nums2,请你将 nums2 合并到nums1中,使 nums1 成为一个有序数组。初始化nums1 和 nums2 的元素数量分别为m 和 n 。你可以假设nums1 的空间大小等于m + n,这样它就
c语言中录入指定人数成绩、统计成绩分布
1、 #include stdio.h# define NUMBER 80int main(void){ int a[NUMBER]; int number; puts("please input the number of the student."); do { printf("number of student = "); scanf("%d", number); if (number 1 | number NUMBER) { printf("\athe san o
用C语言给NI数据采集卡编程序实现多路数据的同时采集
因为写的上一篇NI数据采集卡的程序有人留言说想要实现多路数据的同时采集,我没有及时回复,深感抱歉,在此写一篇关于NI数据采集卡的多路数据同时采集的程序 第一个程序实现的功能:六路数据同时采集,采集有限个数据,并且保存到txt文档中。采用的是参考单
本周阅读打卡--c++设计新思维-智能指针
新在哪里 只有c++语言有关系,不会涉及其他知识。 适合对象: After reading this chapter, you will be an expert in smart pointer issues such as the following: 阅读后的收益: smart 具备value语义,你可以对放心的其拷贝和复制操作。指针不行 其他函
jvm相关知识
编程语言最终是如何去调用CPU的? 像java、c、c++、python等都属于高级语言,而高级语言是通过调用汇编语言,而汇编语言内部封装了很多操作指令,比如 add sub等,去操作机器指令,像cpu不同的型号或类型都有不同的指令,比如x86,x64是不一样,所以对应的机
C 程序关于时间和日期的操作详解
在平时的 C 语言编程中,经常会遇到关于时间和日期的相关操作。时间日期的操作其实并不难也不复杂,但是作为程序员还是必须要熟练掌握相关操作的,就像对待字符串和内存操作一样。 1. time_t 类型时间 在 C 语言中可以用 time_t 类型表示时间,time_t 类型时
js 冒泡排序
排序前需要了解的知识点:如何交换两个变量的值 交换变量位置案例 var a = 12,b = 54;//使用中间变量方式var temp; //创建个中间变量temptemp = a; //先将 a 的值给temp, 此时temp = 12a = b; //再把 b 的值给到 a,此时 a = 54b = temp; //最后把temp的值给
Electron实用技巧-electron-builder中用户协议(license)的使用及多语言支持
# 1 通常pc软件的安装过程中,会加入用户协议,如: 下面介绍一下使用 electron-builder 打包应用,如何加入license。首先参考官网介绍:windows: nsis[1] ,macOS: dmg[2] # 2 官网上关于license配置说明写的不是很详细,下面是我实践总结出的正确的姿势
Java面试特殊知识点总结 part1
并发与多线程main方法里没有创建新的线程,执行main方法一共需要几个线程? 可以使用 ManagementFactory 的 getThreadMXBean 方法获取 ThreadMXBean 信息,进而获取线程信息进行查看。代码: package Exercise;import java.lang.management.ManagementFactory