C与指针:指针

1.字节:

每个字节包含8个位,可以储存无符号值0至255,或有符号值-128-127。

2.指针变量

1.指针的初始化是用&地址符来完成的,它用于产生操作符的内存地址

                                                  100            104       108                                         112              108

112-11078523331100108

 

                                                  a                 b            c                                             d                 e

112-11078523331100108

a=100;b=104;c=108;d=112;e=108;

int  a=112;//    a=112

int b=-1;//      b=-1

float c=3.14;//   c=3.14

int *d=&a;//    d=100

float *e=&c;//   e=10

注:变量的值就是分配给该变量的内存位置所存储的数值

1.指针变量的的各种自增 *p++

这个问题其实是论述*P++等价于(*p)++还是等价于*(p++)的问题,为了验证这个问题,我编写了下面的小程序(vc++6.0编译环境),作为验证

程序1

#include "stdio.h"
void main()
{
    int a=2;
    int *p=&a;
    int *pold;//记录P指向的地址,为了作为比较使用
    pold=p;
    //-----------原来的信息
    printf("原来的a=%d ",a);
    printf("原来的p=%x ",p);
    printf("原来的pold=%x ",pold);
    //----------进行变化
    printf("*p++的结果=%d,a的结果a=%d ",*p++,a);
    printf("地址变化的结果p-pold=%x ",p-pold);
}

运行结果是:

程序2

#include "stdio.h"
void main()
{
    int a=2;
    int *p=&a;
    int *pold;//记录P指向的地址,为了作为比较使用
    pold=p;
    //-----------原来的信息
    printf("原来的a=%d ",a);
    printf("原来的p=%x ",p);
    printf("原来的pold=%x ",pold);
    //----------进行变化
    printf("(*p)++的结果=%d,a的结果a=%d ",(*p)++,a);
    printf("a的结果a=%d ",a);
    printf("地址变化的结果p-pold=%x ",p-pold);
}

运行结果

程序3

#include "stdio.h"
void main()
{
    int a=2;
    int *p=&a;
    int *pold;//记录P指向的地址,为了作为比较使用
    pold=p;
    //-----------原来的信息
    printf("原来的a=%d ",a);
    printf("原来的p=%x ",p);
    printf("原来的pold=%x ",pold);
    //----------进行变化
    printf("a=%d,(*p)++的结果=%d,a的结果a=%d ",a,(*p)++,a);
    printf("a的结果a=%d ",a);
    printf("地址变化的结果p-pold=%x ",p-pold);
}

运行结果:

程序4

#include "stdio.h"
void main()
{
    int a=2;
    int *p=&a;
    int *pold;//记录P指向的地址,为了作为比较使用
    pold=p;
    //-----------原来的信息
    printf("原来的a=%d ",a);
    printf("原来的p=%x ",p);
    printf("原来的pold=%x ",pold);
    //----------进行变化
    printf("*(p++)的结果=%d,a的结果a=%d ",*(p++),a);
    printf("a的结果a=%d ",a);
    printf("地址变化的结果p-pold=%x ",p-pold);
}

运行结果:

程序5

#include "stdio.h"
void main()
{
    int a=2,*p=&a,*q=&a;
    printf("%d %d  ",(*p),a);
    printf("%d %d %d ",*p++,(*p)++,*q++);
    printf("%d ",a); 
}

 

#include "stdio.h"
void main()
{
    int a=2,*p=&a,*q=&a;
    printf("%d %d  ",(*p),a);
    printf("%d %d %d ",(*p)++,*p++,*q++);
    printf("%d ",a); 
}

 

#include "stdio.h"
void main()
{
    int a=2,*p=&a,*q=&a;
    printf("%d %d  ",(*p),a);
    printf("%d %d %d %d ",(*p)++,*p++,*q++,a);
    printf("%d ",a); 
}

 

#include "stdio.h"
void main()
{
    int a=2,*p=&a,*q=&a;
    printf("%d %d  ",(*p),a);
    printf("%d %d %d  ",*p++,(*p)++,*q++);
    p=&a;q=&a;
    printf("%d %d %d %d ",a,*p,(*q)++,a); 
    printf("%d %d %d  %d ",a,++(*p),++(*q),a); 
    printf("%d ",a);
    
}

综上所述:

*p++是先取出*p的值,然后让p++

(*p)++是先取出*p的值,让这个值++

*(P++)是先取出*p的值,让p++

所以,*p++等价于*(P++)

而且printf的运行顺序是从右向左运行。而且右++是在整个运算表达式运算完才进行,而且右++的执行顺序是从左到右进行。而左++遇到变量就会立即增加变量的值。

 

验证printf的运算顺序

#include "stdio.h"
void main()
{
    int a=2;
    printf("a++=%d,++a=%d,a++=%d ",a++,++a,a++);
    printf("a=%d ",a);
}

运行结果:

 

*p++是先取出*p的值,然后让p++

