c++指针总结(易混淆)

一:指针的概念
指针是一个特殊的变量,里面存储的数值是内存里的一个地址。学好指针,重要的是搞清楚指针的四个方面的内容:指针的类型、指针所指向的类型、指针所指向的内存区、指针本身占据的内存区。
1、 如何判断指针的类型呢:
1
2
3
4
5
int *p;
char *p;
int **p;
int (*p)[3];
int *(*p)[4];
上面的五个声明,指针的类型分别是:int *、char *、int ** 、 int (*)[3]、int *(*)[4];是不是非常简单,就是把变量名去掉,剩下的就是指针的类型。
2、指针所指向的类型
1
2
3
4
5
int *p;
char *p;
int **p;
int (*p)[3];
int *(*p)[4];
上面五个声明,指针所指向的类型分别是:int\char \int *\int()[3]\int *()[4],找出规律了吗?就是把变量名和一个*去掉,剩下的就是指针所指向的类型。特别强调:指针的类型和指针所指向的类型是不同的概念。
3、指针所指向的内存区或者指针的值
指针的值是指针本身所存储的数值,这个值被编译器当作一个地址,而不是而不是一个一般的数值。在32位程序里,所有的类型的指针的值都是一个32位数,因为32位程序中内存地址全部都是32位字长。
指针所指向的内存区就是从指针的值所代表的内存区开始,长度为sizeof(指针所指向的类型)的一篇内存区。以后,我们说一个指针的值是XX,就代表了该指针指向了以XX为首地址的一片内存区域;我们说一个指针指向了某个内存区域,相当于指针的值就是该内存区域的首地址。
指针指向谁,就把谁的地址赋给指针。
4、指针本身所占据的内存区
指针本身占据了多大的内存。用sizeof(指针的类型)测试一个就知道了。在32位平台里,指针本身占据了四个字节的长度。
二、指针的算术运算
1
2
3
char a[20];
int *p=a;
p++;
指针p的类型是 int * ,指针所指向的类型是int ,它被初始化位指向整形变量a,接下来指针p被加了1,编译器是这样处理的:它把指针p的值加上了sizeof(int),在32位程序里,是加上了4.由于地址是用字节做单位的,故p的地址由原来的a的地址指向了加四个字节后的高地址。
由于char类型的长度是一个字节,所以原来p是指向数组从0开始的四个字节,现在指向了从第四个字节开始的四个字节
我们可以用一个指针和一个循环来遍历一个数组:
1
2
3
4
5
6
7
8
int arr[20];
int *p=arr;
.......//此处略去整数型赋值的代码
for(i = 0; i<20 ; i++)
{
(*p)++;
P++;        
}
这个例子将整数型数组各个单元的值加1,由于每次循环都将指针p,所以每次循环都能访问数组的下一个单元。
1
2
3
4
5
char a[20];
int *p=a;
...
...
p+=5;
在这个例子中,p被加上了5,从编译器的角度看:将指针p的值加上sizeof(int),在32位恒旭中就是加上了5*4=20.由于地址的单位十字街,故现在p指向刚开始高字节方向的20个字节。在这个例子中,没加5之前的p指向数组第0个单元开始的四个字节,加5后,p已经指向数组的合法范围之外了。虽然这种情况在应用上会出现问题,但在语法上是可以。这体现了指针的灵活性。
总结一下:一个指针,加上一个整数n后,结果是一个新的指针pnew,pnew的类型和pnew所指向的类型与pold的类型都是一样的。pnew的值比起pold的值增加了n*sizeof(p指向的类型)个字节。加是往高字节移动,减是往低字节移动。
三运算符&和*
&是取地址运算符,*在书上叫做间接运算符。&a的运算结果是一个指针,指针的类型是a的类型加一个*。指针所指向的类型是a的类型,指针所指向的地址就是a的地址。*p的结果是p所指向的结果,类型是p的类型,占用的地址就是p所指向的地址
1
2
3
4
5
6
7
8
9
10
11
12
int a =12;
int b;
int *p;
int *ptr;
p=&a//&a的结果是一个指针,指针的类型是一个int *,指针所指向的数据类型是int ,指向的地址是a的地址。
*p=24;//*p的结果,它的类型是int,它所占用的地址是p所指向的地址,显然*p就是变量a
*ptr=&P;//p本身就是一个指针了,对p在取地址,那么 该指针的类型是int **,指针所知下个的类型是 int * 。指针所指向的地址就是p的地址
**ptr=34;// *ptr的结果就是ptr所指向的东西。在这里是一个指针,对这个指针在做一次*运算,。结果就是一个int 类型的变量
四指针表达式
一个表达式的最后结果如果是一个指针,这个表达式就叫做指针表达式
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int a ,b ;
int array[10];
int *pa;
pa=&a;//指针表达式
int **ptr = &pa;//表达式
*ptr=&b;//表达式
pa=array;
pa++;//表达式
char arr[20];
char **parr =arr//如果把arr看作指针的话,arr也是指针表达式
char *str;
str=*parr;//指针表达式
str=*(parr+1);//指针表达式
由于指针表达式的结果是一个指针,所以指针表达式也需要具有指针的四个要素:指针的类型,指针所指向的类型、指针指向的内存区、指针自身占据的内存区。
指针在左边表示赋值,指针在右边表示取值。
五、数组和指针的关系
1
2
3
4
5
6
7
8
int array[10] = {0,1,2,3,4,5,6,7,8,9},value;
...
...
value = array[0];//也可以写成 value = *array;
value = array[3];//也可以写成 value = *(array+3);
value = array[4];//也可以写成 value = *(array+4);
上例中,一般而言,数组名array代表数组本身,类型是int[10];如果把array看作指针的话,它指向数组的第0个单元,类型是int *,所指向的类型是数组单元的类型 int ,因此*array = 0 就一点也不奇怪了。同理,(array+3)指向数组单元的第三个指针,所以*(array+3) = 3 ;
1
2
3
4
5
6
7
8
9
char *str[3] = {
"Hellp,this is a sample",
"Hi ,good morning ",
"Hello world"
};
char s[80];
strcpy(s,str[0]);//也可以写成 strcpy(s,*str);
strcpy(s,str[1]);//也可以写成strcpy(s,*(str+1));
上例中,str是一个三单元的数组,该数组的每个单元都是一个指针,这些指针每个都指向一个字符串,如果把数组名看作指针的话,它指向数组的第0号元素,类型是char** ,指向的类型是 char *;
*str 也是一个指针,它的类型是char*,指向的类型是char ,它指向的地址是字符串 “Hello,this is a sample”的第一个字符H的地址。*(str+1)也是一个指针,他的类型是char*,指向的类型是char,指向“Hi,good morning”的第一个字符H的地址。
在不同的表达式中,数组名array可以扮演不同的角色 。在表达式sizeof(array)数组名array 代表数组本身,故测出的是整个数组的大小。在表达式*array中,array扮演的是一个指针,所以该表达式就是数组第0号单元的值。sizeof(*array)测出的是数组单元的大小。表达式array+n,array扮演的是指针,故array+n也是指针,他的类型是type*,指向的类型是type,指向数组第n号单元,故sizeof(array+n)测出的是指针类型的大小。
六、指针和结构类型的关系
1
2
3
4
5
6
7
8
9
10
11
struct mystruct
{
int a ;
int b;
int c;          
}
mystruct ss = {20,30,40};
mystruct *ptr = &ss;//声明了一个指向结构对象ss的指针,它的类型是myStruct * ,指向的类型是mystruct
int *pstr = (int *)&ss//声明了一个指向结构对象ss的指针。但是他的类型和它指向的类型和ptr是不同的,请问如何通过指针ptr来访问ss的三个成员变量
答案:
1
2
3
4
5
6
7
8
9
10
ptr ->a;
ptr ->b;
ptr ->c;
请问如何通过指针pstr来访问ss的三个成员变量
答案
*pstr;
*(pstr+1);
*(pstr+2);
这样使用pstr来访问结构成员是不正规的。那么该怎么样来通过指针访问数组的各个单元呢?
1
2
3
4
5
6
int array[3]={35,56,37};
int *pa = array;
//通过指针pa访问数组array的三个单元的方法是:
*pa;
*(pa+1);
*(pa+2);
从格式上看倒是与通过指针访问结构成员的不正规方法的格式一样.所有的C/C++编译器在排列数组的单元时,总是把各个数组单元存放在连续的存储区里,单元和单元之间没有空隙。但在存放结构对象的各个成员时,在某种编译环境下,可能会需要字对齐或双字对齐或者是别的什么对齐,需要在相邻两个成员之间加若干个“填充字节”,这就导致各个成员之间可能会有若干个字节的空隙。所以,在例十二中,即使*pstr访问到了结构对象ss的第一个成员变量a,也不能保证*(pstr+1)就一定能访问到结构成员b。因为成员a和成员b之间可能会有若干填充字节,说不定*(pstr+1)就正好访问到了这些填充字节呢。这也证明了指针的灵活性。要是你的目的就是想看看各个结构成员之间到底有没有填充字节,嘿,这倒是个不错的方法。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/382146.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

