在计算机科学中,指针(pointer)是编程语言中的一个对象,利用地址(内存的编号),它的值直接指向(point to)存在电脑存储器中另一个地方的值。
由于通过地址可以找到所需的变量单元,可以理解为,地址指向改变量单元。
因此,将地址形象化的称为“指针”,意思是可以通过它找到以它位地址的内存单元。
- 指针是个变量,存放内存单元的地址(编号),存在指针中的值都被当作地址处理;
- 地址是唯一标识一块地址空间的。
void main() { int a = 10; //在内存中开辟一块空间,空间的名字叫a,存的值为10 int* p = &a; //在内存中开辟一块空间,空间的名字叫p,存的值为a的地址,指向空间a }
- 在32位的机器上,地址是32个0挥着1组成的二进制序列,那地址就得用4个字节的空间来存储,所以一个指针变量的大小位4个字节。
- 在64位的机器上,地址就由64位的二进制序列组成,那存储地址就需要8个字节的空间,所以一个指针变量的大小位8个字节。
char *pc = NULL;
int *pi = NULL;
short *ps = NULL;
long *pl = NULL;
float *pf = NULL;
double *pd = NULL;
定义指针的方式是:type *指针名。
其中type指的是该指针存放的是什么type的地址;比如 char *类型的指针就是为了存放char类型变量的地址。
指针类型的意义:
指针的类型决定了指针向前或者向后走一步有多大距离。
代码示例:
void main() { int n = 10; char* pc = &n; int* pi = &n; printf("%p\n", &n); printf("------------\n"); printf("%p\n", pc); printf("%p\n", pc+1); printf("------------\n"); printf("%p\n", pi); printf("%p\n", pi+1); }
通过以上代码可以看出,指针的类型决定了指针向前或向后走一步的距离。
void main() { int n = 0x11223344; int n1 = 0x11223344; char* pc = (char *)&n; int* pi = &n1; *pc = 0; //char*类型只能访问一个字节。 *pi = 0; //int*类型只能访问四个字节。 printf("0x%x\n", n); printf("0x%x\n", n1); }
指针的类型决定了,对指针解引用的时候有多大权限(能操作几个字节)。
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)。
- 指针未初始化
- 指针越界访问
- 指针指向的空间释放
void main() { int* p; //局部变量指针未初始化,默认为随机值; int arr[10] = { 0 }; int* pi = arr; int i = 0; for (i = 0; i <= 11; i++) { //当指针指向的范围超出数组arr的范围时,pi就是野指针 *(pi++) = i; } }
- 指针初始化
- 小心指针越界
- 指针指向的空间释放即使置NULL
- 指针使用之前检查有效性
void main() { int* p = NULL; int a = 10; p = &a; if (p != NULL) //有效性检查 { *p = 20; } printf("%d", a); }
void main() { char arr[5]=""; char* p = arr; //利用指针来给字符串赋值 for (int i = 0; i < 4; i++) { *(p + i) = '1'; } *(p + 4) = '\0'; printf("%s", arr); //1111 }
void main() { char arr[5]=""; char* p = arr; //利用指针来给字符串赋值 for (int i = 0; i < 4; i++) { *(p + i) = '1'; } *(p + 4) = '\0'; printf("%s", arr); }
//用指针-指针完成求字符串长度的函数 int my_strlen(char* s) { char* p = s; while (*p != '\0') { p++; } return p - s; } void main() { char arr[] = "123456"; int n = my_strlen(arr); printf("%d\n", n); printf("%d\n", strlen(arr)); }
标准规定:
允许执行数组元素的指针与执行数组最后一个元素后面那个内存位置的指针比较。但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
例如:
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp–)
{
*vp = 0;
}
这样的的代码或许能够顺利执行,但是还是应该避免这样写,因为标准并不保证它可行。
在前面的笔记中讨论过,数字名和数组首元素地址的值是一样的。所以下面的代码是可行的:
void main() { int arr[10] = { 1,2,3,4,5,6,7,8,9,0 }; int *p = arr; //p中存放的使数组首元素的地址 }
既然可以把数组名当成地址存放到一个指针中,那么就可以用指针来访问数组。
void main() { int arr[] = { 1,2,3,4,5,6,7,8,9,0 }; int* p = arr; int sz = sizeof(arr) / sizeof(arr[0]); for (int i = 0; i < sz; i++) { printf("&arr[%d] = %p\n", i, &arr[i]); printf("p + %d = %p\n", i, p + i); printf("\n"); } }
从上述代码的运行结果可以发现,(p+i)其实是计算数组arr下标为i的地址;
所以,可以直接用过指针来访问数组:
void main() { int arr[] = { 1,2,3,4,5,6,7,8,9,0 }; int* p = arr; //指针存放的是数组首元素的地址 int sz = sizeof(arr) / sizeof(arr[0]); //用指针来实现打印数组各元素 for (int i = 0; i < sz; i++) { printf("%d ", *(p + i)); } }
指针变量也是变量,是变量就有地址,那么二级指针就是存放指针变量地址的
int a = 10;
int *pa = &a;
int **ppa = &pa; //二级指针**ppa先通过*ppa找到pa,在对pa进行解引用操作,找到的就是a。
代码示例:
void main() { int a = 10; printf("%d\n", a); int* pa = &a; int** ppa = &pa; **ppa = 30; printf("%d\n", a); //a的值变为了30 }
指针数组是数组,是存放指针的数组
int* arr[5];
//arr是一个整型指针数组,有5个元素,每个元素是一个整型指针。