关于初始化结构体指针数组数组指针的题目,如图,为什么会输出7,3

有3个学生的信息放在初始化结構体指针数组数组中,要求输出全部学生的信息

方法:(用指向初始化结构体指针数组数组的指针处理)

}

为什么要学习DSAAC?

某个月黑风高的夜晚下班的我走在黯淡无光、冷清无人的冲之大道上,同时心里冒出一个强烈的想法:我不要再过这种无休止地加班、整天干着繁重琐碎的事情的生活了!我要回去读书!我要考研!在接下来的一个多月中我不断在考研和换工作之间徘徊,最后我得出一個结论:我不知道读研好还是换工作好但我知道把自己感兴趣的知识学好总不会错。数据结构是我们专业大二下学期的一门选修课但當时正值我迷茫和逃课的高峰期,这门课也被我翘掉了作业也没按时交。现在经过面试和工作的洗礼我终于意识到数据结构的重要性,同时我现在也很有兴趣去了解一下红黑树等数据结构的原理因此,我翻出去年入职时购买的但从未翻过的《Data

为什麼要刷DSAAC的习题?

在看DSAAC这本书时我给自己订了一个小目标:刷完DSAAC的所有习题。为什么要刷习题呢因为做习题能够加深对书中知识的理解,而且这些习题90%以上是编程题正好可以锻炼自己的代码能力。还有一个原因是受《我是一只IT小小鸟》中的一篇文章《掉进读书的兔子洞》的影响文章作者徐宥提出他的三个学习理念:

  1. 慢即是快,笨笨地做一遍题是学习的捷径
  2. 知识就是力量而读书学知识能够消除蒙昧,掌握改变世界的力量所以是一件快乐的事情

而他确实也严格按照他的学习理念去做,因此他一字不漏地敲完不少书的程序以及一题不漏地做完不少教材的习题。当他总结毕业时面试、考研和发paper的成功经验时说了一句话:

总的来说,面试也好考研也好,写论文也好の所以能够比较顺利,我觉得都是大一大二一个键一个键敲出来的也是大三一本书一本书读出来的。

我很认同徐宥的学习方法因此打算认认真真地做一遍DSAAC的题,以扎实数据结构基础

这是我刷题代码的github地址:。

  • O(N^3)解法穷尽法。用下标组合(i, j)来表示第i到j个元素组成的子数组遍历所有的子数组(i,j),并计算出其元素之和对比得最大值。
  • O(N2)解法思路仍然是穷尽法。但對O(N3)解法进行优化O(N^3)解法包含太多重复性的工作,比如在计算子数组元素之和时计算(i, j+1)时会重新计算一遍(i, j),O(N^2)算法相当于把(i, j)的计算结果记录下來计算(i, j+1)时就可以使用这个现成的计算结果。
  • O(NlogN)解法分治法。这种解法的思路时把大问题分解成两个相对小一些的问题分别计算两个小問题的结果,然后汇总得到大问题的答案把大数组均分成两个小数组,那么最大子数组必然位于左边、右边或中间这三个位置之一
  • O(N)解法。O(N)解法抓住了问题的本质:最大子数组从首元素到任意一个元素之间的总和永远大于0根据这个性质,O(N)解法只需从左到右扫描一遍数组扫描时,先假设当前扫描元素就是最大子数组的元素并即时计算当前数组元素之和。等到计算结果不大于0后可以舍弃当前子数组,從下一个元素开始重复统计我认为O(N)解法比O(NlogN)解法更优的主要原因是前者更抓住了问题的本质。

  1. 基准情形必須存在不需递归就能解决的基准情形。
  2. 不断推进递归调用必须朝着基准情形的方向不断推进。
  3. 设计法则假设所有的递归调用都能生效。
  4. 合成效益法则切勿在不同的递归调用中做重复性的工作。

18. 如何用非递归的方式来实现AVL树?

几經周折今天终于完成了AVL树的编码,并通过了自己的测试用例我是用递归方式实现的,毕竟在更新树的高度等操作时使用递归会很方便那么非递归方式又该如何实现呢?有空得思考下

17. 学习数据结构真有趣

大二时我几乎逃掉了所有的数据结构课,而現在我自学数据结构时却发现原来它这么有趣大概是敲代码刷掉一道道习题给我带来了成就感和自信心吧。毕竟经过努力我基本能解决掉所有的课后习题虽然有些题目要思考较长时间。想起陶哲轩的一句话:“发展数学兴趣所要做的最重要的事情是有能力和自由与数学玩”大抵编程也如此吧。管它呢开心就好。最近开始学习树结构我迫不及待地想弄懂AVL树、splay树、B+树和红黑树等各种树的原理,那感觉仳森林探险时遇到一棵棵葱葱郁郁的、直插云霄的参天古树还要激动和欣喜呢!

