学习日记-指针专题

思考几个问题?

1.什么叫指针?

内存以字节为单位开始编号,编号为地址,在c中把这个地址叫做指针,把存储这个地址的变量叫做指针变量。

2.指针占几个字节?

64位系统中有64根地址总线(cpu和内存通过地址总线传输二进制数0和1),占8个字节

32位系统中有32根地址总线,占4个字节

3.使用指针有哪些好处?

让程序简洁,紧凑,高效;有效表示复杂的数据结构;动态分配内存;得到多于一个的函数返回值

4.内存管理(重要)

内存从低地址到高地址区域分布分别为:代码区,静态区,初始化,堆,栈(先进后出)

指针

1.指针变量的说明,一般形式:<数据类型>*<指针变量名> 例: int *p;

2.指针的初始化:例:int a=10; int *p=&a(注意必须要初始化不然会成为野指针非常危险,也可以赋NULL,会在使用出现问题时提醒你)

3.指针指向的内存区域的数据称为指针的目标,可以用*将其中存储内容取出。

4. int *p ; p-指针变量 。 *p-指针指向的对象,内容是数据。&p--指针变量占用的存储区域的地址,是个常量。

5.*和&互为逆运算,*与【】是等价的,可以相互转换,&与【】互为逆运算。

6.二级指针和二维数组有什么关系吗?答:毫无关系!!!

指针的赋值运算

1.通过赋值运算符向指针变量送一个地址值,必须是地址常量或指针变量。

double x=15;double *px; px=&x;

2.把一个已有地址值的指针变量赋给具有相同类型数据类型的另一个指针。

float a,*px,*py; px=&a;py=px;

3.把一个数组的地址赋给具有相同数据类型的指针

int a[20],*pa; pa=a;//pa=&a[0]

指针运算

1.是以指针变量所存放的地址量作为运算量而进行的运算,实质就是地址的运算,只能进行赋值,算术(+,-,++,--),关系运算。

2.同数据类型的指针进行运算才有意义,不同类型的指针进行运算是无意义的。

3.指针变量p+n表示的实际位置的地址

p+n: p+sizeof(p的数据类型)*n 注:int为四个字节,double是8个字节,char是一个字节

p-n: p-sizeof(p的数据类型)*n

总结:指针变量加减n,移动对应数据类型所占的字节数*n,特别注意,后面也会用到!!!

4.指针相减运算p-q:结果是两指针指向的地址位置之间相隔数据的个数是一个整数位。

5.逻辑运算:&& || !是和NULL进行比较

6.指针关系运算:表示它们指向的地址位置之间的关系,指向地址大的指针大于指向地址小的指针。和整数变量之间的关系运算没有意义,但可以和零进行=和≠的关系运算判断指针是否为空(指针一定要给地址,不然会指向随机地址,成为野指针,第二次强调这个非常重要,注意指针当前值,指针变量可以指到数组外的内存单元)

注意:++p和p++的区别,下面我将会通过一个程序的运行结果来向你展示他们的区别

运行结果如下图所示:在q=p++运算中,系统先把p赋值给q,再执行++

我们将q=p++换成 q=++p会出现什么结果呢我们来试试看:看运行结果我们发现如果是++p则会先执行++再赋值

我们再来看一个例子,看看下面这个程序,你能看出他们的运行结果吗?

y=(*--p)++这个表达式的执行顺序是:先将p左移一位取出其中的值赋给y,然后将其中的值就是5将其加1,于是可以得到结果分别为 5和6,我们看看结果是否如我们所料

指针与数组

1.c中,数组的指针是指数组在内存中的起始地址,数组元素的地址是指数组元素在内存中的起始地址。

2.一维数组的数组名为一维数组的指针(起始地址)例:double x[20];x为数组的起始地址。

3.设指针变量px的地址值为数组指针x:px=x,则 x[i],*(px+i),*(x+i),px[i],都具有完全相同的功能,访问数组第i+1个数组元素。

注:p[i]==*(p+i),  p[i][j]==*(*(p+i)+j);指针变量和数组访问数组中元素时,一定条件下使用方法具有相同形式,因为都是地址量。但本质不同,指针变量是地址变量,而数组的指针是地址常量。一定要注意

4.指针和二维数组

二维数组在内存中也是连续存储的,按行优先存储,所以我们可以把二维数a[ ][ ]组当作若干一维数组组成,则a[ ]则为一维数组起始地址,二维数组名为行地址,数组名加1移动一行元素注:*(a+1)==a[1]

5.数组指针(行指针)

一般形式:<数据类型>(<*指针变量名>)【表达式】例:int (*p)[3];int a[2][3]

注:用数组指针也就是行指针操作二维数组时,表达式一般写一行的元素个数,即列数,这样在对p进行加1操作则刚好是移动一行的,但不排除会有一些题他的表达式大小不等于对于的数组一行的元素个数,特别注意!!!

我们来看一个例子,使用数组指针完成对二维数组每一行的求和:

