第八章:指针
1.指针是什么
*指针就是变量,用来存放地址的变量
*&:取地址运算符(可以获取一个数据对象的首地址和存储空间大小)
*声明一个指针
*定义:设一个数据对象为x,设另一个数据对象为p。p存储了x的首地址和空间大小。那么, p称之为x的指针,或者说p指向x
目标类型 *(有无空格均可) 变量名
*指针类型的值就是目标数据对象的首地址
*C语言中通过不同的指针类型来标记目标数据对象的空间大小
*取值运算符 * , 可以根据指针中存储的首地址和空间大小找到目标数据对象
*占位符 %p 是指针类型专用的占位符(通常以16进制显示)
*若指针类型为 4 个字节,使用 %u 为占用符是合适的。但指针类型为 8 个字节时,最好使用 %llu
(可以看出pn,*pn表示的是什么)
*char可以占用小一点的空间,而int会占用大一点的空间
*但是char*与int*存储均为数据对象的地址,因此他们占用空间是相同的
eg.下方若改成*64则结果指针类型占用8个字节
#include<stdio.h>
int main()
{
//定义指针变量储存变量地址
int a = 10;
//指针类型->数据类型*
int* p;
p = &a;
//通过指针间接改变变量的值
*p = 100;
printf("%p\n", &a);
printf("%p\n", p);
printf("%d\n", a);
printf("%d\n", *p);
return 0;
}
2.指针运算
规律 sizeof(目标数据对象)被称为步长 |
指针类型加n后,其首地址向后移动n*步长 字节 |
指针类型减n后,其首地址向前移动n*步长 字节 |
指针类型与指针类型相减后,其结果为两首地址差值除以步长 |
*步长为指针所指向的类型所占空间大小
eg.
int*p=(int*)100
p+1,结果为首地址向后移动sizeof(int)字节,即104
p+1,结果为首地址向前移动sizeof(int)字节,即96
*1.指针类型与整形加减
*2.同类型的指针相减
(这两种运算都有意义,其他运算没有实际意义)
3.指针与数组
规则:(设数组元素为T)
T arr[5];//以T为元素数组arr
T *P;//指向T的指针
当数组名arr出现在一个表达式中,数组名arr将会被转换为指向数组第一个元素的指针
但有两个例外:
1.对数组名arr使用sizeof 时
2.对数组名arr使用&时
使用指针访问数组等价于下标访问,有两种方法:
1.数组名【下标】
2.*(数组名+偏移量) 注:取值运算符 * 优先级高于算术运算符,所以要加括号
(偏移量是指指针指向的地址和数组首地址之间相差几个元素)
*这两种形式是等价的,中括号【】,被称作下标运算符,它的优先级高于一切其他运算符
通常形式为 A[k]
而表达式运算时,最终会将下标运算符展开为
*(A+k)
#include <stdio.h> int main() { int arr[5] = { 111,222,333,444,555 }; printf("arr[2]=%d\n", arr[2]); printf("2[arr]=%d\n", 2[arr]); printf("*(arr+2)=%d\n", *(arr + 2)); return 0; }
4.指针作为参数传递
#include<stdio.h>
void swap(int* x, int* y)
{
int temp = *x;
*x = *y;
*y = temp;
}
int main()
{
int a, b;
int temp;
a = 1;
b = 2;
printf("a=%d b=%d\n", a, b);
swap(&a, &b);
printf("a=%d b=%d\n",a, b);
return 0;
}
注意:不是交换指针x,y的值,而是交换目标数据对象a,b的值。所以,需要在指针前使用取值运算符 * 。
由于指针类型定死了指针所指向的数据类型。为了让函数可以交换更多数据类型,我们仅需要指针类型中保存的首地址,目标数据大小通过额外的参数传入。
int n; void *p=&n; //int*赋值给void*,类型信息丢弃,仅保存首地址 *p; //仅有首地址,未保存目标数据对象大小,无法取值 p+1; //仅有首地址,没有步长,无法进行加减运算
但void*有个好处,就是任意类型指针都可以直接赋值给它。而其他类型的指针是不能相互赋值的,由于赋值会改变目标数据对象的类型。
规律:
1.不同指针类型不能相互赋值,相互赋值后会造成目标数据对象类型的改变,无法通过编译。
2.void*类型为特例,它可以接受任意指针类型的赋值,也可以赋值给任意类型的指针
void swap(void* x, void* y, int size)
{
//指针转为char*,单个字节操作内存
char* pX = (char*)x;
char* pY = (char*)y;
char temp;
for (int i = 0; i < size; i++)
{
temp = pX[i];
pX[i] = pY[i];
pY[i] = temp;
}
}
由于void*不能取值和加减,所以将其转换成char*.
char*可以提供单个操作内存的能力
C语言中void*类型不但可以接受任意类型的指针,也可以自动转换为任意类型指针
而c++中仅能接受任意类型指针,不能自动转换
5.多级指针和指针数组
*指针的指针
形式: int * *p(中间*的空格没有固定要求)
*多级指针
和普通指针一样通过取值运算符*,获取目标数据对象
#include<stdio.h> int main() { int n = 123; int* p = &n; int** pn = &p; int*** pnn = &pn; printf("n=%d", ***pnn); return 0; }
*指针数组
#include<stdio.h> int main() { int arr1[5] = { 1,2,3,4,5 }; int arr2[5] = { 11,22,33,44,55 }; int arr3[5] = { 111,222,333,444,555 }; int* pToArr[3]; pToArr[0] = arr1; pToArr[1] = arr2; pToArr[2] = arr3; for (int i = 0; i < 3; i++) { int** p = pToArr + i; for (int j = 0; j < 5; j++) { printf(" %d", *(*p + j)); } printf("\n"); } return 0; }
分析:
p,指向pToArr的第一个元素,类型为int**
*p,指向arr1的第一个元素,类型为int*
*p+j,指向arr1中的第j个元素,类型为int*
*(*p+j),为arr1中的第j个元素