114. 二叉树展开为链表 golang

114. 二叉树展开为链表 给定一个二叉树&#xff0c;原地将它展开为链表。 例如&#xff0c;给定二叉树 1/ \2 5/ \ \ 3 4 6将其展开为&#xff1a; 1\2\3\4\5\6Code /*** Definition for a binary tree node.* type TreeNode struct {* Val int* Left *Tre…

十戒

用音乐代替游戏&#xff0c;用读书代替上网&#xff0c;用欣赏电影代替电视剧&#xff0c;用思考代替发呆&#xff0c;用行动代替牢骚&#xff0c;用远足代替安逸享受&#xff0c;用写作代替恋爱&#xff0c;用晨读代替晚起&#xff0c;用高质量睡眠代替熬夜&#xff0c;用活跃…

112. 路径总和 golang

112. 路径总和 给定一个二叉树和一个目标和&#xff0c;判断该树中是否存在根节点到叶子节点的路径&#xff0c;这条路径上所有节点值相加等于目标和。 说明: 叶子节点是指没有子节点的节点。 示例: 给定如下二叉树&#xff0c;以及目标和 sum 22&#xff0c; 5/ \4 8/ …

迷途

? 好不容易熬到了周末&#xff0c;却又不知道该做些什么&#xff0c;一转眼&#xff0c;发现什么都没有做又过去了&#xff0c;其实人又总是不这样&#xff0c;总是渴望得到&#xff0c;得到了却没了下一步的计划&#xff0c;这种感觉跟你爬到山顶却索然无味的感觉一样&am…

