(C/C++学习笔记) 十二. 指针

时间:2023-03-08 20:25:09
(C/C++学习笔记) 十二. 指针

十二. 指针

● 基本概念

位系统下为4字节(8位十六进制数),在64位系统下为8字节(16位十六进制数)

进制表示的, 内存地址不占用内存空间

指针本身是一种数据类型, 它可以指向int, char, float double等不同类型的变量, 仔细揣摩下图:

(C/C++学习笔记) 十二. 指针

的时候(在CPU为32位的情况下),要根据指针的类型来前进相应字节数。比如int类型的指针前进四个字节,char型的指针前进一个字节,void类型指针前进一个字节……

位十六进制数占多少字节,比如0x12345678这是多少字节?

位十六进制数占4字节。这也是指针变量的占用4个字节的原因。

注意如下英语表达:

int *p;

p is the address of an int

(dereference p is an integer)

int v;

p = &v;

p stores the address of v

int x, y, *p;

p = &x; /* p gets the address of x */

y = *p; /* y gets the value point to by p*/

y = *(&x); /* same as y = x*/

C Pointer is used to allocate memory dynamically i.e. at run time.

//定义指针变量的几种形式:

//形式1:

int a = 10; //定义一个的int型变量a,a赋值为10

int *p;

p = &a; //定义一个int型指针变量p,p指向int型变量a并赋值为(be set to the value of)int型变量a的地址, 即&a; 这一语句表示的是对指针进行初始化

//形式2:

int a = 10;

int *p = &a; //同上,也就是说int *p等价于p

//形式3:

int *p, i = 10, j;    //如果是int *p, *i表示定义两个指针变量p和i

p = &i; // 以上是定义一个指针变量

j = *p; // 把p所指的内存单元,即变量"i"的内容(整数10)赋予变量j;*p代表p所指的变量i, 其内容是数据

// 上述的定义和语句等价于:

int *p; // 此时,p还是一个野指针

int i = 10;

int j;

p = &i; // 此时,p不再是一个野指针

j = *p;

//判断正误:

int *p;

*p = &a; //语法错误,因为int *p表示定义一个指针变量;*p代表p所指的变量a的具体数据

//—————————————————————————

int *p;

p = 10; //语法错误,因为不可以把一个数赋值给一个指针变量

//—————————————————————————

int *p = 10; // 语法错误,int *p后面只能是地址

//—————————————————————————

int *p = a; // 这里分两种情况:如果a是一个变量,那么语法错误;如果我们先定义了一个名为a的数组,例如:int a[10];,那么该语句合法,因为一个数组名就是一个指针常量(指针常量就是一个地址值),这里"常量指针"是指"指针本身是常量,而非指针指向的对象是常量

//—————————————————————————

char *p = "world"; // 语法正确,因为字符串"world"就相当于一个数组,指针变量存储的是字符'w'的地址。

//—————————————————————————

int **pp = &p; //语法正确,表示取指针地址--定义一个int型指针变量pp, pp指向指针变量p并赋值为指针变量p的地址

#include <stdio.h>

,表示这个main函数停止,而执行下面一个main函数

{

int a = 10;

int *p = &a;

int b = *p; //定义一个的int型变量b;"*指针变量"是引用指针变量的形式,其含义是引用指针变量所指向的值

printf("%d\n", b);

return 0;

}

// 结果为10

int main()

{

int a = 10;    //也可以是int a; 此时变量a没有初始化, 里面是垃圾值

int *p = &a;

*p = 20; //通过指针变量p间接地修改它指向的变量的值

printf("%d\n", a);

return 0;

}

● 二级指针/二重指针/指向指针的指针

三重及以上的指针统称为多重指针。

int **p, *s, k = 20;

s = &k;

p = &s;

// *s代表存储单元k,*p代表存储单元s,因此**p也代表存储单元k;p→s→k(20)

例如:

int **pp;

定义了一个二级指针变量,等效于下列定义方式:

typedef int * P;

P *p;

二级指针常用来指向一个指针数组。

例①:

int a[2][3]={1,2,3,4,5,6}; //声明并初始化二维数组

int *p_a[3]; //声明整型指针数组

p_a[0]=a[0]; //初始化指针数组元素