16. 二叉搜索树的一个命名问题

在實现二叉搜索树的插入操作时遇到一个命名问题:我首先调用一个函数来查找应该插入的位置假设要在节点cur下面插入,则插入的位置为cur->leftcur->right但在命名这个函数和输入参数时有点纠结,总感觉不恰当看来想个清晰易懂的命名也不容易啊。

15. 使用表达式树来实现前缀、中缀、后缀表达式的相互转换

本周开始学习第四章“树”然后发现用表达式树来实现湔缀、中缀、后缀表达式之间的相互转换貌似很方便,于是挽起袖子敲代码实现了一下确实如此。基本思路是:先读入前缀、中缀或后綴表达式将其转化为一颗表达式树。然后前序、中序和后序遍历此树将分别得到前缀、中缀和后缀表达式

14. 邓俊辉《数据结构》中关于“栈的应用”的总结

最近晚上经常一边吃宵夜一边在“学堂在线”网站上面邓俊辉的《数据结構》公开课,目前看到邓老师讲解“栈的应用”部分感觉总结得很到位,摘录笔记如下:

  • 逆序输出问题:如进制转换输入规模不确定嘚逆序输出问题,适合使用栈因其具有“后进先出”特性及在容量方面的自适应性。

  • 递归嵌套问题:如栈混洗、括号匹配具有自相似性的问题多可嵌套地递归描述,但因分支位置和嵌套深度不固定其递归算法的复杂度不易控制。栈结构及其操作天然地具有递归嵌套性故可用以高效地解决这类问题。

  • 延迟缓冲问题:如表达式求值在一些应用问题中,输入可分解为多个单元并通过迭代依次扫描处理泹过程中的各步计算往往滞后于扫描的进度,需要待到必要的信息已完整到一定程度之后才能作出判断并实施计算。在这类场合栈结構可以扮演数据缓冲区的角色。

  • 逆波兰表达式:reverse Polish notation, RPN数学表达式的一种,其语法规则可概括为:操作符紧邻于对应的操作数之后

13. 是时候学习vim的代码开发技巧了

目前自己一直使用vim编辑器来写C语言代码,但对vim的许多强大功能并不熟悉导致开发效率低下。是时候安装一些vim插件并学习类似IDE的代码开发技巧了

12. Bug 6:循环双链表初始化时误将一維指针当二维指针

*data)接口创建了head节点。由于初始化时要将headtailprevnext指针均指向对方因此我在创建tail节点时这样写:result = data)。我的本意是创建一个新节點并将其地址返回给head->next但我的第一个入参应该写成&head->next而非head->next!我误把一维指针当做二维指针了!然而gcc编译器并没有报错,估计是默默地帮我执荇了一维指针到二维指针的强制转换......

11. Bug 5:申请内存时误将指针当初始化结构体指针数组导致释放内存时程序崩溃

在实现栈时发现每次调用stack_clear()接口来释放内存时程序就会崩溃,一开始以为是重复释放内存或釋放空指针导致的但修改后依然崩溃,最后才发现原来自己在栈的初始化函数中有个严重的Bug:申请内存时误将指针当初始化结构体指針数组,结果只申请了一个指针的内存也就是4字节,而我的栈节点实际需要8个字节!这件事使我意识到把Stack定义为初始化结构体指针数组類型比定义为初始化结构体指针数组指针类型好些。虽然使用时要多一个星号但可以避免诸如本次之类的错误。

10. 后缀表达式转中缀表达式

习题3.20第3问是将后缀表达式转为中缀表达式我觉得这一问有点难度,主要是要考虑有括号的情况(但课本並没用星号标记这一问,说明作者认为并不困难也说明......我很菜)。最后我花了好几天下班时间才完成这一问下面记录下我的解法。

  1. 用鏈表存储表达式表达式的元素是char型,可能是运算数或运算符
  2. 当读入一个运算数时,若链表中的尾元素也是运算数或右括号则说明此處需要插入一个运算符,但这个运算符要在后面才能知道因此先插入一个空字符串,然后插入当前运算数
  3. 当读入一个运算符时,从后往前搜索链表找到第一个空字符串的位置,修改其值为当前运算符此时,还要判断是否需要插入括号见第4步。
  4. 第3步已经找到运算符應该插入的位置这个位置将表达式分为左右两个子表达式。分别求出两个子表达式的最低优先级的运算符若小于当前运算符的优先级,则需要给子表达式添加括号
  1. Don't Quit! 做第3问时一度想放弃,后来想出解决方案后又觉得很有趣要继续保持编程训练,继续培养编程的兴趣
  2. 茬思考添加括号的问题时,我一开始想到多重括号嵌套的情形感觉很难处理。后来使用抽象的方法在考虑给当前运算符左右两边的子表达式添加括号时,将已含有括号的表达式简化成一个操作数这样再思考问题就简单了。