113. 路径总和 II golang

113. 路径总和 II 给定一个二叉树和一个目标和&#xff0c;找到所有从根节点到叶子节点路径总和等于给定目标和的路径。 说明: 叶子节点是指没有子节点的节点。 示例: 给定如下二叉树&#xff0c;以及目标和 sum 22&#xff0c; 5/ \4 8/ / \11 13 4/ \ / \7 …

二项式系数的递归算法

??写递归函数时&#xff0c;偶然想起来二项式系数的递归写法。废话不说&#xff0c;上代码。code:#include #include using namespace std;int er(int n,int k){if(k0||kn)return 1;elsereturn (er(n-1,k-1)er(n-1,k))%2;}int main(){int a,b;while (cin>>a>>b)c…

222. 完全二叉树的节点个数 golang

222. 完全二叉树的节点个数 给出一个完全二叉树&#xff0c;求出该树的节点个数。 说明&#xff1a; 完全二叉树的定义如下&#xff1a;在完全二叉树中&#xff0c;除了最底层节点可能没填满外&#xff0c;其余每层节点数都达到最大值&#xff0c;并且最下面一层的节点都集中…

刚刚有水了一道,用递归实现进制转换

code :#include <iostream>using namespace std;int a[101],i0;void jinzhi (int n){if (n1)a[i]1;else {a[i]n%2;jinzhi(n/2);}}int main (){int k,j;cin>>k;jinzhi(k);for (int ji-1;j>0;j--)cout<<a[j];}其实原理都是一样&#xff0c;无非是换成方法而…

958. 二叉树的完全性检验 golang

958. 二叉树的完全性检验 给定一个二叉树&#xff0c;确定它是否是一个完全二叉树。 百度百科中对完全二叉树的定义如下&#xff1a; 若设二叉树的深度为 h&#xff0c;除第 h 层外&#xff0c;其它各层 (1&#xff5e;h-1) 的结点数都达到最大个数&#xff0c;第 h 层所有的…

