不定长的指针数组 里面存字符串_C/C++指针总结(下)---这些你混淆了吗?

指针总结1、常量与指针(1)常量指针:(2)指针常量(3)常量常指针2.函数指针,结构体指针(1) 函数指针(2)结构体指针3. 数组指针(1)数组也是指针(2)指针数组(3)行指针,列指针--行指针--列指针4.二级指针与指针作形参(1)二级指针(2)指针作函数的形式参数5.指针与引用(1)指针与引用的关系?(2)引用能简化形式参数

1、常量与指针

(1)常量指针:

格式: const 数据类型 * 名字 = 地址

即指向常量的指针,它不能修改常量的值,但可以指向其他常量和变量

首先需要了解一下常量修饰限定符const,在变量前加上const就表示这是一个常量

const int a=100;

const int * ptr =&a;(或者可以写成 int const * ptr =&a;)

12

不能写*ptr=200; 因为指针ptr是一个变量,它指向的是一个常量,而常量是不能改变值的.但可以写成ptr=&c;

意思是对指向的数据不能进行修改,只能访问

(2)指针常量

格式: 数据类型 * const 名字 = 地址

指针常量即该指针是常量,既然指针是常量,那么该指针就不能改变,意思是不能再指向其他的变量,不能脚踏两只船,但是可以改变指向变量的值

int a=100;

int *const ptrA=&a;

12

不能写 ptrA=&b;但可以写成 *ptrA=1000;

(3)常量常指针

就是前两者的结合,既不能改变指针的值,也不能改变指向变量的值

conest int a=100;

conest int b=10;

conest int * conest ptr =&a;

123

这里大家可能会有点分不清楚,这里有个小窍门,就是看const离谁进就限定谁

比如 const int *ptr,这里const相当于int 和

*ptr 来说,离int 更近,所以这是一个指向常量的指针

再看 int *const ptr,const 离ptr近,所以这是一个指针是常量!!

2.函数指针,结构体指针

其实函数和结构体都是有地址的!!!

(1) 函数指针

所以类似于其他变量等,我们也可以用指向函数的指针

格式 数据类型 (*函数名)(数据类型 变量名)

int fun1(int a,int b);

int (*compare)(int a,int b);

compare=fun1;

int result=(*compare)(3,5);

1234

首先我们定义了一个函数fun1和一个函数指针,这个函数指针指向了fun1这个函数,然后后面调用这个函数.

(2)结构体指针

struct MyStruct

{

int a;

int b;

int c;

};

struct MyStruct ss={20,30,40};

//声明了结构对象ss,并把ss 的成员初始化为20,30 和40。

struct MyStruct *ptr=&ss;

/*声明了一个指向结构对象ss 的指针。它的类型是MyStruct *,

它指向的类型是MyStruct。*/

int *pstr=(int*)&ss;

//声明了一个指向结构对象ss 的指针。但是pstr 和

//它被指向的类型ptr 是不同的。

1234567891011121314

请问怎样通过指针ptr 来访问ss 的三个成员变量?

答案:

ptr->a; //指向运算符,或者可以这们(*ptr).a,建议使用前者

ptr->b;

ptr->c;

又请问怎样通过指针pstr 来访问ss 的三个成员变量?

答案:

*pstr; //访问了ss 的成员a。

*(pstr+1); //访问了ss 的成员b。

*(pstr+2) //访问了ss 的成员c。

这样使用pstr 来访问结构成员是不正规的,为了说明为什么不正规,让我们看看怎样通过指针来访问数组的各个单元: (将结构体换成数组)

int array[3]={35,56,37};

int *pa=array;

//通过指针pa 访问数组array 的三个单元的方法是:

*pa; //访问了第0 号单元

*(pa+1); //访问了第1 号单元

*(pa+2); //访问了第2 号单元

123456

从格式上看倒是与通过指针访问结构成员的不正规方法的格式一样。

所有的C/C++编译器在排列数组的单元时,总是把各个数组单元存放在连续的存储区里,单元和单元之间没有空隙。但在存放结构对象的各个成员时,在某种编译环境下,可能会需要字对齐或双字对齐或者是别的什么对齐,需要在相邻两个成员之间加若干个"填充字节",这就导致各个成员之间可能会有若干个字节的空隙。

所以,在例十二中,即使pstr 访问到了结构对象ss 的第一个成员变量a,也不能保证(pstr+1)就一定能访问到结构成员b。因为成员a 和成员b 之间可能会有若干填充字节,说不定*(pstr+1)就正好访问到了这些填充字节呢。这也证明了指针的灵活性。要是你的目的就是想看看各个结构成员之间到底有没有填充字节,嘿,这倒是个不错的方法。

不过指针访问结构成员的正确方法应该是象例十二中使用指针ptr 的方法。

3. 数组指针

首先看一下数组与指针的区别

(1)数组也是指针

其实数组也相当与指针(数组名其实是指针常量,指向数组的第一个数据,并且不能不允许进行运算等操作)

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);

1234