我分别使用了数组指针以及下面我们会讲到的指针数组来完成这个程序由此来让你知道指针数组与数组指针的区别:指针数组本质是一个数组,只不过数组中存放的元素是指针,而数组指针本质是一个指针,只不过它指向整个数组,从程序中我们可以看到指针数组我们分别存储了二维数组每一行的行地址,而数组指针我们让他指向了整个二维数组,在实际运用中,我们对于指针的判断非常重要,判断指针的类型,以及指针指向的类型以及指针当前所指向的位置都是我们解决问题的关键,在本文最后我附上了如何判断的技巧,这里我分享一个好记的方法在区分指针数组和数组指针,名字最先念的最后结合,最后念的最先结合。(【】的优先级比*高!!!!)

   #include <stdio.h>
   
   int main(int argc, char *argv[])
  {
      int a[5][3]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15};
      //int *p[5]={a[0],a[1],a[2],a[3],a[4]};//指针数组
      int (*p)[3]=a;//数组指针(行指针)
      int i,j,sum=0;
      for(i=0;i<5;i++)
      {
          for(j=0;j<3;j++)
          {
              sum+=p[i][j];
          }
          printf("第%d行的和为%d\n",i,sum);
          sum=0;
      }
      return 0;
  }


字符指针与字符串

1.我们把char数据类型的指针变量称为字符指针变量

2.通常通过字符数组来处理字符串

3.初始化:把内存中字符串首地址赋予指针,当一个字符指针指向一个字符串常量时,不能修改指针指向对象的值。举例:char *p="hello"; *p='H';这样是不行的。

4.注:从内存管理角度:全局变量,static修饰的局部变量,字符串常量都是放在静态区,程序结束才释放内存,而作用域为局部的变量是放在栈上的,调用结束就会释放内存,特别注意。

接下来让我们用一个例子来具体认识字符指针:

举例:不使用任何字符串函数,编程实现字符串连接函数的功能:

 让我们分析一下这个程序的编写过程,在指针的使用中往往可以画图来分析,以免思路混乱,首先我们通过while循环找到ch【】数组中字符串的有效长度,随后我们将字符指针指向的字符串常量依次赋值,最后循环结束后字符指针p指向字符串常量中的‘\0’,此时我们将‘\0’也赋给它,标志字符串结束。让我们看看运行结果:

 指针数组

1.一般形式:<存储类型><数据类型>*<指针数组名>[大小]  举例:int *pa[2](说明:pa先和【】结合表示一个数组,随后和*结合表示存储的int*数据类型)

注:这里【】里的大小一般是数组的行数

2.定义:有若干个具有相同存储类型和数据类型的指针变量构成的集合

3.初始化:举例说明 int a[2][3]; int*pa[2]; pa[0]=a[0],pa[1]=a[1];

下面让我们通过一个例子来认识指针数组:

举例:用指针数组处理二维数组,求所有元素的和:

 让我们看看程序运行结果是否正确:

 *(p[i]+j)这里是将第i行第j个元素取出的意思,也可以写成*(*(p+i)+j)或者p[i][j]。

解释:这里我们先用*解一层找到行数为*(p+i)再+j找到列数,这里*(p+i)+j表示第i行第j列的元素地址,我们再用*解一层取出对应地址的元素值,这里解释应该比较通俗易懂吧!!!

几个思考问题?

 1.如何计算指针数组占用的内存空间?

sizeof(p):看指针数组中包含几个指针变量,乘以一个指针变量所占字节(64位系统一个指针变量占8个字节,32位系统占4个字节)

2.指针数组名加1移动多少字节?

64位系统移动8个字节,32位系统移动4个字节

3.指针数组名相当于一个什么指针?

相当于一个二级指针,int **p

多级指针

指向指针变量的指针变量,称位多级指针变量

多级指针的运算

1.指针变量加1,是向地址大的方向移动一个目标数据,多级指针运算也是以其目标变量为单位进行偏移。

2.举例:int **p;p+1移动一个int**变量所占的内存空间(64位系统为8个字节,32位系统为4个字节,这里我们已经重复强调很多次)

多级指针和指针数组

我们通过一张图来直观了解多级指针和指针数组

这里我们可以发现指针数组也可以用另一个指针来处理,让我们结合这张图来看看下面这段代码,你就可以理解了:

 然后我们来看看运行结果,现在你应该已经理解了多级指针的初步使用了

思考一下,多级指针在内存中占几个字节呢,多级指针加1移动多少字节呢?

如果你注意看之前的思考那么你应该已经注意到了指针所在字节只和系统有关,是固定的,64位占8个字节,32位占4个字节,加1也分别移动了8个字节和四个字节,这里我已经反复强调了多次!

void指针和const修饰符

void指针:void *<指针变量名>

是一种不确定数据类型的指针变量,它可以通过强制转换让该变量指向任何数据类型的变量。

之后的学习中我们会常常用到void指针,因为它可以处理任何数据类型的指针,但注意在没有强制类型转换时,不能进行算术运算。