dp问题:采药

今天把采药A了&#xff0c;属于dp问题&#xff0c;主要思路是把所有时间都存进一个数组中&#xff0c;数组的值对应药的价值&#xff0c;下标是时间&#xff0c;然后记忆化搜索&#xff0c;碰到价值高的就赋值&#xff0c;相比于摘花生&#xff0c;辰辰是一个聪明的猴子&#x…

662. 二叉树最大宽度 golang

662. 二叉树最大宽度 给定一个二叉树&#xff0c;编写一个函数来获取这个树的最大宽度。树的宽度是所有层中的最大宽度。这个二叉树与满二叉树&#xff08;full binary tree&#xff09;结构相同&#xff0c;但一些节点为空。 每一层的宽度被定义为两个端点&#xff08;该层最…

传值的一点认识

今天又把C课本翻了一遍&#xff0c;发现了好多以前没彻底弄清楚的问题&#xff0c;比如说传值。说起传值&#xff0c;最先想到的恐怕是赋值&#xff0c;他是最基本的传值方式&#xff0c;不过有时候我们希望用另外的方式来传值。传值一共有三种方式&#xff0c;第一种是赋值&am…

递归经典例子

程序调用自身的编程技巧称为递归&#xff08; recursion&#xff09;。 一个过程或函数在其定义或说明中又直接或间接调用自身的一种方法&#xff0c;它通常把一个大型复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解&#xff0c;递归策略只需少量的程序就可描述出…

golang中的栈(LeetCode刷题)

栈的模拟&#xff08;LeetCode刷题用法&#xff09; func main() {stack : make([]string, 0)stack append(stack, "1" )stack append(stack, "2" )stack append(stack, "3" )fmt.Println(stack)stack stack[:len(stack)-1]fmt.Println(sta…

739. 每日温度 golang (list实现)

739. 每日温度 根据每日 气温 列表&#xff0c;请重新生成一个列表&#xff0c;对应位置的输出是需要再等待多久温度才会升高超过该日的天数。如果之后都不会升高&#xff0c;请在该位置用 0 来代替。 例如&#xff0c;给定一个列表 temperatures [73, 74, 75, 71, 69, 72, …

openjudge基础题3计算书费

总时间限制: 1000ms内存限制: 65536kB描述下面是一个图书的单价表&#xff1a;计算概论 28.9 元/本数据结构与算法 32.7 元/本数字逻辑 45.6元/本C程序设计教程 78 元/本人工智能 35 元/本计算机体系结构 86.2 元/本编译原理 27.8元/本操作系统 43 元/本计算机网络 56 元/本JAV…

739. 每日温度 golang

739. 每日温度 根据每日 气温 列表&#xff0c;请重新生成一个列表&#xff0c;对应位置的输出是需要再等待多久温度才会升高超过该日的天数。如果之后都不会升高&#xff0c;请在该位置用 0 来代替。 例如&#xff0c;给定一个列表 temperatures [73, 74, 75, 71, 69, 72, …

查找和排序的一点浅显认识

以前老是混淆各种排序的方法&#xff0c;对此我也感到十分苦恼&#xff0c;去网上搜索各种排序教程&#xff0c;结果收获颇微&#xff0c;就在期末考试时&#xff0c;我还担心会有排序的题&#xff08;事实证明我多虑了&#xff09;&#xff0c;不过作为算法的基本功&#xff0…

581. 最短无序连续子数组 golang

581. 最短无序连续子数组 给定一个整数数组&#xff0c;你需要寻找一个连续的子数组&#xff0c;如果对这个子数组进行升序排序&#xff0c;那么整个数组都会变为升序排序。 你找到的子数组应是最短的&#xff0c;请输出它的长度。 示例 1: 输入: [2, 6, 4, 8, 10, 9, 15] …

引用 看图英语作文万能模板

来源&#xff1a;新东方 2007-7-27 纵观历年的考研作文题&#xff0c;均为图画作文&#xff0c;鲜有图表作文&#xff0c;而现在考研作文的要求采用八股文的模式——命题者已经告诉你要去写什么、怎么写甚至于顺序都已经安排 好&#xff01;那么针对这么一个固化的考试模…