上例中,一般而言数组名array 代表数组本身,类型是int[10],但如果把array 看做指针的话,它指向数组的第0 个单元,类型是int* 所指向的类型是数组单元的类型即int。因此array 等于0 就一点也不奇怪了。同理,array+3 是一个指向数组第3 个单元的指针,所以(array+3)等于3。其它依此类推

(2)指针数组

大家接触过整形数组,字符型数组.那么指针数组就是数组里面装的指针(也就是地址)

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

int *ptr[2];

if(a[0][0]>a[0]][1])

ptr[0]=&a[0][0]

1234

int *ptr[2];定义了一个指针数组,大小为2,即存储两个int型的指针. 比较二维数组元素大小,把较大的地址存储到指针数组的第一个位置. 如果后面要访问就是 *ptr[0] 等价于a[0][0]

再看一个例子

char *str[3]={

"Hello,thisisasample!",

"Hi,goodmorning.",

"Helloworld"

};

char s[80];

strcpy(s,str[0]); //也可写成strcpy(s,*str);

strcpy(s,str[1]); //也可写成strcpy(s,*(str+1));

strcpy(s,str[2]); //也可写成strcpy(s,*(str+2));

12345678910

上例中,str 是一个三单元的数组,该数组的每个单元都是一个指针,这些指针各指向一个字符串。把指针数组名str 当作一个指针的话,它指向数组的第0 号单元,它的类型是char **,它指向的类型是char 。

str 也是一个指针,它的类型是char ,它所指向的类型是char,它指向的地址是字符串"Hello,thisisasample!"的第一个字符的地址,即’H’的地址。注意:字符串相当于是一个数组,在内存中以数组的形式储存,只不过字符串是一个数组常量,内容不可改变,且只能是右值.如果看成指针的话,他即是常量指针,也是指针常量.

str+1 也是一个指针,它指向数组的第1 号单元,它的类型是char,它指向的类型是char。

(str+1)也是一个指针,它的类型是char,它所指向的类型是char,它指向"Hi,goodmorning."的第一个字符’H’

(3)行指针,列指针

–行指针

行指针和列指针是针对二维数组来说的.

二维数组可以看成是由若干个一位数组构成的

int a[3][4]={0};//定义一个二维数组初始化为0

这里a[0]可以看成是由元素a[0][0],a[0][1],a[0][2],a[0][3],这四个整形元素组成的一维数组的数组名(意思就是我们把a[0]想象成一位数组的数组名)这里a[0]代表的是一维数组第一个元素的a[0][0]的地址,a[0]+1代表元素a[0][1]元素的地址;

所以二维数组特殊的地方在于a是一个地址,a[0],a[1].a[2]同样是地址.

a代表的是这个二维数组的第一个元素a[0][0]的地址,而a[0]代表的是二维数组第0行的地址,a[1]代表的是第一行的地址.

大家可以把二维数组的行地址理解成一个宾馆的楼层号,a[1]代表第一层.然后我们要具体访问哪个房间,只需要房间在这一层的号数即可.

那么怎么表示行指针呢?

int (*p)[4]. 定义了一个指向含有4个元素的一维整形数组的指针变量,这里p就是行指针. int 代表行指针所指向的一位数组的类型,[4]代表数组的大小;

那么怎么访问?

12345

这里也可以写成cout<<*(p+2); 这里p就相当于A[0],即每一行的行地址,由于每一行也是一个一位数组,所以可以像一位数组那样进行加减;

–列指针

int *p=A[0],这里p就是列指针,注意和行指针p=&A[0]的区别,行指针存储的是每一行的地址,列指针存储的是每一行中元素的号地址

p=A[0]<–>p=&A[0][0]<—>p=*A

定义了列指针p后,为了能通过p引用mn的二维数组(m行n列),可通过(p+i×n+j)来访问

比如*(p+1n+2)就相当于访问第二行的第二列的数字

in 表示跳过多少行

+j 表示在每一行中的哪一号

4.二级指针与指针作形参

(1)二级指针

既然有指向变量的指针,那么也有指向指针的指针,我们俗称为二级指针,有二级指针当然又有三级,四级,多级指针,但我们用的较多的是二级指针,所以这里着重讲的是二级指针.

指向指针的指针?,因为我们提到指针变量也是一个变量,它在内存中也有地址,所以我们可以用另外一个指针来保存这个指针变量的地址,具体如下

int a=5;
int *p=&a;
int **ptr=&p;

我们这里p是一个一级指针,p指向a,然后又定义了一个ptr,大家可以看到这个ptr前有两个**号,没错,这个ptr就是二级指针,表示
他存储的就是p指针的地址.不清楚?我们来看图

7eb742285f4f51cd1cd29132f87f7fdb.png

我们知道指针是有地址的,比如此时p指针的地址是0x1078,那我们int **ptr=&p;就是把p的地址0x1078给ptr保存,以后我们要修改p的值,就可以通过间接访问ptr来实现.

比如我们要修改指针p的值,那我们可以

int *s=NULL;

*ptr=&s;

//就相当于p=&s;

123

(2)指针作函数的形式参数