接下来让我们用一段代码来看看void指针的使用:

举例:用void指针遍历一维数组

 以后用到void指针的地方还很多我们后面再举例详细说明。

const变量(常量化变量的值)

1.const <数据类型>*<指针变量名>[=表达式]:

限制通过指针改变其目标的数值,但变量存储的地址是可以修改的,我们看一段代码加深理解:

 我们来编译一下看看结果:

我们发现我们对指针变量p指向的值进行++运算出现错误警告,现在你理解了把!

2.<数据类型>*const <指针变量名>

限制修改指针变量存储的地址值,但可以改变其目标的数值。

我们再看一段代码来加深理解:

 我们编译该程序会出现什么情况呢?让我们看看:

 我们发现当我们让q指向一个新的地址时,出现了报错,现在你应该理解了吧!

3.const <数据类型>*const<指针变量名>

都不可以进行修改!!

补充:static的作用:

1.修饰全局变量,将其限制在本文件中

2.修饰局部变量,延长其生命周期

3.修饰函数,将其限制在本文件中。

extern的作用

1.可以在一个文件中去扩展全局变量作用范围

2.将全局变量的作用扩展到其他文件

3.修饰函数,此函数就成了外部函数,可供其他函数使用。

 好了现在思考几个问题?

1.void指针的作用?通过不同的类型转换完成不同场合的应用。

2.main函数是否可以带参数?

指针函数

定义:是一个函数的返回值为地址量的函数

一般形式:<数据类型>*<函数名称>(参数说明)

这里有个需要注意的地方:函数中的变量作用域是局部的,他是存放在栈上的,函数调用结束就会被释放导致内存失效,所以返回值是四种情况:1.全局变量的地址2.static变量的地址3.字符串常量的地址4.堆上的地址(动态内存)

我们通过一个程序来理解:

#include <stdio.h>
char *mystring()
{
    char str[20];
    strcpy(str,"hello");
    return str;
}
int main()
{
    printf("%s\n",mystring());
    return 0;
}

上述程序你能看出有什么问题吗?

1.使用了strcpy函数但没有包含string.h头文件。

2.str是一个局部变量,存放在栈上,函数结束内存释放,变量回收,访问的内存失效了。

注意:面试中问到程序问题,如果有指针一般是内存问题,我们可以这么回答,这个程序内存有问题,执行结果是不确定。

那么要怎么修改呢?

1.可以把str改为全局变量。2.把str用static进行修饰

接下来我们来看几个指针函数的例子你能写出来吗?

1.运用指针函数,删除一个字符串中的空格。

我们来看看这段代码,试着理解一下它的思路

 其实思路很简单,利用循环,当我们遇到空格时,我们将指针移动到下一位,不是空格我们就将他复制,最后别忘了加上'\0'表示字符串结束!

2.利用指针函数实现字符串的连接

来看看代码,尝试理解:

3.编写一个指针函数,把整数123转化成字符串“123”

注意字符串和整数的转换的方法:字符转数值:字符-‘0’    数值转字符:整数+‘0’

看代码!

 

函数指针 

1.用来存放函数的地址,这个地址是一个函数的入口地址。

2.函数名代表函数的入口地址。

3.一般形式:<数据类型>(*<函数指针名称>)(<参数说明>) 举例:int (*p)(int,int)

我们通过一段代码来初步认识函数指针的作用:

 我们来看看运行结果:

 发现了吗,我们的函数指针可以通过指向不同的函数地址实现不同的功能,方便了不少!!

函数指针数值

1.定义:是一个保存若干函数名的数组

举例:

int (*p[2])(int,int);
p[0]=add;
p[1]=sub;

一般形式如下:

<数据类型>(*<函数指针数值名>【大小】)(参数说明):大小表示函数指针数值元素个数

接下来我们看一个综合性的题目:

调用c库中的qsort函数实现整形数组排序

 发现了吗这个题目中运用了空指针,以后我们还会频繁的使用!!

补充:

指针的类型与指针指向的类型与指针的值

1.指针的类型:把指针声明语句的指针名去掉就是指针类型

int *p  他的类型就是int *

int **p 它的类型就是int **

int (*ptr)[3]它的类型就是int (*)[3]

2.指针所指向的类型:指针名字和名字左边的指针声明符*去掉

int **ptr 它所指向的类型就是 int *(加减1会移动指向类型的字节数)

3.指针的值:指针所指向的内存区或地址,指针本身存储的值被编译器当作一个地址,在32位程序里所指的值都是一个32位整数

4.使用异或可以实现两个数的交换并且更快

a^=b

b^=a

a^=b

就完成了两个数的交换!!!

5.&为升阶,*为降阶

一维数组 a[10],a表示该数组首地址,如果我们对它取地址符,&a此时就表示整个数组地址

二维数组 a【10】【10】,a【0】表示第0行的首地址,如果对他取地址符,&a【0】便表示整行的行地址。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值