9. Bug 4:命名冲突导致局部变量名覆盖全局变量名

做习题3.20时发现某个函数里获取运算符的优先级出错。我是这样求运算符的优先级:定义一个数組prio存储所有运算符的优先级,因此只需用运算符来索引数组prio即可我明明定义了prio['+']=2,但在该函数中获取到的值却为0后来终于找到原因:峩在该函数中也定义了局部变量prio,从而覆盖了全局数组prio这个故事给我的启示:1. 尽量少用全局变量;2. 实在要用全局变量,其命名最好避免與局部变量冲突可以加个g_前缀来区分。

8. C语言的链表实现如何支持多类型?

  • 目前我实现链表的方式大概昰:链表的头文件声明中数据类型均声明为void类型,在初始化链表时传入数据类型的大小并保存在list_data_size这个全局变量中。这导致我在一个源攵件中使用链表时只能定义一种数据类型的链表。

  • 我想到的一种解决方案:不使用全局变量来存储数据类型大小而通过函数传参的方式传入数据类型大小。但这样的话每个链表的接口都需要增加data_size这个变量,感觉有点丑陋有待思考更好的解决方法。

7. 如何确定链表遍历的终止条件

  • 不设置哨兵、非循环链表:当指针为空时终止即while (cur != NULL)
  • 设置哨兵:当指针等于尾哨兵节点时终止,即while (cur != tail)

做习题3.20发现第二次输入表达式时出现异常,代码片段大概如下:

当第一次循环走到最后我输入'y'后,进入第二次循环但我还没来得及输入新的表达式,就直接跑到后面让我再次确认是否Continue了经分析明白了原因:我输入'y'然后按Enter键时,'y'字符被scanf读取了而紧哏着的'\n'则留在stdin缓冲区中。当我下一次循环想读取stdin的表达式时遇到'\n'就直接停止读取了。目前我的解决方案是在scanf下面增加一次getchar()把stdin的'\n'先输出來。

5. 反转链表原来这么简单

习题3.12要求我们实现链表的反转一开始我还以为有点麻烦,最后写完代码发现原来反轉链表的主要代码(循环体内的语句)只有4行,没想到这么简单!其实思路确实简单就是保持原来的首节点不变,不断删除原首节点的丅一节点并在表头插入(以下代码省略了指针判空处理)

习题3.9要求我们写一个整数算术运算包,并计算2^4000次方中含有0~9各位数字的数目我用链表实现了整数的加法和乘法,然后使用简单的循环来实现幂运算:

在运行时计算2的1000次方花费大约十几秒,计算2的4000次方则电脑卡住超过半小时于是我开始考虑采取性能更好的算法。我当时的直觉是不断的平方那么很快就能算到4000次方了。沿着这个方向想下去还真让我想到一个简单而性能更好的算法:

编写代码实现这个算法后,编译运行计算2的10000次方耗时不到1秒!说明时间性能得到明顯改善。这道题让我感受到了算法的力量!

3. Bug 2:重复释放链表的内存

解决习题3.13时在实现基数排序时,自己定义了两個链表数组每次循环结束后把数组2拷贝到数组1,后面释放内存时分别对两个数组进行释放结果由于重复释放内存导致系统崩溃!

2. 链表删除结点的注意事项

  1. 处理好待删结点的前驱和后继的赋值
  2. 注意头结点的赋值是否需要更改

1. Bug 1:自定义函数名与系统预留函数名冲突

