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

十二. 指针

● 基本概念

变量的地址就是指针,存放指针的变量就是指针变量(因而又叫作地址变量 address variable); 这个地址编号本身就是一个无符号的整数,32位系统下为4字节(8位十六进制数),64位系统下为8字节(16位十六进制数)

 

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

 

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

 

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

 

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

一位十六进制代表四位二进制,0x12345678转换成二进制就是0001-0010-001-0100-0101-0110-0111- 1000,而每八位二进制占一个字节,所以8位十六进制数占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型变量aa赋值为10

int *p;

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

//形式2:

int a = 10;

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

//形式3:

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

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>

 

int main01() // 这里main后面如果加了个01,表示这个main函数停止,而执行下面一个main函数

{

    int a = 10;

    int *p = &a;

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

    printf("%d ", b);

    return 0;

}

// 结果为10

 

int main()

{

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

    int *p = &a;

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

    printf("%d ", a);

    return 0;

}

// 结果为20

 

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

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

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

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

 

● 指针的简单例子

#include <iostream.h>

#include <stdio.h>

 

int main()

{

    int a=1;

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

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

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

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

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

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

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

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

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

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

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

    return 0;

}

 

&*两个运算符

&----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: ");

    scanf("%ld",&i);

    p=&i;

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

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

}

 

 

 

● 有关*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;

}

#include<stdio.h>

 

main()

{

    long i;

    long *p;

    printf("please input the number: ");

    scanf("%ld",&i);

    p=&i;

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

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

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

}

 

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

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

int *p;

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

int *p=10;

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

③ 空指针(NULL pointer)

一个指向NULL的指针,我们称之为空指针,意味着这个指针不指向任何一个变量(points to nothing; NULL"stdio.h"等头文件中被宏定义为0 (严格来说是#define NULL ((void *)0), (void *)0 就是将0强制转化为(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

先看从右至左第一个符号*,这表明变量p是指针,再看第2个符号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 ",a);    //出错, 提示"l-value specifies const object"

    printf("%d ",*p);    //结果为1

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

    printf("%d ",*p);    //结果为2

    return 0;

}

_____________________________________________________

//指针常量的案例

#include<stdio.h>

 

int main()

{

    int a=1, b=2;

    int *const p=&a;

    *p=3;

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

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

    //p=&b;

    //printf("%d ",*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 ",a);        //出错

    //p=&b;

    //printf("%d ",*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 ", p);

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

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

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

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

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

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

    return 0;

}

#include <stdio.h>

 

int main()

{

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

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

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

    printf("%s ", 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 ", p);

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

    printf("%s ", p);

    return 0;

}

 

//注意如下相似案例:

#include <stdio.h>

 

int main()

{

    char *p = "abc";

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

    printf("%s ", p);

    return 0;

}

 

#include <stdio.h>

 

int main()

{

    char *p = "abc";

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

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

    return 0;

}

 

 

#include <stdio.h>

int main()

{

    char p[10] = "abc";

    printf("%s ", p);

    *p = "def";    

    printf("%s ", p);

    return 0;

}

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

图解:

char a[] = "hello";

char *p = "world";

 

● 指针与一维数组

指针与一维数组:

//定义数组元素的指针

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

 

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

 

对于一维数组及其指针:

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

int *p;

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

一维数组首地址

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

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

(从0开始计数)

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)之所以正确, 是因为*的运算对象除了可以是指针变量, 还可以是地址

 

一维数组第0个元素的值

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

(从0开始计数)

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 ",a);

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

    

    printf("%d ",&a);

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

    

    printf("%d ",p);

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

}

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

#include <stdio.h>

 

void main()

{

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

    int *p;

    p=a;

    printf("%p ", a);

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

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

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

    printf("----------- ");

 

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

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

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

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

    printf("----------- ");

 

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

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

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

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

    printf("----------- ");

 

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

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

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

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

}

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

#include <iostream>

using namespace std;

void main()

{

    int i,a[10];

    int *p;

    //利用循环,分别为10个元素赋值

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

        a[i]=i;

    //将数组中的10的元素输出到显示设备

    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个元素的地址

}

 

● 指针与二维数组

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

 

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

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

int *p;

p=*a;

二维数组首地址

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

具体含义

数据类型

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

a

0一维数组指针的地址

int (*)[2]

*(a+2)+1

&a[0]

0一维数组指针的地址

int (*)[3][2]

*(pp+2)+1

&a

整个二维数组指针的地址

int (*)[2]

/

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

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

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

0行第0列数组元素的地址

int *

&a[2][1]

a[0]

  

int *

/

*a

0行第0列数组元素的地址

int *

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

*a+2*2+1

p

指向第0行第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 (*pp)[2]=a;        //定义一个int (*)[2]类型的一维数组()指针pp, pp指向一个包含5个元素的一维数组

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

    

    printf("%p ", a);

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

    printf("%p ", &a);

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

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

    printf("%p ", *a);

    printf("%p ", p);

    printf("----------- ");

 

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

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

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

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

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

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

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

    printf("----------- ");

 

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

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

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

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

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

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

    printf("%d ", *p);

 

    printf("----------- ");

 

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

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

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

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

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

}    

 

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

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

    }

}

 

● 熟记下表

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!='')

    {

        *p2=*p1;

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

        p2++;

    }

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

    printf("Now the string2 is: ");

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

}

 

● 数组指针&指针数组

数组指针: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 ", *(p[0]));

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

    printf("%p ",p[0]);    //数组p0个元素的值是变量a的地址    

    printf("%p ",&(p[0]));    //数组p0个元素的地址, 等同于printf("%p ",&p[0]);

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

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

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

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

}

 

 

 

 

 

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

#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 ", *(tab[i]));

    }

return 0;

}

 

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

#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 ", *(tab[i]+j));

        }

    }

return 0;

}

 

 

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

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

}

 

 

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

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

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

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

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

命令行的一般形式为:

命令名    参数1    参数2...参数n

例如:

d:debug1.exe hello world

#include <stdio.h>

#include <stdlib.h>

 

int main(int argc, char **argv)

{

    int i;

    

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

    

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

    {

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

    }

 

    system("PAUSE");

    return 0;

}

//得到可执行文件E: estDebug est.exe

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

 

 

● 指针的算术运算

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

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

#include <iostream>

using namespace std;

void main()

{

    int a=100;

    int *p_a=&a;

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

    p_a++;

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

    char b='A';

    char *p_b=&b;

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

    p_b--;

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

    int c[5]={0};

    int *p_c1=&c[0];

    int *p_c3=&c[3];

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

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

    int *pp[5];

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

}

 

● 指针与函数

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

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

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

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

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

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

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

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

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

{

    函数体

}

 

● 指针变量作函数参数

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

#include <stdio.h>

void func(int *a)

{

    (*a)++;

}

 

int main()

{

    int a = 1;

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

    printf("%d ", a);

    return 0;

}

 

void func1(int *p)

{

    p = NULL;

}

 

int main()

{

    int a = 1;

    int *p = &a;

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

    // p指向a的地址还是NULLp还是指向了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 xint y); 声明一个函数

int (*f) (int xint 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;

}

 

 

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

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

}

 

 

原文地址:https://www.cnblogs.com/ArrozZhu/p/8377912.html