概述:
- 普通变量:一个地址对应一个内容;
- 指针变量:一个地址对应一个内容(该内容为一个地址),所以指针变量有两个地址,一个是存储内容(地址)的该单元地址(类似于普通变量的地址),还有一个是存储内容里的内容是个地址。
- 指针也是一种变量,它所表示的不是数据的值,而是存储着数据的内存的地址。通过使用指针,就可以对任意指定地址的数据进行读写。
请大家看一下代码清单4-2。这是定义了d、e、f这3个指针变量的C语言程序。和通常的变量定义有所不同,在定义指针时,我们通常会在变量名前加一个星号(*)。我们知道,d、e、f都是用来存储32位(4字节)的地址的变量。然而,为什么这里又用来指定char (1字节)、short(2字节)、long(4字节)这些数据类型呢?大家是不是也感到很奇怪?实际上,这些数据类型表示的是从指针存储的地址中一次能够读写的数据字节数。
代码清单4-2 各种数据类型指针的定义
char *d; //char类型的指针d的定义 short *e; //short类型的指针e的定义 long *f; //long类型的指针f的定义
假设d、e、f的值都是100。在这种情况下,使用d时就能够从编号100的地址中读写1个字节的数据,使用e时就是2个字节(100地址和101地址)的数据,使用f时就是4个字节(100地址~103地址)的数据。怎么样?指针是不是很简单呢(图4-5)。
图4-5 指针的数据类型表示一次可以读写的长度
4.指针大小:是指针变量占用内存的大小(字节数),在32位机上,所有指针类型变量占用内存字节数都为4。因为32位机就是 4字节 * 8个二进制位/字节 计算出来的。如果在64位机上,指针占用内存大小就是:
8个字节。
5.数组指针(或称“行指针”):int (*p)[n];(或 int (*)[n]p)()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。
数组指针:首先它是一个指针,它指向一个数组。在 32 位系统下任何类型的指针永远是占 4 个字节,至于它指向的数组占多少字节,不知道,具体要看数组大小。它是“指向数组的指针”的简称。
6.指针数组:int *p[n]; []优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型的数组元素。这里执行p+1时,则p指向下一个数组元素,这样赋值是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。
指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身的大小决定,每一个元素都是一个指针,在 32 位系统下任何类型的指针永远是占 4 个字节。它是“储存指针的数组”的简称。
7.语法没错,但是偏离了实际意义:
int *p; scanf("%d", p); scanf("%d", &p);
8.%c、%s 在指针中的使用:
char a[20]="12345", *p=a; printf("%c\n", *p); //1 printf("%s\n", p); //12345
9.以下都是对的:
char str[]="123456889"; char *p; p=str; p="34546"; char *ps=&(*("123"));
10.指针初始化 & 默认值
指针变量未被初始化,指针变量就不会被分配存储空间(无论是全局还是局部)。在程序中如果使用了未被分配空间的指针,就会出现难以查找的错误。
char *p1; int main() { char *p2, *p3; p3=(char*)malloc(sizeof(char)); }
此时此刻,p1和p2都是一样的情况,本身有地址,但是内容区域是空的(因为没有分配存储空间,导致使用取内容符号会报错);而p3属于常规申请内存操作。
11.待更新...
案例一:
int main() { int n=10,n1=20; // 值的变量 int *a=&n; // 地址的变量,其实与值的变量一样的,只是值的变量,这个变量的值在变,地址不变;而地址的变量,地址在变(当然这里的地址对应的值是跟地址绑定在一起的) int *c=a; printf("a == %d\n",a); printf("a == %d\n",*a); printf("c == %d\n",c); printf("c == %d\n\n",*c); *a=1000; printf("a == %d\n",a); printf("a == %d\n",*a); printf("c == %d\n",c); printf("c == %d\n\n",*c); a=&n1; // a地址改变了对c没有影响 printf("a == %d\n",a); printf("a == %d\n",*a); printf("c == %d\n",c); printf("c == %d\n\n",*c); int *&b=a; // b就代表a,单单只是名字不一样 printf("a == %d\n",a); printf("a == %d\n",*a); printf("b == %d\n",b); printf("b == %d\n\n",*b); b=c; // b地址改变了对a有影响 printf("a == %d\n",a); printf("a == %d\n",*a); printf("b == %d\n",b); printf("b == %d\n",*b); printf("c == %d\n",c); printf("c == %d\n\n",*c); return 0; }
案例二:
struct node { int val; node *left; node *right; // 这里left、right一定要初始化NULL,否则遍历的时候指针乱值并不等于NULL node(const int x):val(x),left(NULL),right(NULL){} }; void preTraversal(node *t) // 前序遍历 { if(t) { printf("%d ",t->val); preTraversal(t->left); preTraversal(t->right); } } int main() { node *rt=new node(-1); rt->left=new node(1); rt->right=new node(2); rt->left->left=new node(3); preTraversal(rt); puts("\n"); node *newNode=new node(4); // 修改叶子结点(不影响) rt->left->left=newNode; preTraversal(rt); puts("\n"); newNode=new node(5); // 修改中间结点(上下都有相连)(下面的有影响) rt->left=newNode; preTraversal(rt); return 0; }
案例三:*p、p、&p之间的关系
案例四:二维指针(二维数组)
#include<bits/stdc++.h> using namespace std; int main() { int a[2][3]={0,2,4,6,8,10}; cout<<a<<endl; cout<<a[0]<<endl; cout<<(a+1)<<endl; cout<<*(a+1)<<endl; cout<<**(a+1)<<endl; cout<<*(a[1]+1)<<endl; cout<<**(a+1)+2<<endl; cout<<*(*(a+1)+2)<<endl; cout<<**((a+1)+2)<<endl; return 0; } Console: 0x6afee8 0x6afee8 0x6afef4 0x6afef4 6 8 8 10 4198653
案例五:
#include<bits/stdc++.h> using namespace std; struct node { int d; node * next; }; int main() { node *L,*L2,*nd=new node; nd->d=1; nd->next=NULL; L2=L=nd; printf("L2 == %d\n",L2); printf("L == %d\n",L); L=NULL; // 对L2没有影响,因为指针就像个临时工,给它什么地址,就去另外一个地址做事情 printf("L2 == %d\n",L2); printf("L == %d\n",L); return 0; } Console: L2 == 16126224 L == 16126224 L2 == 16126224 L == 0