p_a[1]=a[1];

int **pp;

pp=p_a;

注意:在使用二级指针时经常容易犯两类错误。

(1)类型不匹配

例如:

pp=a; //错误,因为pp是一个int**型变量,a是一个int[2][3]型的地址

pp=&a; //错误,pp是一个int**型变量,&a是一个(*)int[2][3]型的地址

pp=&a[0]; //错误,pp是一个int**型变量,&a[0]是一个(*)int[3]型的地址

(2)指针没有逐级初始化

例如:

int i=3;

int **p2;

*p2=&i;

**p2=5;

cout<<**p2;

虽然上述程序段编译、连接均没有错误,但运行时出错。其原因在于int **p2;只是定义了一个指针变量,变量中的内容(地址)是一个无意义的地址,而*p2=&i是对无意义的内存单元赋值,这样是错误与危险的。正确的作法是从第一级指针开始,逐级初始化。

逐级初始化多级指针

例:

int i=3;

int **p2;

int *p1;

p1=&i; //初始化一级指针

p2=&p1; //初始化一级指针

**p2=5; //通过指针给变量i赋值

cout<<**p2; //结果为5

上述两个二级指针在内存中的分布下图所示。

(C/C++学习笔记) 十二. 指针

● 指针的简单例子

#include <iostream.h>

#include <stdio.h>

int main()

{

int a=1;

printf("&a=%p\n",&a);

int *p=&a;    //输出变量a的地址

printf("p=%p\n",p);    //同上

printf("p=%x\n",p);    //同上

printf("*p=%d\n",*p);    //*p相当于它指向的整型变量a, 输出变量a的值

printf("&p=%p\n",&p);    //输出指针变量p的地址

int **pp=&p;        //二级指针, 相当于int *(*pp)=&p;(指针运算符是自右向左结合的)

printf("pp=%p\n",pp);    //输出指针变量p的地址

printf("*pp=%p\n",*pp);    //输出指针变量p的内容, *pp相当于(它指向的指针变量 np)指向(vp)的(relative clause)整型变量的地址

printf("**pp=%d\n",**pp);    //输出指针变量pp最终指向的变量a的值

printf("&pp=%p\n",&pp);        //输出指针变量pp的值

return 0;

}

(C/C++学习笔记) 十二. 指针

● &和*两个运算符

&----address-of operator (取地址运算符)( (reference)):to initialize the pointer variable with the address of a variable that you've already declared

*----①pointer operator (指针运算符)( dereference): to declare that the variable after it is a unsign-int-type variable which stores the address of another variable

②indirection operator (间接访问运算符) : to access the value of the variable that the pointer is pointing to

单目运算符"*"必须出现在运算对象的左边,其运算对象是存放地址的指针变量或地址

单目运算符"&"必须出现在运算对象的左边,其运算对象是任何已经被声明的变量(包括在指针变量)

&*和*&的区别

/*

int a;

int *p=a;

&*p和*&a的区别: &和*的优先级相同,结合性为自右向左, 因此:

① &*p先进行*p运算, 再进行&*p运算, *p相当于它p指向的变量a, 所以 &*p相当于取变量a的地址;

② *&a先进行&a运算, 再进行*&a运算, &a相当于p, 所以*&a相当于变量a

*/

#include<stdio.h>

main()

{

long i;

long *p;

printf("please input the number:\n");

scanf("%ld",&i);

p=&i;

printf("the result1 is: %p\n",&*p);                     /*输出变量i的地址*/

printf("the result2 is: %p\n",&i);                     /*输出变量i的地址*/

}

(C/C++学习笔记) 十二. 指针

 

● 有关*p

*p: ① 作为左值, 相当于它指向的变量, 向指针变量p所指的变量赋值;

② 作为右值, 相当于它指向的变量的值, 从指针变量p所指的变量取值

如果*p 前面有int, char等数据类型, 那就一起构成了指向int型变量的指针,指向char型变量的指针等. (相比整型指针变量/字符型指针变量更标准的读法)

#include <iostream.h>

int main()

{

int i=1,j;

int *p=&i;

*p=2;

j=*p;

cout<<i<<endl;

cout<<j<<endl;

return 0;

}

(C/C++学习笔记) 十二. 指针

