当前位置: 首页 > news >正文

C语言-指针总结

C语言-指针总结

  • 1. 指针简介
    • 1.1间接运算符:*
    • 1.2声明指针
    • 1.3使用指针在函数间通信
  • 2.指针和数组
  • 3.函数、数组和指针
    • 3.1使用指针形参
    • 3.2指针表示法和数组表示法
  • 4. 指针操作

1. 指针简介

从根本上看指针是一个值为内存地址的变量(或数据对象)。
假设一个指针变量名是ptr,可以编写如下语句:

ptr=&pooh;//把pooh的地址赋给ptr

对于这条语句,我们说ptr **“指向”**pooh,需要注意的是ptr是变量,pooh是常量,或者是ptr是可以修改的左值,而&pooh是右值,还可以把ptr指向别处;

1.1间接运算符:*

假设已知ptr指向bash,

ptr = &bash;

然后使用间接运算符 “ * ”找出存储在bash中的值,该运算符也称为解引用运算符。

val = * ptr;//找出ptr指向的值

1.2声明指针

声明指针变量时必须指定指针所指向变量的类型,因为不同的变量类型占用不同的存储空间,一些指针操作要求知道操作对象的大小。另外,程序必须知道存储在指定地址上的数据类型。

int* pi;//pi是指向int类型变量的指针
char* pc;//pc是指向char类型变量的指针
char* pf,pg;//pf,pg是指向float类型变量的指针

类型说明符表明了指针所指向对象的类型,*表明声明的变量是一个指针。
pc指向的值(*pc)是一个char类型。那么,我们思考一下,指针是什么类型的?我们通常描述它的类型是“指向char类型的指针”。pc的值是一个地址,在大部分系统内部,该地址由一个无符号整数表示,但是指针并不是一个整数类型,一些处理整数的操作不能用来处理指针,例如,可以将两个整数相乘,但是不能将两个指针相乘。所以指针是一个新的类型,不是整数类型。

1.3使用指针在函数间通信

现在我们实现一个将两个整数交换的代码。如下:

void Swap(int* u, int* v)
{int temp = *u;*u = *v;*v = temp;
}int main()
{int x = 5, y = 10;printf("交换前:x=%d y=%d\n", x, y);Swap(&x, &y);printf("交换后:x=%d y=%d\n", x, y);return 0;
}

首先看函数调用Swap(&下,&y);

该函数传递的不是x,y的值,而是他们的地址,这表示在Swap函数中的u,v接受的是x,y的地址。将地址作为他们的值。因此应该把他们声明为指针。由于x,y是整数,所以u和v是指向整数的指针;

接下来,在函数体中声明了一个交换值得临时变量:

int temp=*u;

u的值是&x,所以u指向x,这意味着*u可以表示x的值。

总结一下这段代码:
我们需要一个函数交换一下x和y的值。把x,y的地址传给函数,我们让Swap()访问这两个变量。使用指针和*运算符,该函数可以访问存储在这写位置的值并改变他们。

一般而言,我们可以把变量相关的两类信息传递给函数。两种形式的调用,传递x值的是:
fun(x);
如果是下面函数形式调用,那么传递的是地址
fun(&x);
第一种形式的函数调用,函数定义中的形式参数必须是一个与x类型相同的变量
int fun(int num);
第二种形式的函数调用,函数定义中的形式参数必须是一个指向正确类型的指针
int fun(int * x);
如果要计算或处理值,那么使用第一种形式的函数调用;如果要在被调用函数中改变主调函数的变量,则使用第二种形式的函数调用。

2.指针和数组

计算机的硬件指令非常依赖地址,指针在某种程度上把程序员想要传达的指令以更接近机器的方式表达,因此,使用指针的程序更有效率。

我们举一个例子:首先需要知道的是:数组名是数组首元素的地址也就是说,如果arr是一个数组,下面的语句成立:

arr = &arr[0];