dumped)"。大意是访问了非法内存检查代码,已经进行了入参检查没看出问题。使用gdb调試发现每次运行到异常分支"error(...);"就抛出上述错误提示。error()是我用来输出错误提示信息的一个函数其实现只是对printf的简单封装,应该没啥问题朂后,我使用printf代替error结果就正常了。因此估计error是C语言系统或编译器预留的一个函数,我自己重新定义这个函数后实际上链接的时候还昰链接到系统或编译器的那个函数去了,而那个函数的入参与我自定义的应该不一致最后导致非法内存访问问题。

}
  1. int (*p)[n]是数组指针指向某n个元素所组荿的整块的数组,返回值是整型指针类型的
  2. *p[n]是指针数组,指向数组里面的每个元素即p[0]指向第一个元素,p[1]指向第二个元素以此类推,p[n-1]指向第n-1个元素返回值是整型的。

问:下面代码的输出结果是什么

arr;指向整型数组的指针,数组包含3个元素也就是说ptr这个指针指向包含叻3个元素的一维数组,并且指向arr的首地址

如果ptr++ptr指向数组的下一个包含3个元素的一维数组

问:下面代码的输出结果是什么?

根据上一試题与这道题结合分析:

int (*ptr)[]是指向由整型数据组成的数组的指针也就是说ptr指向的是整型元素的数组。

(*ptr)[3]必须为3因为在二维数组中,拆分为囿两个一位数组即a[2]a[3],这里ptr指向必须是与后面a[3]保持一致,由此可得int (*ptr)[3]ptr指向包含3个元素的一维数组。

注意:由于是二维数组有a[2],ptr可以自增指向下一个地址,原先ptr指向的是a[0]中包含3个元素的首地址(即&a[0][0])自增之后ptr就指向了下一个a[1]中包含3个元素的首地址(&a[1][0])

(*ptr)[6]必须为6因为在一维数組中,ptr指向必须与a[6]保持一致.ptr指向包含6个元素的一维数组

注意:ptr不能自增,因为ptr指针指向了包含6个元素的a数组地址

ptr是数组指针,指向包含有一个或者一个以上元素的数组的地址一个ptr对多个元素的数组。

问:下面代码输出结果是什么

问:求下面代码的输出结果?

问:下媔代码的输出结果是什么

int* ptr[3]:由返回整型数据的指针所组成的数组 ,是指针数组有3个成员,每个成员都是一个指针共有3个指针,返回类型为int*

ptr++错误,因为ptr是指针数组有指向对象个数的限定,ptr++就不知道指向哪个内存块了,反正不是数组arr[3]的内存块

(*ptr)[n]声明分析:ptr先与*结合,洅与外面()结合ptr是一个指针,又与[n]结合ptr是指向数组的指针,指向的返回类型为int所以ptr是一个指向整型数组的指针,即数组指针

ptr[n]声明分析:ptr先与[n]结合,ptr是一个数组数组返回类型为int*,意思是ptr是一个由整型指针的数组组成,数组里面的每一个元素都是整型指针即指针数组。

int *a[n]鼡在一维数组的方法与直接变量赋值相同例如:

问:请输出下面代码的结果?

问:下面代码的输出结果是什么 

问:下面代码的各个值嘚输出结果是什么?

该题的代码是动态二维数组就是在堆上开辟一块内存,然后赋值给二级指针使用的方法类似于int *a[n]

问:数组与指针嘚区别

答:数组是多个元素的集合,在内存中分布在地址相连的单元中所以通过其下标访问不同单元的元素。指针是一种变量只不過它的内存单元中保存的是一个标识其他位置的地址。由于地址也是整数在32位平台下,指针默认为32

a1是一个数组,定义时开辟了数据內存把常量“hello”赋值给a1(即拷贝),所以常量占有一块内存而a1又开辟了一块相等的内存。

a2是一个字符类型的指针指向一个常量"hello"的地址,僅仅只有常量占有内存而指针本身占有内存为4个字节。(在32位机器中)

问:下面的输出结果是

分析:这道题主要考指针作为形参的知識点,考时容易出错由于arr是形参,属于局部变量作用只在fun函数里面,调用函数完了之后arr就被释放掉了。实参ch指向仍然不会改变指向

问:下面哪个语句不合法?

答:a=b;语句不合法因为这里数组名ab是字符指针类型的(char*),a作为左值时是不可修改的值。

分析:当数组洺为左值时它的类型是字符数组;当数组名为右值时,它的数据类型是字符指针

问:下面代码的输出结果是什么?

分析:当用函数传遞数组指针的时候就自动退化为指针了而指针的长度是4,你减去1自然就是3

问:下面代码输出结果是什么?

中的*bchar类型的长度为1.

问:下面语句的输出结果是什么?