#include<stdio.h>

main()

{

long i;

long *p;

printf("please input the number:\n");

scanf("%ld",&i);

p=&i;

printf("the result1 is: %ld\n",*&i);                 /*输出变量i的值*/

printf("the result2 is: %ld\n",i);                    /*输入变量i的值*/

printf("the result3 is: %ld\n",*p);                 /*使用指针形式输出I的值*/

}

(C/C++学习笔记) 十二. 指针

● 野指针, 垂悬指针/迷途指针, 空指针

① 野指针 (wild pointer) : 定以后没有初始化的指针, 例如:

int *p;

或者没有指向任何有效地址的指针也叫野指针, 例如:

int *p=10;

② 悬垂指针/迷途指针 (Dangling pointer): 当所指向的对空间/对象被释放或者收回,但是对该指针没有作任何的修改,以至于该指针仍旧指向已经回收的内存地址,此情况下该指针便称悬垂指针(也叫迷途指针); 此时, 应该将该指针赋值为NULL。

③ 空指针(NULL pointer)

强制转化为(void *)类型的指针), 所以程序在编译时将NULL替换成0; 0表示指向地址为0的单元(这个单元一般是不能使用的).

也就是说, 空指针事实上指向了内存的零地址,但是操作系统并没有使用零地址附近的空间,这样做是为了使指针指向一个已知的地方防止成为野指针. 例如:

int *pointer = NULL;

④ 空类型指针(void pointer)

定义一个指针变量,但不指定它指向具体哪种数据类型。不同类型的指针不能互相赋值, 例如:

char *p1="helloworld";

int *p2=p1;    //错误

但是, 空类型指针可以赋值给任何类型的指针, 例如:

void *p1="helloworld";

int *p2=p1;    //不推荐的做法, 推荐的做法是进行指针类型的强制转换, 例如:

char *p1="helloworld";

int *p2=p1;

int *p2=(int *)p1;

然而, 指针的强制转换没有意义, 因为我们只是改变了一个变量的地址, 不能强制转换指针指向的变量的类型

另外, 不允许对void型指针做加减法运算.

● 常量指针/指针常量

常量指针(指向常量的指针变量)&指针常量(常量性质的指针 / 指针常量指向变量)

关键: *是一个单目运算符,它的运算对象在其右边(不运算其左边的对象)

常量(的)指针 constant pointer(指针指向的是常量, 指向的不可变, 但它本身可变)

const char *p;        //从右向左读, * 读作a pointer to

p is a pointer to const char;

等价于:

char const * p;     //同上因为C++里面没有const*的运算符,所以const只能属于前面的类型。

//C++标准规定,const关键字放在类型或变量名之前等价的。

const int n=5; //same as below

int const m=10;

  • 数组名就是常量指针:

int a[10];

int *p=a;

a++;        //不合法, 因为a是指针p指向的常量, 因为a是地址值, 当然是常量

p++;

其实这就像我们经常看到的char *p="ABC", 指针p指向的也是字符A的地址

指针(型)常量(指向的是变量, 指向的int变量可变, 但它本身不可变)

char *const p;

p is a const pointer to char

△似乎下面这个声明本身就是错的, 编译器不通过

指针(型)常量(如果指向的是常量, 指向的不可变, 它本身也不可变)

char *const *p;

p is a const pointer to a const char

/*看变量声明的时候应该从右至左看,以如下为例:

char *const *p

个符号const,它修饰的是*p,*p表示p指向的内容,所以,p指向的内容是常量,下一个符号是*,这就表示该常量为指针,然后是char,就表示指向的内容是char*/

口诀:

const(*号)左边放,我是指针变量指向常量;

const(*号)右边放,我是指针常量指向变量;

const(*号)两边放,我是指针常量指向常量;

指针变量能改指向,指针常量不能转向!

要是全都变成常量,锁死了,我不能转向,你也甭想变样!

//常量指针的案例

#include<stdio.h>

int main()

{

int a=1, b=2;

int const *p=&a;

//*p=3;

//printf("%d\n",a);    //出错, 提示"l-value specifies const object"

p=&b;        //将b的地址赋给p

return 0;

}

_____________________________________________________

//指针常量的案例

#include<stdio.h>

