(C/C++学习笔记) 十二. 指针
十二. 指针
● 基本概念
位系统下为4字节(8位十六进制数),在64位系统下为8字节(16位十六进制数) 进制表示的, 内存地址不占用内存空间 指针本身是一种数据类型, 它可以指向int, char, float double等不同类型的变量, 仔细揣摩下图: 的时候(在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 上述两个二级指针在内存中的分布下图所示。 |
● 指针的简单例子
#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; } |
● &和*两个运算符
&----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的地址*/ } |
● 有关*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:\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的值*/ } |
● 野指针, 垂悬指针/迷途指针, 空指针
① 野指针 (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";
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"; |
#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; } |
#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; } #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"; |
● 指针与一维数组
指针与一维数组: //定义数组元素的指针 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是指向数组起始元素的指针常量, 是右值, 不能
● 不能用&(a+4), 否则提示error C2102: '&' requires l-value(有地址的值), 即&的运算只能是已经被声明的变量; *(a+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) } |
||||||||||||||||||||||
//通过指针变量获取一维数组元素的地址和值(简单案例) #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)); //指针法 } |
||||||||||||||||||||||
//通过指针变量获取一维数组的元素(复杂案例) #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个元素的地址 } |
● 指针与二维数组
二维数组用矩阵方式存储元素, 在内存中仍然是线性结构:
对于二维数组的指针及其元素的值: int a[3][2]={{1,2},{11,12},{21, 22}}; int *p; p=*a;
|
||||||||||||||||||||||||||||||||
//通过指针变量获取二维数组的元素(简单案例) #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]))取值 } |
||||||||||||||||||||||||||||||||
//通过指针变量获取二维数组的元素(复杂案例) #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!='\0') { *p2=*p1; p1++; /*指针移动*/ p2++; } *p2='\0'; /*在字符串的末尾加结束符*/ printf("Now the string2 is:\n"); 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\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存储的内容是指针数组首元素的地址, 所以 } //指针数组的指针元素指向不同的整形变量 #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; } //指针数组的指针元素指向不同的数组 #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; } //指针数组的指针元素指向不同的字符串 #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()函数的参数, 参数个数包括命令. 命令行的一般形式为: 参数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 |
按Win键+R打开运行,在打开的运行中输入CMD打开命令行程序cmd.exe
|
● 指针的算术运算
对指针作算术运算有两种:
|
#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)); } |
● 指针与函数
函数存放在代码区(code area), 在编译时被分配给一个入口地址, 这个地址就是函数的指针. 可以用一个指针变量指向函数, 然后通过该指针变量调用这个函数. ① 指针形参(指针变量作函数参数) 数据类型 函数名(数据类型 *pt1, 数据类型 *pt2, ...) ② 函数型指针(指向函数的指针) 数据类型 (*函数指针名)(形参表) ※回调函数: 通过函数指针而被调用的函数
数据类型 *函数名(参数表) { 函数体 } |
● 指针变量作函数参数
//指针形参(指针变量作函数参数) #include <stdio.h> void func(int *a) { (*a)++; } int main() { int a = 1; func(&a); //通过函数的形参间接地修改实参的值, 之所以实参为地址, 是因为子函数func的形参类型为整形指针变量 printf("%d\n", a); return 0; } |
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; } |
● 指针型函数(返回指针值的函数)
#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++学习笔记) 十二. 指针的更多相关文章
- python3.4学习笔记(十二) python正则表达式的使用,使用pyspider匹配输出带.html结尾的URL
python3.4学习笔记(十二) python正则表达式的使用,使用pyspider匹配输出带.html结尾的URL实战例子:使用pyspider匹配输出带.html结尾的URL:@config(a ...
- Go语言学习笔记十二: 范围(Range)
Go语言学习笔记十二: 范围(Range) rang这个关键字主要用来遍历数组,切片,通道或Map.在数组和切片中返回索引值,在Map中返回key. 这个特别像python的方式.不过写法上比较怪异使 ...
- java jvm学习笔记十二(访问控制器的栈校验机制)
欢迎装载请说明出处:http://blog.csdn.net/yfqnihao 本节源码:http://download.csdn.net/detail/yfqnihao/4863854 这一节,我们 ...
- Python学习笔记(十二)—Python3中pip包管理工具的安装【转】
本文转载自:https://blog.csdn.net/sinat_14849739/article/details/79101529 版权声明:本文为博主原创文章,未经博主允许不得转载. https ...
- ROS学习笔记十二:使用gazebo在ROS中仿真
想要在ROS系统中对我们的机器人进行仿真,需要使用gazebo. gazebo是一种适用于复杂室内多机器人和室外环境的仿真环境.它能够在三维环境中对多个机器人.传感器及物体进行仿真,产生实际传感器反馈 ...
- JavaScript权威设计--命名空间,函数,闭包(简要学习笔记十二)
1.作为命名空间的函数 有时候我们需要声明很多变量.这样的变量会污染全局变量并且可能与别人声明的变量产生冲突. 这时.解决办法是将代码放入一个函数中,然后调用这个函数.这样全局变量就变成了 局部变量. ...
- MySQL学习笔记十二:数据备份与恢复
数据备份 1.物理备份与逻辑备份 物理备份 物理备份就是将数据库的数据文件,配置文件,日志文件等复制一份到其他路径上,这种备份速度一般较快,因为只有I/O操作.进行物理备份时,一般都需要关闭mysql ...
- Java基础学习笔记十二 类、抽象类、接口作为方法参数和返回值以及常用API
不同修饰符使用细节 常用来修饰类.方法.变量的修饰符 public 权限修饰符,公共访问, 类,方法,成员变量 protected 权限修饰符,受保护访问, 方法,成员变量 默认什么也不写 也是一种权 ...
- Python学习笔记十二
HTML全称:Hyper Text Markup Language超文本标记语言 不是编程语言 HTML使用标记标签来描述网页 2. HTML标签 开始标签,结束标签. 例如:<html&g ...
随机推荐
- 加法变乘法|2015年蓝桥杯B组题解析第六题-fishers
加法变乘法 我们都知道:1+2+3+ ... + 49 = 1225 现在要求你把其中两个不相邻的加号变成乘号,使得结果为2015 比如: 1+2+3+...+1011+12+...+2728+29+ ...
- ajax post 参数说明
- session的理解和使用
Session的使用与Session的生命周期 1.HttpSession的方法 Object getAttribute(String); Enumeration<String> getA ...
- HDU 1757 A Simple Math Problem(矩阵快速幂模板)
题意:题意很简单,不多说了. 思路: |f(10) | |a0 a1 a2 ...a8 a9| |f(9)|| f(9) | | 1 0 0 ... 0 ...
- POJ 3628 Bookshelf2(0-1背包)
http://poj.org/problem?id=3628 题意:给出一个高度H和n个牛的高度,要求把牛堆叠起来达到H,求出该高度和H的最小差. 思路:首先我们计算出牛的总高度sum,sum-H就相 ...
- go 并发
package main import ( "fmt" "time" ) func say(s string) { ; i < ; i++ { time. ...
- xss脚本注入后端的防护
1.脚本注入最主要的是不要相信用户输入的任何数据,对数据进行转义 可以使用:org.springframework.web.util.HtmlUtils包中的 HtmlUtils.htmlEscape ...
- steam
1.steam 教育 Science(科学), Technology(技术), Engineering(工程), Arts(艺术), Maths(数学) 2. steam 平台 Steam英文原译为 ...
- idea忽略文件
- Codeforces 614E - Necklace
614E - Necklace 思路:如果奇数超过1个,那么答案是0:否则,所有数的gcd就是答案. 构造方案:每个数都除以gcd,如果奇数个仍旧不超过1个,找奇数个那个在中间(如果没有奇数默认a), ...