(*p)++是先取出*p的值,让这个值++

*(P++)是先取出*p的值,让p++

所以,*p++等价于*(P++)

  void main()
{
 int a=2,*p=&a,*q=&a;
    printf("%d %d  ",(*p),a);
    printf("%d %d %d  ",*p++,(*p)++,*q++);
 cout<<a<<endl;
    p=&a;q=&a;
    printf("%d %d %d %d ",a,*p,(*q)++,a);
    printf("%d %d %d  %d ",a,++(*p),++(*q),a);
    printf("%d ",a);
 getchar();

}

2 2 3 2 2 3

4 4 3 4 6 6 6 6 6

而且printf的运行顺序是从右向左运行。而且右++的执行顺序是从左到右进行。而左++遇到变量就会立即增加变量的值。

验证printf的运算顺序

#include "stdio.h"
void main()
{
    int a=2;
    printf("a++=%d,++a=%d,a++=%d ",a++,++a,a++);
    printf("a=%d ",a);
}

运行结果:

a++=4,++a=5,a++=2 a=5

#include <iostream>
#define f(x) x*x

void main()
{
 int a=2;
 cout<<f(a++);
 cout<<f(++a);}

运行结果: 4  36

 

首先char*c[]为一个指针数组,对吧,这个相信大家都能看懂,千万不要把指针数组和数组指针搞混乱了。

首先先给大家讲解下什么是指针数组,什么又是数组指针呢?

eg:char *c[5]因为[]的优先级高于*,所以c先与[]结合形成一个数组,类型为char* 类型,大小为5。里面存放属于char类型的指针。其实数组指针和二维指针有一定的相似之处,如果感兴趣,大家可以百度什么是二维指针,在以后的篇幅里,我也会详细给大家解释什么是二维指针,他们的联系是什么,区别又是什么呢?

而对于char(*c)[5]因为()的优先级最高,所以c先和*形成一个指针,然后再与数组结合,这就形成了数组指针,即为指向数组的指针。它指向包含5个char类型元素的一维数组,此时c的增量以它所指向的一维数组长度为单位;

好了 弄懂了数组指针和指针数组,那么接下来继续解答该道题。

看这句代码,

char**cp[]={c+3,c+2,c+1,c};
1
仔细看看,这里的**cp[]比上面的*c多了一个*。不知道大家看到这里有没有点疑惑呢?

多一个*号那他又代表什么呢?我来给大家举一个简单的例子吧。

char a='a';

char *b=&a;
1
2
3
上面这个代码的很简单吧!先定义了一个char变量,再由b指向a的地址。也就是说b里面存放的数据是变量a的地址。那么接下来就简单了,

再回到原题,char**cp[]也比char*c[]多一个*,那么不用说,cp[]里面也是存放着c[]的地址吧。看看cp[]里面的元素,c+3,c+2…果然是地址吧。

这里大家又有疑问了,为什么c+3是一个地址?那你们听说过“数组名可看做一个地址”这句话吧!,那就对了,虽然c[]是个指针数组,那他仍然是个数组啊!!这就不难理解c+3,c+2等地址了吧。这里大家谨记可以吧数组名看做指针,但是数组名与指针又不是完全等价的!这个以后慢慢详解。那么接下来回到原题,看第三句,char***cpp=cp;

这句话我想很好理解吧!他是把一个存放指针数组地址的数组显式转化为一个指针。emmmmm,是不是有点头晕,转化来转化去的。看看他有3个*号,你就知道他是个三级指针了。

接下来看第一句printf, 参数是**++cpp;;首先大家要知道输出三级指针指向的内容,他两个*又代表什么。

首先先看cpp前面有个++,什么意思呢?大家看看第三句代码,cpp指向cp,那么 ++cpp是不是就是cp[1]的地址啊,肯定是的。然后带个*号,取到cp[1]的值,而 cp[1]的值是c+2,同时c+2又是一个地址,请大家好像体会这句话,给一个指针变量前面加一个*号就是表示他指针指向地址的值,那么这就很好理解 **++cpp了。先是++cpp获取到cp[1]的地址,要获取cp[1]指向的内容再 给前面加个*号就可以了,即 *++cpp就代表cp[1],即c+2。而 c+2也 是一个地址,要获取到c+2这个地址指向的内容,那再给前面加个*号就可以 了。即为**++cpp;

这样**++cpp就代表指针数组c的第三个元素了,是一个字符串“world”,%s接收到这个字符串会把它都输出出来。即第一个printf打印WORLD.

举个简单的例子,给你们提个问题。

int a[5]={1,2,3,4,5}

int *p=a;

p++;//1

a++;//2
1
2
3
4
5
6
7
这两个哪个正确呢?

1是正确的2是错误的。

要取到里面的元素 * ( p ),* ( p+1 ).…;注意看这里也带个*,这个代码的意义是把数组显式转化为一个指针!,再结合图片的第二行和第三行,你现在应该清楚他的作用了吧。对,就是将数组显式转化为一个同维度的指针。。。好累明天继续更博。