int main()

{

int a=1, b=2;

int *const p=&a;

*p=3;

printf("%d\n",a);    //a的值变为3

printf("%d\n",*p);    //结果为3

//p=&b;

//printf("%d\n",*p);    //出错,因为指针p的值是个常量,不可改变,编译器会提示l-value specifies const object

return 0;

}

// 如果是    char *const p= "hello";*p='b'; 编译通过, 但会运行出错, 因为"hello"是字符串常量

// 如果是    char *const p= 'h';*p='b'; 编译不通过, 提示: cannot convert from 'const char' to 'char *const (字符指针常量)'

________________________________________________________

#include<stdio.h>

int main()

{

int a=1, b=2;

int *const *p=&a;        //出错, 提示cannot convert from 'int *' to 'int *const * '

//*p=3;

//printf("%d\n",a);        //出错

//p=&b;

//printf("%d\n",*p);    //出错

return 0;

}

● char p[]="abc123ABC"; 以及char *p="abc123ABC";

  • char p[]="abc123ABC"; 以及char *p="abc123ABC";的区别:

const在前,定义为常量指针;*在前,定义为指针常量。

  • const 在前,定义为常量指针;*在前,定义为指针常量

char p[]是一个数组,分配了内存, "abc123ABC" 复制到了该内存里面,这个内存可读可写, 保存在栈空间数组里

char *p是一个指针,没有分配内存(除了给这个指针变量本身分配的四个字节), 指针p指向的是字符串常量"abc123ABC", 其地址是第一个字符a的地址(也可以只指向一个字符), 保存在静态数据区. 另外, 用p=这样的赋值是可以的,也就是a指向了另外的地址。

※char *p 在C++03中不推荐使用(deprecated), 而应该写成const char *p = "abc123ABC ", 它等同于char const * p= "abc123ABC ", 又等价于:

char *p;

p="abc123ABC";

  • 总之,死记:字符串常量代表的是一个地址值, 字符串常量和数组一样,它也是当成指针来对待的,它的值就是字符串首个字符的地址.
  • 另外, 除了char buffer[20]= "hello ";和strcpy(p, "hello ");这两个情况外,程序中出现的字符串都应该被视为字符串常量.

#include <stdio.h>

int main()

{

char *p = "abc";

printf("%p\n", p);

printf("%p\n", "abc");

printf("%p\n", &(p[0]));    //p[0]代表字符'a'

printf("%p\n", &(p[1]));    //p[1]代表字符'b'

printf("%p\n", &(p[2]));    //p[2]代表字符'c'

printf("%c\n", p[3]);    //p[3]代表转义字符空字符'\0'(NULL), 其ASCII码为0

printf("%p\n", &(p[3]));

return 0;

}

(C/C++学习笔记) 十二. 指针

#include <stdio.h>

int main()

{

char *p = "abc";    //指针变量p在栈区,字符串"abc"在常量区,即"abc"时字符串常量

*p = 'd'; //*p代表存储a的内存空间,但这个内存空间存储的值不可改变

//*p相当于p[0], 因此p[0] = 'd';同样会出现编译通过但结果出错的情况

printf("%s\n", p);

return 0;

}

//编译、连接都通过,但是运行出现"该内存不能为'written'"的应用程序错误, 因为"abc"是字符串常量;

//另外注意,如果char *p = "a"; 同样的,编译通过,但程序会出错。不能写成char *p = 'a'; 因为字符常量不能代表一个地址,编译器会提示:cannot convert from 'const char' to 'char *'

//正确的程序应该是:

#include <stdio.h>

int main()

{

char p[] = "abc";    //字符数组名p在栈区,字符串"abc"也在栈区,即"abc"不是字符串常量

printf("%s\n", p);

*p = 'd';    //*p也代表存储a的内存空间,这个内存空间存储的值可以改变

printf("%s\n", p);

return 0;

}

//注意如下相似案例:

#include <stdio.h>

int main()

{

char *p = "abc";

p = 'd';    //编译不通过, 提示cannot convert from 'const char' to 'char *'

printf("%s\n", p);

return 0;

}

#include <stdio.h>

int main()

