概述
C语言—指针的学习心得和理解
文章目录
- C语言—指针的学习心得和理解
- 一、什么是指针
- 二、为什么使用指针、什么情况下使用指针
- 类型一
- 类型二
- 类型三
- 三、如何使用指针
- 四、使用指针要注意的问题
- 五、指针与数组的关系
- 六、指针的运算
- 七、指针与const配合
- 八、什么是二级指针、什么情况下使用
- 九、函数指针
- 十、指针数组与数组指针
一、什么是指针
什么是指针?指针是一种数据的类型(代表地址的整数),使用定义的变量叫指针变量。变量里面存储的就是无符号整数,这些整数代表。指针具有四个基本的要素:1.指针的类型 2.指针指向的类型 3.指针的值 4.指针本身所占据的内存区
二、为什么使用指针、什么情况下使用指针
为什么使用指针、什么情况下使用指针?使用指针的情况大致可以分为三类:
①函数之间无法通过传参共享变量。属于调用者,函数之间的名字空间相互独立是可以重名的,函数之间的数据传递都是值传递。
②函数调用时传参的效率太差,指针可以优化函数之间传参的效率
③堆内存无法与标识符建立联系,只能配合指针使用
类型一
#include<stdio.h>
void func(int a,int b)
{
a=100;b=50;
}
int main()
{
int a=50;int b=100;
func(a,b);
printf("%d %d",a,b);
}
结果 50 100
在以上的代码块中函数之间就无法共享变量(main的a.b和func的a.b是相互独立的)它们变量存放的内存位置并不相同。
#include<stdio.h>
void func(int* a,int* b)
{
*a=100;*b=50;
}
int main()
{
int a=50,b=100;
func(&a,&b);
printf("%d %d",a,b);
}
结果 100 50
以上代码块中传递的是a.b的地址,再对a.b进行解引用修改其中的值。
类型二
#include<stdio.h>
#include<string.h>
typedef struct Stu
{
char name[20];
int age;
char sex;
}Stu;
void intput_stu(Stu* stu)//输入函数
{
stu->age = 10;
strcpy(stu->name,"MYJ");
stu->sex = 'N';
}
void show_stu(Stu* stu)//显示函数
{
printf("%s %d %c",stu->name,stu->age,stu->sex);
}
int main()
{
Stu stu;
intput_stu(&stu);
show_stu(&stu);
}
在以上的的代码块中,函数调用传递的是结构体地址,只需要传递4字节的内容,可以大大提高传参的效率,该传参方式体现在传递数组指针和传递函数指针等方面…
类型三
int *p =NULL;
p = malloc(4);
与堆内存的配合使用
三、如何使用指针
int num=0;
int* p=NULL;
p=#
printf("%dn",*p);
printf("%p",p);
结果 0
0xbface5c8
定义: 类型* 变量名p;
- // * p表示此变量是指针变量,一个*只能定义出一个指针变量,不能连接定义
int *p1,p2,p3 ; // p1是指针,p1,p21是int变量
int *p1, *p1,*p3; //p1,p2,p3都是指针 - int* 表示p是一个int类型的指针变量,一般来说指针的默认值也是不确定的,一般初始化为NULL;
- p=&num 表示p指向的是int类型的变量num的地址,’&'是取num的地址赋值给p。
- *p 表示对p所指向的地址进行解引用。因为指针p也是一种变量,所以他也有内存去储存,%p可以输出存储p的内存的地址。
- 类型表示的是存储的是什么类型的变量的地址,它决定当通过地址访问这块内存时访问的字节数
解引用(根据地址访问内存):*指针变量名
- 根据变量中存储的内存编号区访问内存中的数据
- 如果指针变量中存储的地址出错,此时可能发生段错误(这是赋值产生的错误)
四、使用指针要注意的问题
空指针:指针变量的值为NULL(一般情况下是0,也有特殊情况是1),这种指针变量叫空指针,空指针不能进行解引用(*指针变量 ),NULL被操作系统当作复用指针了(存储了系统重启所需要的数据)当操作系统察觉到程序访问NULL位置的数就会向程序发送错误信号。
空指针还被当做错误标志,如果一个函数的返回值是指针类型,实际返回的值是NULL,则说明函数执行失败或错误;
在C语言代码中应该杜绝对空指针进行解引用,当使用来历不明的指针需要对指针进行判断他是否为空。
if(NULL ==p)
野指针:指针变量的值是不确定的或都是无效的,这种指针叫野指针。
使用野指针不一定会出现问题,但是可能会出现以下问题:
①一切正常 ②段错误 ③脏数据
虽然野指针不一定会出错,但是野指针比空指针的危险更大,野指针是无法判断来,也无法测试出来,也就意味着一旦产生无法避免。
虽然野指针无法判断也无法测试出来,但是所有的野指针都是人为制造出来的最好的方法不生产野指针:
1. 定义指针的变量时没有初始化
2. 获取到局部变量的指针的地址(因为局部变量作用域结束后会被释放,这时指针指向不确定)运算完后需要置空
3. 资源释放后,指向他的指针要及时置空
五、指针与数组的关系
数组名就是一个指针(常指针)
数组名与数组首地址是映射关系 *p=arr是指向关系(&*p是有一个地址的)
由于数组名就是指针,所以数组名可以使用指针的解引用运算符,而指针也可以使用数组。
使用数组当函数的参数时,数组会蜕变成指针,长度也就丢失,因此需要外增加一个参数来传递数组的长度。
#include <stdio.h>
int main()
{
int arr[5] = {1,2,3,4,5};
int* p = arr;
printf("%p %p %pn",arr,&arr,&arr[0]);
for(int i=0; i<2; i++)
{
printf("%d %dn",*(arr+i),p[i]);
}
}
结果:
0xbff54d74 0xbff54d74 0xbff54d74
1 1
2 2
arr、&arr、&arr[0] 三者的地址都是一样的(0xbff54d74),但是三者表示的意义并不相同。
arr表示数组的首个元素的地址arr是个数值指针,&arr表示一个包含了5个int类型元素的数组的数组的首地址
&arr[0]表示数值首个元素的地址是对arr[0]取地址。
如果还分不清&arr和arr可以看以下代码:
#include<stdio.h>
int main()
{
int arr[5] = {1,2,3,4,5};
int *ptr = (int *)(&arr+1);
int result1 = *(arr+1);
int result2 = *(ptr-1);
printf("%d,%dn",result1,result2); // 结果为2,5
printf("%pn%pn",arr+1, &arr+1);
}
结果:
2,5
0xbfd8dc44
0xbfd8dc54
&arr+1 是跳过了整个arr数组的内存指向了下一块相邻的地址(在该代码中是内存向后移动了20个字节,0xbfd8dc40->0xbfd8dc54),*(ptr-1)然后转换为int类型,向前移动4字节(int类型)地址变为0xbfd8dc50该地址里面的值为5.
六、指针的运算
指针的本质是一个int类型的整数,因此此语法上来说整数能使用的运算符他都可以使用,不是所有的运算符对指针运算都是有意义的:
指针 + 整数 <=> 指针 + 宽度(类型) * 整数 向右移动 指向下一个元素的地址
指针 - 整数 <=> 同上
指针 - 指针 <=>(指针-指针)/宽度 =>计算出两个指针之间相隔多少个元素。
七、指针与const配合
const的作用:C语言中一种保护机制,保护对象不能被显性修改
const * p
:保护指针指向的数据,不能通过指针解引用修改内存的值
int const *p
:同上 , int位置无所谓
int * const p
:保护指针变量,指针变量初始化后不能再显式赋值
const int *const p
:指针和值都不能修改
int const * const p
:同上
八、什么是二级指针、什么情况下使用
二维指针:指向指针的指针,它也是个指针变量,但它存储的是指针变量的地址。
float * p = NULL;
定义:float** pp = &p;
使用:*pp <=> p ; **p <=> *p;
什么情况下使用?函数之间共享普通变量使用一级指针,那么同理,共享指针变量时则需要使用二级指针
#include <stdio.h>
#include <stdlib.h>
void create_memory(void** p,int size)
{
*p = malloc(size);
}
int main()
{
int* p = NULL;
create_memory(&p,40);
printf("%pn",p);
for(int i=0; i<10; i++)
{
printf("%d ",p[i]);
}
}
在该代码块中,我前面说过堆内存是无法和标识符建立联系的,所以只能使用指针。该代码就是依靠二级指针来申请内存并回传内存地址的。
九、函数指针
函数指针:
函数就是存储在代码段中的一段数据,当被调用时跳转到那个位置去执行,而函数名就是这段数据的首地址(函数指针),因此函数名就是个指针。
程序员可以自己定义函数指针来指向函数:
1、写出函数的声明
2、为函数名添加小括号
3、修改函数名,并在函数名前加*
函数:int sum(int , int );
函数指针声明:int (*pa) (int , int ); //函数指针变量是pa
定义好函数指针后就可以指向函数了,通过()就可以调用函数,而不用*解引用。
函数指针真正的作用是可以把函数当作参数在函数之间进行传递,然后达到某一效果:多年前写的代码来调用现在所定的代码,这种模式叫回调。
这里可以举一个编辑器函数自带的函数qsort()的例子:
void qsort(void *base, size_t nmemb, size_t size,int(*compar)(const void *, const void *));
功能:快速排序
base
:待排序数组首地址
nmemb
:数组中待排序元素数量
size
:各元素的占用空间大小
compar
:指向函数的指针
#include <stdio.h>
#include <stdlib.h>
int compar(const void* p1,const void* p2)
{
int num1 = *(int*)p1;
int num2 = *(int*)p2;
if(num1 > num2)
return -1;
else if(num1 < num2)
return 1;
else
return 0;
}
int main()
{
int arr[] = {2,5,1,4,6,7,8,3,0,9};
qsort(arr,sizeof(arr)/sizeof(arr[0]),sizeof(arr[0]),compar);
for(int i=0; i<sizeof(arr)/sizeof(arr[0]); i++)
{
printf("%d ",arr[i]);
}
}
下面这个例子可能更为准确:
#include <stdio.h>
#include <stdbool.h>
bool is_prime(int num)
{
printf("我能判断是否是素数...n");
}
int main()
{
printf("%pn",main);
bool (*is_prime_p)(int num);
is_prime_p = is_prime;
is_prime_p(10);
}
十、指针数组与数组指针
指针数组:由指针变量构成的数组 它的实质是一个数组,元素为指针变量
定义:char* arr[5]; // 定义一个长度为5的数组,成员类型是char*
相当于定义的5个 char* 指针变量,char *p1,*p2,*p3,*p4,*p5;
数组指针:专门用来指向数组的指针。 它相当于二维指针的行指针。它的本质是指针,指向数组
定义:int (*p)[10] = NULL;
更多时候用来指向二维数组,当使用二维数组当函数参数时,可以使用数组指针来当形参。
指针数组和数组指针的内存分布:
指针数组例子:
//输入一个数,将其各位相加,转换为相应的中文字符
#include <stdio.h>
int main()
{
char* str[] = {"ling","yi","er","san","si","wu","liu","qi","ba","jiu"};
char num[1000] = {};
gets(num);
int count = 0 , sum = 0 , arr[4] = {};
for(int i=0; num[i]; i++)
{
sum += num[i]-'0';
}
while(sum)
{
arr[count++] = sum % 10;
sum /= 10;
}
for(int i=--count; i>0; i--)
{
printf("%s ",str[arr[i]]);
}
printf("%sn",str[arr[0]]);
}
输入 12345
输出 yi wu
输入 1234567
输出 er ba
最后
以上就是碧蓝毛巾为你收集整理的指针的运用C语言—指针的学习心得和理解的全部内容,希望文章能够帮你解决指针的运用C语言—指针的学习心得和理解所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复