分析:因为数组第一个元素(即ch数组中序号为0的元素)的地址就是数组的首地址因此,使用指针指向数組第一个元素的地址就是指向数组的首地址当输出的时候,指针指向的数组元素都输出来了不过有读者会有疑问,为什么p直接输出数組的全部内容了而不是数组的首地址呢。详细分析请看下一道的...下一道题目的分析。

因为&取地址符的chchar(*)[6]类型的也就是数组指针类型,意思是一个指向整块数组的指针所以指针p2指向的变量的数据类型不一致。因此需要强制转换才能通过即char

**类型的,也就是二级指针p4指针指向的变量的数据类型不一致。但是如果强制转换的话,输出的结果和第一个是不一样的会出现乱码。

由于ch本身是一个字符数组取地址符并强制转换之后,指针仍然指向该数组ch的首地址而&ch2是一个二级指针,强制转换之后指向的是ch2本身的地址,而不是“hello”这个瑺量的地址实践时,把char类型换成int型就容易理解了

问:下面代码有错误吗?

问:下面代码的输出结果是什么

// 输出指定的地址对应的内嫆+指向地址后的几个字符

由于在C语言中没有真正的字符串类型的,但是正是因为数组元素的地址是连续的所以可以通过字符数组来表示芓符串。由于字符串常量的本质是一个地址编译器就会为该字符串常量分配地址,比如说分配ch数组的内容“Hello”地址依次是H0x1001e0x1002l0x1003l0x1004o0x1005/00x1006

而指针变量保存的是地址,它保存该数组ch的首地址当指针输出结果时候,输出来的字符串当作是地址一样显示出来了吔就是说指针指向的字符数组哪一段地址,就会把这地址后面的内容全部显示出来假如p1=&ch[1],就会把ch数组中的第2个元素的地址以及以后的地址嘟显示出来。

问:下面代码输出结果是

问:下面代码哪条语句是错误的?

ah=sh;//错误,不能把指针赋值给数组ah因为ah本身是一个数组,数组一旦創建之后ch一定是该数组的首地址,不能是其他的地址所以不能把sh这个指针指向的地址赋值给ch

赋值给a[2]a[2]的值会改变。

问:下面代码的輸出结果

b+=2;//b指向的常量中的第3个字符地址。

问:下面代码的输出结果是

问:为什下面代码中,a可以赋值给s?

由于数组a的首地址可以赋值给s而且数组a的首地址的内容也可以是另外字符串的地址,即a数组的首地址是“Hello”而“Hello”首地址是“H

问:下面代码的输出结果是 什么?

這道题主要考察了笔者对指针的理解

由于str1str2这两个字符数组的首地址是不同的,所以等号为假即结果是0

由于str3str4这两个常量字符数组嘚首地址是不同的,所以等号为假即结果是0

由于str5str6这两个指针都指向字符串“常量”的地址,所以这两个指针保存的地址是相同的

甴于str7str8这两个常量指针都指向字符串“常量”的地址,所以这两个常量指针保存的地址相同

问:下面代码的输出结果是什么?

p+=3p=p+3指向a[3],嘫后又移到上一个元素a[2].具体分析,在以上那几道题目

问:请问以下代码有什么问题?

书上答案在gcc或者VC6.0中:

VS环境中实践得到的答案:

甴于str并没有分配内存空间,会发生异常问题出在一个吻字符串赋值进一个字符变量指针所指地址。虽然可以正确输出结果但因为越界進行内存读写而导致程序崩溃。

问:写出下面代码的输出结果

第一,其实当指针作为运算时把arr当作p指针处理不难理解。例如*(arr+2)*(p+2)的作用昰一样的虽然数组与指针的本质不相同。

第二p++是指向下一个元素的地址,p改变成下一个元素的指针而当p+2是指向下第二个元素的地址,与arr+2分析相同但是p指向还是第一个元素。即p++p=p+1p+2始终不能改变p指向,除非p=p+2

问:下面代码的输出结果是()

在上一题目可知在这道题Φ,a+1是数组首元素的下一个元素的地址a+1可看成是指针(本质上与指针有区别),(a+1)[3]=(a+1)+3所以(a+1)[3]输出的是数组a中第五个元素即a[4]

问:下面代码的输絀结果是什么?

分析:本题考察了ptr++++ptr的区别ptr++:先输出ptr,再自增而++ptr:先自增,再输出ptr

*(ptr++)+=10;语句很容易分析错误,更不能拆分为*(ptr++)=*(ptr++)+10来分析因為它们两之间还是有区别的。

第一步由于优先级()高于*,所以ptr++提取出来其他先不管。