arr和&arr[0]都表示数组首元素的内存地址。两者都是常量,不会改变,但是可以把他们赋值给指针变量,然后可以修改指针变量的值。

**指针加1,指针的值递增它所指向类型的大小(以字节为单位)。

dates + 2 == &date[2];
*(dates + 2) == date[2];

以上关系表明了数组和指针的关系非常密切,可以使用指针标识数组的元素和获得元素的值,从本质上看,同一个对象有两种表示法。实际上,C语言标准在描述数组表示法时确实借助了指针。也就是说,定义**ar[n]**的意思是 *(ar+n)。可以认为“到内存的ar位置,然后移动了n个单位,检索储存在那里的值”。

需要注意的是:不要混淆*(dates+2)和 dates+2。间接运算符 ( * )的优先级高于 +,所以dates+2相当于( * dates)+2:

*(dates+2)//表示的是dates第3个元素的值
*  dates + 2//dates第1个元素的值加2
#define MONTHS 12
int main()
{int days[MONTHS] = { 31,28,31,30,31,30,31,31,30,31,30,31 };int index;for (index = 0; index < MONTHS; index++){printf("Month %d has %d Days\n", index + 1, *(days + index));}return 0;
}

这里,days是数组首元素的地址,days+index是元素days[index]的地址,而*(days+index)则是该元素的值,相当于days[index]。

3.函数、数组和指针

假设要编写一个处理数组的函数,该函数返回数组中方所有元素之和,待处理的是名为arr的int类型数组。该如何调用该函数。

total=sun(arr);//可能的函数调用

函数的原型是什么?记住,数组名是该数组首元素的地址,所以实际参数arr是一个储存int类型的地址,应该把它赋值给一个指针类型的参数:

int sum(int*arr);//对应的函数原型

注意:声明数组形参
因为数组名是该数组首元素地址,作为实际参数的数组名要求形式参数是一个与之匹配的指针。
只有在这种情况下,C才会把 int arr[ ] 和 int *arr解释成一样的,所以下面4种原型都是等价的:

int sum(int* arr, int n);
int sum(int* , int n);
int sum(int arr[], int n);
int sum(int [], int n);

求数组之和:

int sum(int arr[], int sz)
{int i = 0;int sum = 0;for (i = 0; i < sz; i++){sum += arr[i];}return sum;
}int main()
{int arr[] = { 20,10,5,39,4,16,19,26,31,20 };long answer;answer = sum(arr, 10);printf("%ld", answer);return 0;
}

3.1使用指针形参

函数要处理数组必须知道何时开始、何时结束。sum()函数使用一个指针形参表示数组开始,用一个整型形参表明待处理数组的元素个数(指针形参也表明了数组中的数据类型)。

