你好, 欢迎回来｡
在这节课中,
我想谈谈指针的使用情况, 我们什么时候使用它们, 我们怎么使用它们, 我们知道如何创建它们｡
声明它们并定义它们｡
我们知道如何使用address of运算符将现有变量的地址分配给它们｡
我们知道如何解引用它们, 如何得到指针指向的值｡
现在我们该如何开始使用它们｡
所以c提供了一些基本的操作, 你可以对指针执行｡
你可以给一个指针分配一个地址, 我们已经学过了｡
这是一个现有变量, 其中变量具有地址｡
你可以通过运算符的地址来查看变量的地址, 我们有一个要点｡
我们也可以将一个已有的指针赋值给另一个指针｡
这是两种给指针分配地址的方法｡
因此它本身可以是数组名｡
我们还没有讨论数组, 但是当你给一个指针赋值时,
你所要做的就是指定它的名字｡ 您甚至不必使用操作员的地址｡
到目前为止, 我们只关注了使用现有变量并传入变量本身的地址｡
但是你可以这样做的3种方法都提到了｡
您也可以取消指标的指涉｡ 我们刚刚了解到, 星号是间接操作符｡
这将为给予提供存储在指向的指针中的值｡ 指针,
指针的值, 也就是指针指向的地址, 就用这个.
也可以采用指针地址｡
这是在创建指针时指针的实际地址, 指针本身存储在该地址｡
你也可以在那个指针上使用地址i have运算符｡
您还可以执行指针运算｡ 我们会有一个单独的讲座｡
在这里,
您可以使用加号和减号运算子, 将整数加到指标, 或从指标减去整数｡
这允许你有特定的符号,
允许你在内存中更快地跳跃, 特别是当你使用数组的时候｡
所以你通常要做的是, 当你用加号运算符给一个指针加一个整数的时候, 它只会把指针的地址向上设置这么多个字节｡
如果你从一个指针中减去一个数字, 它会把那个地址设置为减去它一定数量的字节｡
你也可以用1来递增,
递减, 你会发现这在数组中是很常见的.
所以当你在一个数组中, 想要移动到下一个元素, 而不是使用索引, 你可以使用一个指针,
然后加1.
如果你想移动到前一个元素而不是使用索引, 你可以只减少它指向一｡
指针运算, 我们将专门讨论,
但这是你可以用指针来做的事情｡
再一次, 它是一种特殊的符号, 可以让你在内存中快速移动,
更有效率｡
您也可以找到2个指针之间的差异｡
所以如果你有两个指针, 元素在同一个数组里你可能想知道它们之间的距离,
元素,
你可以减去它们｡
这将是2个指针和的差值｡
所以如果你的索引是5, 你有一个指针指向索引5,
你有另一个指向索引15的指针, 你可以减去它们,
这样给予可以得到它们之间的距离,
因为它是十六进制地址减去另一个十六进制地址, 假设数组在连续内存中, 它会显示它们之间的距离｡
您可以使用关系运算子来比较两个指标的值｡
指针必须是相同类型｡ 所以如果它们都指向整数, 它们必须是相同的类型｡
它只是告诉您地址是大于还是小于.
同样, 您可以确定您在数组的哪个部分｡
减法也有两种形式｡
我刚才讲了减法,
你可以用一个指针减去另一个指针, 得到一个指针, 或者用整数减去一个指针, 给予一个指针.
所以从一个指针减去另一个指针得到一个整数或者从一个指针减去一个整数得到一个指针｡
这是两个案子｡
记住, 我们会再看一些例子.
使用指针递增或递减并导致数组越界时, 请小心｡
所以大多数指针运算都是在数组上进行的.
因为同样, 它可以让你快速移动, 它的语法很简单｡
但请记住, 数组有一个越界错误｡
所以如果增量太大, 就会超出数组的范围｡
如果递减太多, 就会有一个下下限, 就会变成一个负数｡
计算机本身不会跟踪指针是否仍然指向数组元素｡
所以在数组上使用算术点时, 你要小心｡
如果你想在表达式中使用指针, 你也可以这样做｡
我们之前讨论过这个问题, 我们解除了指针的引用, 并给它加了15｡
我想我们有20个｡
所以pi指针的值可以用在算术表达式中｡
因此, 如果将变量定义为指向整数的指针类型,
则使用整数运算规则对其进行计算｡
我们用解指来证明｡
如果你有一个变量和一个指针, 初始化为null,
然后用变量的地址标记指针. 然后你就可以说, 星星p数加等于25｡
记住, 星星pnumber是指所指向的值｡
它的作用就是说pnumber的值等于pnumber加上25.
所以这个值将等于35｡
这并没有改变它指向的内容, 因为我们使用了星号,
所以它没有改变我们指向的地址, 它没有改变地址本身, 因为我们没有运算符的地址｡
我们正在改变它指向的值｡
所以把number变量的值增加25, 星星表示你要问的是pnumber变量指向的内容｡
如果一个指针指向变量x, 则该指针已被定义为指向与x相同数据类型的指针｡
所以如果你创建一个结束指针一个浮点指针, 你只能有一个地址的数据类型是float还是int取决于你声明的是哪一个.
因此, 在表达式中使用间接运算符与在同一表达式中使用x是相同的｡
这就是它所说的, 如果你使用一个浮点数, 或者你有一个整型数,
你使用间接操作符, 你解引用它,
你可以做任何你能做的, 如果你有一个浮点数, 不管它指向什么｡
我们知道, 定义为int指针的变量可以存储中任何类型的变量的地址｡
这里int值等于999,
我们可以把这个int赋值给pnumber只要pnumber是一个指向int的指针, 然后我们可以把它加进去｡
该语句将使用新变量值进行操作,
新内容值将为1024.
Pnumber是指向value的, value的地址｡
所以如果你改变指针指向的值, 你就改变了这个变量的值｡
记住, 这是一个概念, 在指针的一个点上改变一个变量,
你是在多个变量上改变它｡
指针可以包含适当类型的任何变量的地址｡
所以你可以用一个指针变量来改变很多不同变量的值, 如果有整型､
双精度或浮点型的话｡ 只要它们的类型与指针类型兼容, 就可以更改该值｡
如果pnumber指向浮点数且值为整数, 则不能更改pnumbers值｡
你做不到的｡ 您将遇到编译错误｡
再举一个例子｡
这里我们将向您展示如何获取一些变量的地址｡
所以在这里, 我们在实际的表达式上做一些指针运算｡
所以我们没有对数组做任何操作, 所以我们没有增加或减少指针｡
所以我们在减少值｡
所以我们有两个long类型num1和num2, 然后我们有一个long类型的指针, 我们把它初始化为null｡
我们将num1的地址赋给指针pnum｡
然后我们把pnum的值赋给L, 这意味着num1现在是2l｡
如果我们增加num2, num2还没有做任何事情｡
所以pnum的值是2l, 这意味着num1的值是2l｡
L只是后缀, 表示它是一个长整数｡
如果我们现在递增长整数num2的值, 它是0l,
它将是1l｡
如果我们说num2+等于pnum,
pnum是2l, 那么它就是1l
+2l, num2现在等于3l｡
如果我们把num2的地址赋给pnum, pnum就等于num2的地址｡
所以如果我们把pnum加1,
我们指向的是, 我们现在会有4l｡
我们会在冷的街区里看这个, 但我会带你走一遍｡
Num1将等于2l｡
Num2将等于它指向pnum｡
Pnum是3l, 所以我想应该是3l｡
Pnum是3l, 然后pneum加上num2是6l｡
让我们看看我是不是对的, 在代码块中检查一下｡
这里是代码块, 我们尝试手动完成,
但现在我们想看看我们是否正确｡ 让我们使用调试器｡
我们把它放在这里, 我们会在程序中看到价值是如何变化的｡
让我们来构建它, 然后调试它｡
它会启动, 然后停在第10行.
如果我们打开watch窗口,
我们可以通过在windows watches中调试来实现, watches, 我们可以看到值的变化｡
所以现在num1是零, num2是零, pnum指向空｡
这就是为什么它是0x0, 因为我们知道null在pnumber中代表0｡
我们可以在if语句中比较它｡
因此, 在执行第10行之后, pnum现在应该具有num1的地址｡
所以我们还是过去吧｡
现在pnum的实际地址为0x60ff08｡
在执行第11行之后, 我们会说pnum的值等于2l, 我们不会在监视窗口中看到任何关于这一点的内容｡
但是我们继续执行它, pnum仍然具有相同的地址｡
因为pnum有Num1的地址, 所以现在改变了num1,
我们改变了它｡
所以现在num1是2｡ 现在在第12行, 我们要递增num2｡
所以num2会变成1l｡
会是1｡ pnum的地址仍然是相同的｡
现在我们要把pnum加到num2上｡
Pnum记住是指向num1的, 所以num2应该是3｡
然后我们到14, 数字2是3, 数字1仍然是2｡
现在在第15行, 我们将为pnum分配一个新地址num2｡
所以在这之后, 我们不再是0x60ff08｡
我们将是060FF04｡
如果我们把pnum指向的值加1, 它是2, 而2是3,
那么2应该是4.
我们看到它是4｡
现在我们要打印出所有这些值, 你会看到num1的值应该是2｡
num2的值应为4｡
Pnum指向num2, 所以应该还是4｡
然后我们会说pnum的值是4加上num2的值是4应该是8｡
所以我可能真的是6分｡
所以现在我们无法在监视窗口中看到它｡
我们来播放一下, 看看输出结果是printf语句｡ 我们应该看到2448｡
现在, 让我们在非调试模式下运行它, 我们会看到2
4 4 8｡
好了, 我们走吧｡ 我希望你说的有道理｡
我们回去继续讨论吧｡
那么当我们接收输入时, 我们如何以那种方式使用指针呢？
到目前为止, 当我们使用scanf来输入值时, 只要我们有一个int或float数据类型,
我们就必须使用“与”运算符｡
唯一的一次是在字符数组的时候, 因为字符a实际上是一个字符指针,
我们会讲到的｡
但是如果我们使用scanf来输入值,
并且我们在非字符数组的普通数据类型上使用它们, 我们就必须使用这个add或sub运算符｡
原因是scanf的第二个参数是一个指针｡
所以我们得给它发个地址｡ 指针只有地址的值｡
因此, 当您有一个已经包含地址的指针时, 可以使用指针名称作为scanf的参数｡
所以现在如果你真的创建了一个指针,
就不需要使用&操作符了, 因为scanf的第二个参数是一个指针, 所以你只需要说出指针的名字就可以了.
在过去,
如果我们想在value中存储一个值, 我们会在scanf的第二个参数中使用“与”号value｡
但是如果我们创建了一个指针, 并且它已经指向了一个地址,
就像这个例子中那样, 那么当我们使用scanf时, 我们只需要说值的名字, 指针的名字.
这里有一个整数,
但我们不需要用"符号, 因为我们有一个指向整数的指针.
我们只说d, p值百分比｡
因此,
如果你有一个指针, 除了字符数组, 还有其他时候你可以把变量的正则名称传递给scanf｡
您还可以打印出他们之后输入的内容,
您会注意到, 值实际上将更改为他们输入的内容, 因为我们指向该地址｡
测试空值｡
有一条规则你必须牢记在心｡ 这非常非常重要｡
我们讨论了解引用,
但是如果你解引用一个非有意的指针, 一个指向空的指针或者一个没有内存分配的指针会发生什么.
如果我们做这样的事呢？
我们声明了一个指针, 但我们从未真正初始化过它, 然后我们试图给它赋值｡
我们取消引用并赋值给它, 这是一个大错误｡
所以第二行基本上意味着你想把值5存储在pt指向的位置, 但是pt并没有指向任何东西,
因为它还没有被初始化｡
所以pt现在会有一个随机值｡
因此, 不知道5将被放置在哪里, 它可以被放置在内存中的任何地方｡
这可能是无害的｡
它可能会高估程序中的现有数据或现有状态变量｡
这可能会导致程序崩溃｡ 这很成问题｡
因此, 当您创建指针并分配内存来存储指针本身时, 它不会分配内存来存储数据｡
所以说instar
pt, 它为指针分配了内存,
但是它没有为指针的值, 地址分配内存｡
请记住, 指针包含地址｡
它创建了指针, 但没有为它所指向的内容创建任何内存｡
所以你不能取消引用它｡
因此它不分配内存来存储数据｡
在你使用指针之前, 应该给它分配一个已经分配好的内存位置,
或者你必须使用动态内存分配, 我们会讲到malloc,
所以你要么给指针分配一个现有变量的地址, 要么使用malloc,
这是动态内存分配｡
但要记住, 当你使用一个指针时, 如果你创建了一个指针,
它是空的, 你要事先检查它是否为空,
我们一会会讲到这个.
但是, 如果你创建了一个没有初始化的指针,
不要给它赋值, 除非它指向另一个以这种方式初始化的变量, 或者已经分配了内存｡
这就是null的作用所在｡
实际上, 您可以测试null,
以确保没有解引用错误｡ 确保您的程序不会崩溃｡
我们已经知道null基本上指向空,
我们应该初始化所有的变量, 指针为null｡
所以我们应该一直这样做｡
Null是c中的一个特殊符号, 它表示指针与普通数字的零相当｡
下面的还使用零将指针设置为null｡
所以如果我们不想传递或者赋值给null, 我们可以直接说0,
这就相当于什么都不指向.
因为null等价于0,
如果你想测试p值是否为null, 你可以做一些类似显式测试的事情｡
你可以说指针等于null｡
如果指针等于null, 也就是说它是零,
那么你就可以做点什么了｡
所以如果它不是零或者非空, 它是一个true或者任何其他值,
这是一种检查是否有崩溃的方法｡
在实际尝试解引用某个对象之前, 要确保它会告诉你它是否分配了内存｡
因此, 您需要在取消引用指针之前检查是否为null｡
你经常在函数中这样做, 因为函数将指针作为参数｡
如果调用者传入错误的数据或传入没有分配任何内存的指针, 则可能会导致代码崩溃｡
所以你要做的就是确保调用者有一个有效的地址, 这样你就可以检查它是否为空｡
基本上, 这意味着总是有这些如果检查说,
不是p值或不是指针｡
如果它不是指针, 那么您可以取消引用它｡
我们一直在谈论指针｡ 我们已经讨论了很多｡
我肯定你有点受不了了｡
你可能想休息一下, 复习一下前面的课程, 试着在你的脑海中真正同步这个概念｡
指针只包含地址｡
但它有三个部分, 所以它可能会有问题｡
但要保持乐观｡ 继续跟我说我知道这很混乱｡
我们已经讨论过如何使用地址｡
我们已经讨论过如何与价值观合作｡
我们已经讨论了如何处理指针本身, 我们也讨论了如何处理指向指针的变量.
所以你必须记住所有这些东西, 这可能会让你非常非常沮丧｡
要想弄清楚到底发生了什么总是很难的, 这需要大量的练习｡
要理解这个概念以及我们到目前为止所讨论的所有内容,
最好的方法就是练习, 编写使用我所提到的所有概念的短程序｡
所以写一个程序,
用一个指针来获取值, 这可能是用了间接操作符, 取值.
编写一个使用指针改变值的程序｡
这就是解引用, 用指针得到的值就是指针的名字,
也就是地址. 打印地址, 我们已经看过了所有的语法｡
一遍又一遍地练习这些东西, 这样你就能做得很好｡
你必须树立信心, 你必须详细研究这些概念才能掌握它们｡
我们还有很多关于指针的内容要谈, 但现在可能是时候离开一下, 回顾一下我们到目前为止所讨论的内容,
以便深入到您的脑海中｡
-谢谢—谢谢