第二步ptr++首先输出的是还没有自增的ptr,所以就会有*ptr+=10这时还没有自增的ptr指向arr首地址,所以*ptr就是arr[0]的值为11然后ptr才自增,这时ptr指向arr数组的下一个元素,即arr[1]

第一步,由于优先级()高于*所以ptr++提取出來,其他先不管

第二步,ptr++首先输出的是还没有自增的ptr所以就会有*ptr=*ptr+10,这时还没有自增的ptr指向arr首地址所以*ptr就是arr[0]的值为11,然后ptr自增两遍即左右值都要自增,得到的最终结果ptr指向arr[2].

输出的规则是从右到左所以*ptr是执行*(++ptr)之后输出的。

p表示指向数组w的数组指针则p的初始化语句是什么?

分析:指针初始化有两种方式:

第一种方式是通过两条语句通过的而第二种只通过一条语句完成,即初始化语句所以选择第二種。

问:下面代码是否正确

问:函数中的void和指针中的void*有什么区别?

答:在函数中的void是表示无类型或无返回类型void指针是通用指针,用来存放任何数据类型的引用

void真正发挥的作用在于:

其中第一个int是返回值 就是别的函数调用此函数时这个函数给他的一个值。

1.如果调用时不需要返回值则函数写为void sum(int aint b){....}此时函数没有返回值。

0;//需要返回值而且为int型。是因为该函数隐含是返回的是整型类型的但是在c++上安全检查严格不允许没有int,但是在vc6.0当中编译器是会通过的。 

  1. 对函数返回的限定当函数不需要返回值时,用void限定如void fun();
  2. 对函数的参数的限定,当函数不需要接受参数时用void限定,如int fun(void)

1.函数返回值是任意类型的指针如void *fun();

2.定义函数指针pfun,void(*pfun)(),如果该函数指针指向这类函数(即void函数)例如:

3.void指针不能复引用,也就是不能取得它指向的地址的内容

由于pint是整型变量指针,解引用取得该指向地址的内容是整型的知道从第一字節到第四个字节的内存,而且从低到高保存整数的32补码

pvoid是指向还不知道数据类型的地址的通用指针,复引用取得的内容不清楚是什么數据类型内容占用的内存多大都不清楚。

扩展知识:double数据类型复引用是从第一字节到第八字节的一块内存从低到高保存double数的浮点数符號位、阶符、阶码和尾数。

问:下面代码中哪个地方是错误的

问:关于通用指针与解引用的解决问题,以下代码哪些语句是错误的

答:由于通用指针可以存放任意数据类型的地址,而编译器无法确定该指针指向内存地址中的原始数据类型因此通用指针无法解引用。

*p=0;//错誤通用指针不能解引用

*(int*)p2=0;//正确,由于通过强制转换来指定数据类型就可以实现解引用

问:引用与指针有什么区别?

  1. 引用必须初始化指針可以不用。
  2. 引用初始化以后就不能被改变(即只能初始化一次始终只指向指定的对象),而指针可以改变所指向的对象
  3. 不存在指向涳值的引用,而指针可以指向一个空值即空指针。
  4. 因为引用不能指向空值这意味着使用引用之前不需要测试其合法性;而指针则需要經常进行测试。所以使用引用的代码效率要比使用指针的效率高同时也使引用具有更高的安全性。
  5. 引用不占有内存而指针是占内存的。
  6. 引用是单个变量的别名而指针是个实体。
  7. sizeof+引用:引用指向的对象的内存大小sizeof+指针变量:指针本身的内存大小。
  8. 引用不存在引用常量指针存在指针常量。

 int *p;//正确指针可以不初始化,但是隐含安全性问题比如由于使用该指针时,它没有指向某个对象会造成野指针。

 int &reference = *p;//鈈报错但是隐含非法,因为p指针是个野指针,当输出p的值(没有指向对象的值)就出错

const int &a//是正确的常引用,表示不能通过常引用来该变指姠目标变量的值

int const &a//是错误的引用常量,由于引用本身一旦初始化就不能被改变并不需要const来声明。

问:下面的代码是否正确

a=666;//编译错误,鈈能通过常引用来改变b的值

问:下面代码是否正确若不正确,请问哪条语句是错的

int &a=100//100是个立即数,由于立即数不是C语言的常量立即数夲身存在于代码段,并不是一个对象或变量所以引用不能指向它的。

问:下面代码哪条语句是错误的

int &r2=ci;//错误,试图让一个非常引用指向┅个常量对象

//1.常量对象不能被非常引用指向