int sum(int* arr, int* sz)
{int i = 0;int sum = 0;for (i = 0; i < *sz; i++){sum += *arr;//把数组元素的值加起来arr++;//让指针指向下一个元素}return sum;
}int main()
{int arr[] = { 20,10,5,39,4,16,19,26,31,20 };long answer;int sz = 10;answer = sum(arr,&sz );printf("%ld", answer);return 0;
}

可以把sum中的循环体压缩成一行代码:

sum + = *arr++;

一元运算符*和++的优先级相同,但结合律是从右往左,所以arr++先求值,然后才是 *arr。也就是说,指针arr先递增后指向。

使用后置++形式意味着先把指针指向位置上的值加到sum上,然后再递增指针。如果使用*++arr,顺序则反过来,先递增指针,再使用指针指向位置上的值。如果使用( * start)++,则先使用start指向的值,再递增该值,而不是递增指针。

3.2指针表示法和数组表示法

从以上分析可知,处理数组的函数实际上用指针作为参数,但是在编写这样的函数时,可以选择是使用数组表示还是指针表示法。

至于C语言,arr[ i ]和*( arr + i )这两个表达式都是等价的。无论arr是数组名还是指针变量,这两个表达式都没有问题。但是,只有当arr是指针变量时,才能使用arr++这样的表达式。

指针表示法(尤其与递增运算符一起使用时)更接近机器语言,因此一些编译器在编译时能生成效率更高的代码。

4. 指针操作

可以对指针进行哪些操作?C语言提供了一些基本的操作,下面程序提供了8种不同的操作。

int main()
{int arr[5] = { 100,200,300,400,500 };int* p1, * p2, * p3;p1 = arr;p2 = &arr[2];printf("p1 = %p , *p1 = %d , &p1 = %p\n", p1, *p1, &p1);//指针加法p3 = p1 + 4;printf("p1 + 4 = %p , *(p1 + 4) = %d\n", p1+4, *(p1+4));//递增指针p1++;printf("p1 = %p , *p1 = %d , &p1 = %p\n", p1, *p1, &p1);//递减指针p2--;printf("p2 = %p , *p2 = %d , &p2 = %p\n", p2, *p2, &p2);--p1;//回复为初始值++p2;//回复为初始值printf("p1 = %p , p2 = %p\n", p1, p2);//一个指针减去另一个指针printf("p2 = %p , p1 = %p , p2 - p1 = %td\n", p2, p1, p2 - p1);//一个指针减去一个整数printf("p3 = %p , p3 - 2 = %p\n", p3, p3 - 2);return 0;
}

在这里插入图片描述
在这里插入图片描述

赋值:可以把地址赋给指针。注意,地址应该和指针类型兼容

解引用:*运算符给出指针指向地址上储存的值

取值:和所有变量一样,指针变量也有自己的地址和值。对指针而言,&运算符给出指针本身的地址。

指针与整数相加:可以使用+运算符把指针与整数相加,或整数与指针相加。无论哪种情况,整数都会和指针所指向类型的带下(以字节为单位)相乘,然后把结果与初始地址相加。因此p1+4与&arr【4】等价。

递增指针:递增指向数组元素的指针可以让该指针移动至数组的下一个元素。

指针求差:可以计算两个指针的差值。

比较:使用关系运算符可以比较两个指针的值,前提是两个指针都指向相同类型的对象。

使用指针时一定要注意,不要解引用未初始化的指针!

http://www.dtcms.com/a/446600.html

相关文章:

  • 非小米电脑安装电脑管家实现互联互通
  • 怎样在网站上做外贸php网站开发环境说明
  • PostgreSQL 单库备份
  • 阳春做网站已有网站开发app客户端
  • 版面设计图大全长沙如何优化排名
  • 网站验证码文件厦门建设银行网站
  • 力扣hot100做题整理(31-40)
  • Oracle OCP认证考试题目详解082系列第54题
  • 淘宝客网站搜索怎么做湖南优化电商服务有限公司
  • 安阳市建设工程领域网站图片 移动网站开发
  • 掌握 Mock 的艺术:用 unittest.mock 优雅隔离外部依赖的单元测试实战指南
  • 哪些网站可以做兼职设计西安建设和住房保障局网站
  • 松江佘山网站建设南县网站设计
  • 网站备案号密码找回天水市住房和城乡建设局网站
  • php 8.4.6 更新日志
  • Linux处理停止信号相关函数的实现
  • 【学习笔记】Redis数据库设计与实现研究综述
  • 一键整合,万用万灵,Python3.11项目嵌入式一键整合包的制作(Embed)
  • 上海做网站品牌公司wordpress删除用户头像
  • 静态网站素材网站的尾页要怎么做
  • 有关房地产开发建设的网站ps制作博客网站界面
  • 蒙阴网站优化做俄罗斯外贸网站
  • 绍兴市住房和城乡建设局网站专业网站建设机构
  • 拼多多前端面试题及参考答案(上)
  • 为食堂写个网站建设南宁建站
  • 使用Java连接redis以及开放redis端口的问题
  • Git应用详解:从入门到精通
  • 【Linux】 Ubuntu 开发环境极速搭建
  • asp学习网站网站由哪三部分组成
  • 新增网站备案时间郑州怎么做外贸公司网站