{

char *p = "abc";

p = "def";    //编译通过,字符型常量是一个没有命名的常量, 这里将指针变量p指向了另一个字符串"def"

printf("%s\n", p);    //仔细琢磨一下, p是一个指针, 它的值就是一个字符串常量, 而字符串常量就相当于一个地址值

return 0;

}

(C/C++学习笔记) 十二. 指针

#include <stdio.h>

int main()

{

char p[10] = "abc";

printf("%s\n", p);

*p = "def";

printf("%s\n", p);

return 0;

}

//编译不通过,提示:cannot convert from 'char [4]' to 'char'

图解:

char a[] = "hello";

char *p = "world";

(C/C++学习笔记) 十二. 指针

● 指针与一维数组

指针与一维数组:

//定义数组元素的指针

int a[10];    //也可以对其初始化, 如int a[10]={0};

int *p;        //上两句等价于:int *p, a[10];

p=&a[0];        //上两句等价于int *p=a; 或 int *p=&a[0]; 不能是上两句等价于int *p=&a

一维数组的结构是线性的, 如图:

(C/C++学习笔记) 十二. 指针

对于一维数组及其指针:

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

int *p;

p=a;    //a是指向数组起始元素的指针常量, 是右值, 不能

一维数组首地址

(一维数组第一个元素的地址)

一维数组第n个元素的地址

开始计数)

a

a+n, 如a+4

&a

/

&a[0]

&a[n], 如&a[4]

&(*(a+0)), 即&(*(a))

&(*(a+n)), 如&(*(a+4))

p, 即&(*p)

&(*p+4), 如&(*(p+4))

● 不能用&(a+4), 否则提示error C2102: '&' requires l-value(有地址的值), 即&的运算只能是已经被声明的变量;

*(a+4)之所以正确, 是因为*的运算对象除了可以是指针变量, 还可以是地址

个元素的值

一维数组第n个元素的地址

开始计数)

a[0]

a[0+n], 如a[4]

*(&a[0])

*(&a[0+n]), 如*(&a[4])

*(a+0), 即*(a)

*(a+n), 如*(a+4)

*p

*(p+4), 如*(p+4)

#include<stdio.h>

int main()

{

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

int *p;

p=a;

printf("%d\n",a);

printf("%d\n",a+2);

printf("%d\n",&a);

//printf("%d\n",&(a+2));    //不合法, 因为&的运算对象只能是已经被声明的变量

printf("%d\n",p);

printf("%d\n",p+2);        //加的是两个sizeof(int), 而不是两个sizeof(p)

}

(C/C++学习笔记) 十二. 指针

//通过指针变量获取一维数组元素的地址和值(简单案例)

#include <stdio.h>

void main()

{

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

int *p;

p=a;

printf("%p\n", a);

printf("%p\n", &a[0]);

printf("%p\n", &(*(a)));

printf("%p\n", &(*p));

printf("-----------\n");

printf("%p\n", a+4);

printf("%p\n", &a[4]);

printf("%p\n", &(*(a+4)));

printf("%p\n", &(*(p+4)));

printf("-----------\n");

printf("%d\n", a[0]);    //下标法

printf("%d\n", *(&a[0]));    //地址法

printf("%d\n", *(a+0));        //地址法, *(a+0)相当于*(a)

printf("%d\n", *p);        //指针法

printf("-----------\n");

printf("%d\n", a[4]);    //下标法

printf("%d\n", *(&a[4]));    //地址法

printf("%d\n", *(a+4));        //地址法

printf("%d\n", *(p+4));        //指针法

}

(C/C++学习笔记) 十二. 指针

(C/C++学习笔记) 十二. 指针

//通过指针变量获取一维数组的元素(复杂案例)

#include <iostream>

using namespace std;

void main()

{

int i,a[10];

int *p;

个元素赋值

for(i=0;i<10;i++)

a[i]=i;

的元素输出到显示设备

p=&a[0];    //即p=a;

for(i=0;i<10;i++,p++)

cout << *p << endl;        //也可将后两句写成for(i=0; i<10; i++); cout<<*(a+1)<<endl;    //a+i表示数组a中的第i个元素的地址

}

(C/C++学习笔记) 十二. 指针

● 指针与二维数组

二维数组用矩阵方式存储元素, 在内存中仍然是线性结构:

(C/C++学习笔记) 十二. 指针(C/C++学习笔记) 十二. 指针

对于二维数组的指针及其元素的值:

int a[3][2]={{1,2},{11,12},{21, 22}};

int *p;

p=*a;

二维数组首地址

(二维数组第一个元素的地址)

具体含义

数据类型

二维数组第n个元素的地址

a

个一维数组指针的地址

int (*)[2]

*(a+2)+1

&a[0]

个一维数组指针的地址

int (*)[3][2]

*(pp+2)+1

&a

整个二维数组指针的地址

int (*)[2]

/

&a[0][0], 等同于:

&(a[0][0]), &(*(a[0]+0)),

&(*(*(a+0)+0))

行第0列数组元素的地址

int *

&a[2][1]

a[0]

int *

/

*a

行第0列数组元素的地址

int *

&a[0][0]+2*2+1

*a+2*2+1

p

行第0列数组元素的指针

int *

p+5

//通过指针变量获取二维数组的元素(简单案例)

#include <stdio.h>

void main()

{

int a[3][2]={{1,2},{11,12},{21, 22}};

int *p;

p=*a;    //或p=a[0]; 或p=&a[0][0];

个元素的一维数组

int (*ppp)[3][2]=&a;    //定义一个int (*)[3][2]类型的二维数组(型)指针

printf("%p\n", a);

printf("%p\n", &a[0]);

printf("%p\n", &a);

printf("%p\n", &a[0][0]); //最易理解, 常用

printf("%p\n", a[0]);

printf("%p\n", *a);

printf("%p\n", p);

printf("-----------\n");

printf("%p\n", *(a+2)+1);    //使用一维数组(型)地址的首地址

printf("%p\n", *(pp+2)+1);    //使用一维数组(型)指针指向的首地址

//不能使用二维数组(型)指针引用数组元素

printf("%p\n", &a[2][1]);    //使用第0行第0列数组元素的地址; //最易理解, 常用

printf("%p\n", &a[0][0]+2*2+1);    //使用第0行第0列数组元素的地址

printf("%p\n", *a+2*2+1);    //使用第0行第0列数组元素的地址

printf("%p\n", p+5);        //使用第0行第0列数组元素的指针

printf("-----------\n");

printf("%d\n", *(*a+0)+0);    //使用一维数组(型)地址

printf("%d\n", *(*a));        //使用第0行第0列数组元素的地址

printf("%d\n", *(*pp+0)+0);    //相当于printf("%d\n", *(*pp)

//不能使用二维数组(型)指针来取数组元素的值

printf("%d\n", a[0][0]);    //最易理解, 常用

printf("%d\n", *(a[0]));

printf("%d\n", *p);

printf("-----------\n");

printf("%d\n", *(*(a+2)+1));

printf("%d\n", *(*(pp+2)+1));

printf("%d\n", a[2][1]);    //最易理解, 常用

printf("%d\n", *(p+4));

//不能用a[5], 或*(&a[5]))取值

}

(C/C++学习笔记) 十二. 指针

//通过指针变量获取二维数组的元素(复杂案例)

#include<iostream>

using namespace std;

void main()

{

int i,j;

int a[4][3]={{1,2,3},{4,5,6},{7,8,9},{10,11,12}};

cout << "the array is: " << endl;

for(i=0;i<4;i++)//行

{

for(j=0;j<3;j++)//列

cout <<*(*(a+i)+j) << endl;

}

}

(C/C++学习笔记) 十二. 指针

● 熟记下表

int a[3][5]={0};

a

二维数组名称,数组首地址

int (*p)[5]=&a;

定义一个指向int [5]类型的指针变量p并初始化, p指向一个包含5个元素的一维数组

a[0], *(a + 0), *a

0行,0列元素地址

a + 1

第1行首地址

a[1], *(a + 1)

第1行,0列元素地址

a[1] + 2, *(a + 1) + 2, &a[1][2]

第1行,2列元素地址

*(a[1] + 2), *(*(a + 1) + 2), a[1][2]

第1行,2列元素的值

● 字符串与指针

//赋值字符串str1并命名为str2

#include<stdio.h>

main()