有时候,我们的函数的形式参数不一定就是传入整形的变量,或char型的字符等,有时我们会传入地址(这里传地址有几点好处,打家可以看我上一篇文章)

举例如下:

大家看一下这两个例子,哪个是正确,哪个是错误的?

(1)

#include <iostream>

using namespace std;

void GetMemory(char *p, int num)

{

p = (char*)malloc(sizeof(char)*num);

}

int main()

{

char *s = NULL;

GetMemory(&s, 100);

strcpy(s, "hello");

printf(s);

return 0;

}

(2)

#include <iostream>

using namespace std;

void GetMemory(char **p, int num)

{

*p = (char*)malloc(sizeof(char)*num);

}

void main()

{

char *s = NULL;

GetMemory(&s, 100);

strcpy(s, "hellon");

printf(s);

}

这里(1)是错误的!!!为什么?

GetMemory是调用malloc申请一块内存。乍一看好像没什么问题,编译也不会报错。但是运行起来,程序直接奔溃。 其实有了上面的分析就可以知道,我们要间接改变一个变量或指针的值,我们得把这个变量或指针的地址给它,然而main函数中我们传入的不是指针s的地址,然而我们的Getmemory的形式参数char **p必须接收指针的地址,所以你这里牛头不对马嘴,所以会程序会崩掉!!

那我们正确的写法是如(2)所示,传入指针s的地址,即Getmemory(&s),这样形参p保存的就是指针s的地址了,然后我们就可以在Getmemory中通过指针p来间接对s进行操作了.

注意,我们在用指针p时,前面要加上一个*,为什么,因为p是二级指针,我们首先要通过解引得到指针s,相当于这里*p就是指针s

5.指针与引用

(1)指针与引用的关系?

引用是c语言中不含有,是c++中的的操作符,用&表示,注意这和取地址符虽然长得一样,但是在不同的地方有各自不同的用处,就好比世界上两个同名的人,虽然名字一样,但发挥的功能不同.

引用怎么用呢?

int a=10;

int c=2;

int &b=a;

b=20;

1234

解释一下 int &b=a是什么意思,就是定义了一个b,把a的地址给它,前面提到引用实际上是一个指针常量,所以这里在底层代码中,是这样表示的 int * const b=&a;

但是人嘛毕竟是要偷懒的是吧!!所以为了我们使用方便,我们写成int &b=a而不是int &b=&a,这样以后就可以直接用b进行操作,

比如b=20 而不是 *b=20我们就是对b进行修改,那么有小伙伴会问?

改了b以后,a的值会不会变呢?答案是肯定要变的因为引用其实也是指针,它指向你引用的对象,所以你对b修改,其实是也在对a修改

还有就是我们不能写了int &b=a后,又写int &b=c,这是不允许的,因为我们讲过,引用是指针常量,不能脚踏两只船!!!

(2)引用能简化形式参数

先看一个简单例子

用了引用

#include <iostream>

using namespace std;

void swap(int&, int&);

int main(){

int a = 3, b = 4;

cout << "a=" << a << ", b=" << b << endl;

swap(a, b);

cout << "a=" << a << ", b=" << b << endl;

system("pause");

return 0;

}

void swap(int &x, int &y){

int t = x;

x = y;

y = t;

}

其实swap函数底层是这样表示的

void swap(int *const x,int *const y){

int t=*x;

*x=*y;

*y=t;

}

12345

不用引用

#include <iostream>

using namespace std;

void swap(int*, int*);

int main(){

int a = 3, b = 4;

cout << "a=" << a << ", b=" << b << endl;

swap(&a, &b);

cout << "a=" << a << ", b=" << b << endl;

system("pause");

return 0;

}

void swap(int *x, int *y){

int t = *x;

*x = *y;

*y = t;

}

aa4ae7360b1c36938a043e3140115925.png

两种效果一样

再看一个例子

#include <iostream>

using namespace std;

void GetMemory(char **p, int num)

{

*p = (char*)malloc(sizeof(char)*num);

}

void main()

{

char *s = NULL;

GetMemory(&s, 100);

strcpy(s, "hellon");

printf(s);

}

123456789101112131415

这里形式参数是用的char **p,这里可以用引用来简化,我们可以写成char *&p.

然后 Getmemory(&s,100)写成Getmemory(s,100)

什么意思?清楚点!!!意思写成如下代码

#include <iostream>

using namespace std;

void GetMemory(char *&p, int num)

{

p = (char*)malloc(sizeof(char)*num);

}

void main()

{

char *s = NULL;

GetMemory(s, 100);

strcpy(s, "hellon");

printf(s);

}

我们一步一步来?

首先我们传入s,不是&s.因为在引用里面,我们写的s其实上是&s,只不过在编译的时候编译器偷偷给你换掉了.所以我们用引用&p来接收s,根据上面的分析,我们知道引用可以直接用变量名表示你引用的对象,所以&p就相当于是s,那好我们接受了以后要声明这是什么类型的引用呀!是引用的整形指针,还是字符型指针,所以我们在前面加上char *,表示引用字符型指针.所以p就相当于s了,大家认为这样写是不是比写两个**号要简单些呢?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值