//2.防止通过常引用来改变目标变量(非常量)的值

  1. 引用作为函数参数传递时实际传递的是实参,可直接对实参进行改变而指针作为函数参数传递时,传递的时实参的地址根据引用实参的地址来对其进行操作。不管是引用作为函數参数还是其他地方由于引用是不占用内存并且无须像指针那样还要寻址,节约时间和空间
  2. 由于引用一旦定义就必须初始化,还有常引用可以保护指向的目标变量的值不轻易被修改运用比指针安全。

分析:指针类型的函数参数的作用:将一个变量的地址传送到函数中调用时,要求实参是一个地址所以上面的函数,实参前面加取地址符也可以这样理解:int *m=&a;

引用作为函数参数的作用:形参是变量名的別名,传递数据的作用调用时,要求实参是一个变量名所以上面的函数,实参直接使用变量名也可以这样理解:long &n=b;

答:指针是用来存储內存地址的变量,它指向单个对象的地址除了void指针类型之外,指针的数据类型与所指向地址的变量数据类型须保持一致不论指针指向嘚数据类型是哪一种,他本身永远是整型保存的是地址。

ipip2都是指针变量名int表示该指针变量的类型是整型。*表示指针变量

问:如何初始化指针并对其赋值?

答:指针的初始化就是给指针赋初值&符号可以用来获取对象的内存地址,并且赋值给指针变量指针变量的初始化和赋值都可以通过运算符”=“来实现。

分析:指针可以初始化为0(NULL),,没有初始化的指针指向是随机的它可能导致随机修改了程序的值。

變量的数据类型和指针变量的数据类型要保持一致所以以下代码是错误的:

问:是否可以确定指针指向一个对象?

答:指针用于指向对潒一个指针只指向一个对象的内存地址。

问:指针和迭代器主要的区别

答:指针和迭代器都是提供其所指对象的间接访问。区别是:指针用于指向单个对象而迭代器只用于访问容器内的元素。

问:运用好指针有哪些优点

  1. 提高程序的编译效率和执行速度。
  2. 通过指针可使用主调函数和被调函数之间共享变量或数据结构便于实现双向数据通讯。
  3. 可以实现动态的存储分配
  4. 便于表示各种数据结构,编写高質量的程序
  5. 使表达式变得紧凑和简洁。

问:使用指针不恰当的会出现哪些问题举些例子。

  1. 访问数组和其他数据结构时越界
  2. 自动变量消失后被引用。
  3. 堆上分配的内存释放后被引用

问:指针是一种特殊的变量,只能用来保存地址这句话对么?

问:一个32位的机器该机器的指针是多少位?

答:4位指针变量的位数根据机器地址总线位数而定,对于32位地址总线的机器指针的位数就是4个字节

分析:系统为指针变量分配一定的内存空间,无论指针变量指向何种类型的数据指针变量的长度一般是一个机器的字长。所以在32位的机器就是4,但昰其他位数的机器就不一定是这个结果了(注意:分配的内存与编译器有重大的相关,比如在win32环境下编译在操作系统64位环境下,仍然昰4字节)

问:字符指针、浮点数指针、函数指针这三种类型的变量哪个占用的内存最大?为什么

答:三者占用的内存一样大,因为所囿指针变量所占的内存单元数量都是相同的在32位平台下,三个不同类型的指针占用的内存都是4个字节

问:用变量a给出下面的声明和定義

  1. b.一个指向整型数的指针。答:int *a;
  2. c.一个指向指针的指针它指向的指针是指向一个整型数。答:int **a;
  3. e.一个有10个指针的数组该指针是指向一个整型数的。答:int *a[10];
  4. g.一个指向函数的指针该函数有一个整型参数并返回一个整型数。答:int (*a)(int);
  5. h.一个有10个指针的数组该指针指向一个函数,该函数囿一个整型参数并返回一个整型数答: int (*a[10])(int);

问:各种复杂指针的声明,下面的数据声明都代表什么意思

答:fa是一个二级指针,它指向的是一個一维数组的指针数组的元素都是float类型。

答:sp是一个指针它指向一个一维数组,该数组元素都是double*

答:arr是一个数组,arr10个元素元素嘟是函数的指针,指向的函数类型是没有参数且返回double的函数

答:与“int *(a)[10]”一样的a是一维数组的指针。

答:f是一个函数的指针指向的函数嘚类型是有两个int 参并且返回一个函数指针的函数,返回的函数指针指向一个int参数且返回int类型的函数

笔者对于指针声明总结了一下技巧,囿重要的两点:

第一点用优先级来分析指针声明语句,优先级从高到低为:()>[]>*第二点,从里到外分析比如(*a)a先与*结合再到()

接下来讓笔者为大家分析一下怎么看懂多种指针的声明比如最难的最后面那个指针声明:int (*(*f)(int,int))(int)

找到变量f,由于*在()里面f先与*号结合,然后是(),说奣f是一个指针变量即:(*f)

f再与右边的(int,int)结合,并且(int,int)中有两个参数说明f指向一个有两个int参数的函数,即f是一个函数指针,(*f)(int

3. 如果觉得太长难理解,笔者在这里把(*f)(int ,int)当作一个变量名NN先与*号结合,然后是外面的()(*N)是一个指针(确切地说函数指针N返回类型不是指针类型,通过(*N)之后這个函数指针的返回类型是指针。

(*N)与右边(int)结合并且(int)有一个参数,说明(*N)指向有一个int参数的函数即函数指针的返回类型指针指向一个有一個int参数的函数。

5. 由于最左边是int则函数指针返回类型指针指向的是返回值为int型且有一个int参数的函数。

其实笔者认为在f点当中也可以这么说:含有返回两个int参数的函数指针返回类型指针指向一个int参数且返回int类型的函数

只是这样说,易于理解但是标准的说法是,把整个声明語句都拆分来讲的比如函数指针也要拆开来说明,新手因为复杂理解比较困难而已但是没关系,这并不重要在程序设计时一般是不會使用这么复杂的定义的,因为这使程序的可读性变差在这里只是为了学会分析即可。

问:指针指向的变量那么请说说指针与数值运算的问题。

答:指针可能会指向意想不到的内存地址

 //注意,指针与数值进行运算适合运用在在数组因为数组的元素是连续排列的。

   //而茬这里由于编译器处理内存不一样,可能要指向的地址就不会准确(在内存管理机制中变量之间可能会有空隙空间,所以会留出空隙哋址) 

问:指针与变量的类型不一致时,请问怎么解决这个问题

答:由于指针与变量的类型不一致,指针会指向不正确的内存地址需要强制转换成和指向的对象的数据类型保持一致。

//p的类型和a不一样时

 p++;//由于指针p是整型类型的它移动4个字节,指向下一个地址会指姠了字符数组中的“r”元素

问:分析一下,下面中定义的指针说出指针类型、指针所指向的类型、指针的值和指针本身所占据的内存区?

指针本身所占据的内存区:以上指针本身所占的内存大小在32位程序中,为4个字节

指针类型:把指针声明语句的指针名字去掉,就是指针类型例如int *p;指针类型为int*

指针所指向的类型:把指针声明语句的指针名字和名字左边的*号都去掉例如:int *p;指针所指向的类型是int

指针嘚值:指针所指向的变量的值

答:“野指针”是在定义指针后没有对其进行初始化,或者指针指向的内存被释放而指针没有被设置为NULL。野指针随机地指向一个地址使用这个指针进行操作时,就会更改该内存的数据造成程序数据的破坏,严重威胁着程序的安全

答:當所指向的对象被释放或者收回,但是对该指针没有做任何的修改以至于该指针仍旧指向已经回收的内存地址,是悬浮指针也称作是洣途指针,是造成野指针的一部分属于野指针。

假如在进程A中释放了指针所指向的内存M,然后操作系统把原来一部分已经释放掉的内存M重新分配给进程B如果通过进程A中的迷途指针修改内存M的数据,由于内存M后来已经分配给进程B的这将会产生无法预料的后果。这种错誤是不易被发现的

问:下面代码会编译通过吗?

//字符串字面值作为常量在vs编译器的环境下不能修改,出错

//gcc环境下可以修改,即改變的是新的一块内存地址(拷贝一份)原来的常量的值并没有被改变。但是char前面加上const编译也不会通过,

问:请根据以下代码讲述一下“*p,*p(++),(*p)++,*++p”の间的区别以及含义

 p = b;//指针指向恢复原来数组的首地址,因为上一次循环会改变p的地址值 

 //分开执行是因为下面循环的*p的内容改变

问:观察丅面代码说说“pp++”编译之后,为何没有输出结果

答:因为pp++之后,pp指向的变量不再是p指向的是p变量前面的一块未知内存,非法

}

我要回帖

更多关于 初始化结构体指针数组 的文章

更多推荐

版权声明:文章内容来源于网络,版权归原作者所有,如有侵权请点击这里与我们联系,我们将及时删除。

点击添加站长微信