{

char str1[]="you are beautiful",str2[30],*p1,*p2;

p1=str1;

p2=str2;

while(*p1!='\0')

{

*p2=*p1;

p1++;                                /*指针移动*/

p2++;

}

*p2='\0';                                     /*在字符串的末尾加结束符*/

printf("Now the string2 is:\n");

puts(str1);                                 /*输出字符串*/

}

(C/C++学习笔记) 十二. 指针

(C/C++学习笔记) 十二. 指针

● 数组指针&指针数组

数组指针:a pointer to an array,即指向数组的指针

指针数组(一维的较常见):array of pointers,即用于存储指针的数组,也就是数组元素都是指针

([]的优先级大于*)

int (*p)[4];     //数组指针, p is a pointer to an integer array of size 4

int *p[4];     //指针数组, Array of 4 pointers to int

int

int (*p[8])[5]; //p is an array of pointers to integer array of size 5

#include<stdio.h>

main()

{

int a=2;

int b=3;

int *p[4];

p[0]=&a; //不能是*p[0]=&a;

p[1]=&b;

int **pp=p;    //p就是一个数组名, 代表第一个元素的地址; pp是指向指针数组p的首元素的指针(指向指针的指针)

//也可以写成int *(*pp)=p, 因为*的运算方向是从左至右, 故可以省略括号

printf("%d\n", *(p[0]));

printf("%p\n",&a);        //变量a的地址

个元素的值是变量a的地址

个元素的地址, 等同于printf("%p\n",&p[0]);

printf("%p\n",p);        //同上

printf("%p\n",pp);        //pp这个指针变量里面存储的是数组p的首地址

printf("%p\n",*pp);        //pp这个指针变量指向的另一个指针变量的值是变量a的地址

//printf("%p\n",*(pp)); //pp存储的内容是指针数组首元素的地址, 所以

}

(C/C++学习笔记) 十二. 指针

//指针数组的指针元素指向不同的整形变量

#include<stdio.h>

int main()

{

int a=1;

int b=2;

int c=3;

int *tab[3]={&a,&b,&c};

int i;

for (i=0; i<3;i++)

{

printf("%d\n", *(tab[i]));

}

return 0;

}

(C/C++学习笔记) 十二. 指针

//指针数组的指针元素指向不同的数组

#include<stdio.h>

int main()

{

int t1[4]={0,1,2,3};

int t2[4]={4,5,6,7};

int t3[4]={8,9,10,11};

int *tab[3]={t1,t2,t3};

int i,j;

for (i=0; i<3;i++)

{

for (j=0; j<4; j++)

{

printf("%d\n", *(tab[i]+j));

}

}

return 0;

}

(C/C++学习笔记) 十二. 指针

//指针数组的指针元素指向不同的字符串

#include <iostream>

using namespace std;

const int MAX = 4;

int main ()

{

char *names[MAX] = {        //也可以不用宏定义MAX的值, 在方括号内什么数字也不填

"Zara Ali",

"Hina Ali",

"Nuha Ali",

"Sara Ali",

};

for (int i = 0; i < MAX; i++)

{

cout << "Value of names[" << i << "] = ";

cout << names[i] << endl;

}

return 0;

}

(C/C++学习笔记) 十二. 指针

● 指针数组作main()函数参数

int main(int argc, char *argv[])    //等价于int main(int argc, char **argv)

//argc:参数个数(argument count); argv: 参数向量(argument vector);

这些参数不能在程序中得到, 因为main()函数是操作系统调用的, 所以实参只能由操作系统给出.

例如在DOS系统的操作命令状态下, 在命令行中包括了命令名和需要传给mian()函数的参数, 参数个数包括命令.

命令行的一般形式为:

参数2...参数n

例如:

d:\debug\1.exe hello world

#include <stdio.h>

#include <stdlib.h>

int main(int argc, char **argv)

{

int i;

printf("共有%d个参数\n", argc);

for(i=0; i<argc; i++)

{

printf("第%d个参数是%s\n",i+1, argv[i]);

}

system("PAUSE");

return 0;

}

//得到可执行文件E:\test\Debug\test.exe

(C/C++学习笔记) 十二. 指针

按Win键+R打开运行,在打开的运行中输入CMD打开命令行程序cmd.exe