接下来回到问题,因为经过第一句**++cpp完后,cpp已经指向了cp[1],再次的*++cpp同样的道理,获取到cp[2]的元素,即c+1这个地址。然后再--,即为c这个地址,再带个*号,即为c这个地址下代表的值,其实也可以这么说,可以把指针数组看成一个二维数组。可以写成这样c[5][4]。那么c此时代表的值应该是二维数组的第一行吧。即c[0]。即此时为“hello”,别忘记了,后面还有个+3呢。因为hello本身也是个地址,如果大家再此又困惑,我给大家举了一个简单的例子。希望大家能认真看下帮助理解。



%s这个格式控制符需要接收一个字符串地址,c[0]地址获取到了,他就会把c[0]输出,这里给大家提个问题。他获取到c[0]地址后,会不会也把c[1]也输出出来呢?

哈哈 ,不要困惑了。并不会的。因为c[0]这个字符串 “hello\0”,他会有个默认的'\0'结束符,结束%s的获取。我为什么会问这样的问题。因为要是同样的问题 遇到了二维数组大家想想又是什么样的情况呢?


大家好好对比看下。针对二维数组,让%s获取到c[0]即二维数组的第一行 时,他会把整个二维数组输出出来。直到遇到c[2][2]是个默认的‘\0’结束输出。

通过一个例题 我举出这么多例子。。。。真累。不知道大家能不能理解呢?

接下来回到问题的第三个输出,参数为*cpp[-2]+3。不难理解。可以转化下,切记接下来这个让我受益终身的一句语法糖,*(a+1)等价a[1]。当你被指针搞得头大恨不得疯掉的时候,一定要想起这句语法糖。我不扯了。回到原题 。根据语法糖法则,把*cpp[-2]+3转化成**(cpp-2)+3。我想通过我这么详细的分析。这道题对大家不再有难度了吧。算了 ,送佛送到西。cpp经过上面两句已经指向了cp[2]这个地址,那么cpp-2指向cp[0]的地址没疑惑吧。然后带个*,即为*(cpp-2)即为cp[0]这个元素,即代表c+3这个地址,然后再带个*号为**(cpp+3),成为c[3]代表的元素。c+3这个地址代表的字符串是“SAYHI”,再+3,即把该字符串的地址再往后移动三位。这样就输出了“HI”。切记*(地址)=该地址指向的值。这句话可以套用。即就是这里面的地址可以是地址的地址。请各位好好理解吧。eg:*(*(地址的地址))
接下来第四句 cpp[-1][-1]大声告诉我,是什么!!!算了 ,我继续解答吧,谁让我就是这么这么好呢。
老规矩,先转化成*(*(cpp-1)-1)+1;你们说下cpp此时指向谁呢?指向cp[1],如果你觉得我说错了,
请好好回头仔细看看。很多人困惑了,为什么第三句输出不是移动了cpp了啊 。请你再仔细看看第一句,
第二句和第三句printf里面的区别。对的,第一句第二句++cpp和第三句cpp-2是完全不相同的道理。
就比如int a=1;a++和a+2能一样吗?显然是不一样的 a++后a自增为2。a+2执行完后a还是1。好了接下来
回到原题。指向cp[1]后带个*号取到cp[1]的元素即为c+2又是个地址,在把这个地址--,得到c+1的地址。再
带*号,得到c+1地址指向的值。为“New”,得到这个字符串地址后,再+1,输出EW。

此题结束 。答案选哪个呢?

我再问大家一个问题还是原来的那些数组,删掉那4句printf(这样cpp指针就是初始状态),重新输出一句

printf(“%c”,cpp[1][1][1])
1
会输出什么呢? 我给个答案 :输出A。

printf(“%c”,cpp[1][-1][1])
1
又输出多少呢?答案:输出E。

int *p[4]; //定义一个指针数组,该数组中每个元素是一个指针,每个指针指向哪里就需要程序中后续再定义了。
int (*p)[4]; //定义一个数组指针,该指针指向含4个元素的一维数组(数组中每个元素是int型)。

区分int *p[n]; 和int (*p)[n]; 就要看运算符的优先级了。
int *p[n]; 中,运算符[ ]优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组。
int (*p)[n]; 中( )优先级高,首先说明p是一个指针,指向一个整型的一维数组。

空指针和未初始化指针:

区别:

1.空指针也就是通常指向为NULL的指针(可以确保不指向任何对象或函数)

2.野指针就是指向一块未知的内存区域(可以是通过malloc或new申请空间后,释放后没有将指针置为空),也有可能定义了一个指针没有初始化,由于内存空间中的值在未赋值之前是随机数,所以也有可能诞生野指针。(可以指向任何地方)

无法为内存为0的地址取地址(ox0这个取地址非法)

malloc在其内存分配失败时返回的是一个未初始化的指针
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值