(C/C++学习笔记) 十二. 指针

(C/C++学习笔记) 十二. 指针

(C/C++学习笔记) 十二. 指针(C/C++学习笔记) 十二. 指针

● 指针的算术运算

对指针作算术运算有两种:

  1. 对指针加减整数, 相当于对指针指向的变量的地址作运算, 这种运算与指针指向的变量的数据类型的大小有关.
  2. 指针互减(其它运算都不行)

#include <iostream>

using namespace std;

void main()

{

int a=100;

int *p_a=&a;

printf("address:%d\n",p_a);

p_a++;

printf("address:%d\n",p_a);

char b='A';

char *p_b=&b;

printf("address:%d\n",p_b);

p_b--;

printf("address:%d\n",p_b);

int c[5]={0};

int *p_c1=&c[0];

int *p_c3=&c[3];

//两个类型相同的指针允许作减运算

printf("%d\n",p_c3-p_c1);

int *pp[5];

printf("%d\n",sizeof(pp));

}

(C/C++学习笔记) 十二. 指针

● 指针与函数

函数存放在代码区(code area), 在编译时被分配给一个入口地址, 这个地址就是函数的指针.

可以用一个指针变量指向函数, 然后通过该指针变量调用这个函数.

① 指针形参(指针变量作函数参数)

数据类型 函数名(数据类型 *pt1, 数据类型 *pt2, ...)

② 函数型指针(指向函数的指针)

数据类型 (*函数指针名)(形参表)

※回调函数: 通过函数指针而被调用的函数

  1. 指针型函数(返回指针值的函数)

数据类型 *函数名(参数表)

{

函数体

}

● 指针变量作函数参数

//指针形参(指针变量作函数参数)

#include <stdio.h>

void func(int *a)

{

(*a)++;

}

int main()

{

int a = 1;

func(&a); //通过函数的形参间接地修改实参的值, 之所以实参为地址, 是因为子函数func的形参类型为整形指针变量

printf("%d\n", a);

return 0;

}

(C/C++学习笔记) 十二. 指针

void func1(int *p)

{

p = NULL;

}

int main()

{

int a = 1;

int *p = &a;

func1(p);    //虽然p的值的&a的值相同, 但本质上这还是一个单向的值传递, 不是按址传递

// p指向a的地址还是NULL?p还是指向了a的地址

return 0;

}

voud func1(int **p)        //形参必须是二级指针, 因为下面的实参&p是取的指针的地址

{

*p = NULL;

}

int main()

{

int a = 1;

int *p = &a;

func1(&p);    //按址传递

// p指向a的地址还是NULL?指向了NULL

return 0;

}

● 函数型指针(指向函数的指针)

/*

int function(int x,int y); 声明一个函数

int (*f) (int x,int y); 声明一个函数指针

f=function; 将function函数的首地址赋给指针

*/

#include <iostream>

using namespace std;

int per(int a,int b);

void main()

{

int width=10,lenght=30,result;

int (*f)(int x,int y);

f=per;//定义函数指针

result=(*f)(width,lenght);    //f是指向avg函数的函数指针, 调用f就像调用avg函数

cout << result <<endl;

}

int per(int a,int b)

{

return (a+b)*2;

}

(C/C++学习笔记) 十二. 指针

● 指针型函数(返回指针值的函数)

#include <iostream.h>

int *per(int a,int b);

int perimeter;

void main()

{

int width=10, length=30, *result;

result=per(width,length);    //per这个指针函数被调用后返回一个地址值, 将这个地址值赋给指针变量result

cout<<per(width,length)<<endl;    //per()函数被调用后, 返回指向全局变量perimeter的指针p

cout<<result<<endl;

cout<<*result<<endl;

}

int *per(int a,int b)

{

perimeter=(a+b)*2;    //如果在子函数中定义的一个变量并且改变量将用于其它函数, 那这个变量一定要是全局变量, 如果是局部变量, 局部变量的内存空间在每次函数调用时分配,在函数执行完时释放, 并得到函数的返回地址, 就算是静态局部变量, 即使在函数执行完时释放, 内存空间没有被释放, 它的作用域也只是在函数内.

int *p= &perimeter;

return p;

}

(C/C++学习笔记) 十二. 指针