5. 指针(指针变量,内存地址)。
指针就叫内存地址。变量里面记录的内存地址
语法: 数据类型 * 变量名;
int a =10;
int *p = &a; // 标记p里存放的是内存地址。
// *p 解引用运算,获取指针里面的值。
*p = 200; // 给a重新赋值200
printf("%d\n", *p); // 打印200.
指针变量占用的大小,根数据类型无关,根编译器有关。32位占4字节。64位系统占8字节。
给指针变量赋值的时候,不能把一个数值赋值给指针变量。
利用 *(借用运算符),可以查询数据,存储数据。
5.1 指针的作用:
- 操作其他函数中的变量。 利用指针交换a、b的指针。
- 函数返回多个值。
- 函数的结果和计算的状态分开。
void getMaxAndMin(int arr[], int len, int* max, int* min){
*max = arr[i];
*min = arr[i];
}
int max = arr[0];
int min = arr[0];
getMaxAndMin(arr, len, &max, &min); //
6. 指针高级
6.1 指针的运算。
int * p = 0x001; // 指针中数据类型的作用:获取字节数据的个数。
步长:指针移动依次的字节个数(看实际占用几个字节,int4字节)。
p+N; p-N; 往后或往前移动N个步长。
有意义的操作:
- 指针跟整数进行加、减操作(每次移动N个步长)
- 指针跟指针进行减操作(间隔步长数)
6.2 野指针,悬空指针
野指针:指针指向的空间未分配;
int *p2 = p1+10; // 野指针
悬空指针:指针指向的空间已分配,但是被释放了。
int *method() {
int num =10;
int *p = #
return p;
}
int *p3 = method();
printf("%p\n", p3);
printf("%d\n", *p3);
总结:野指针,悬空指针 都可能导致数据异常。
6.3 void *p; 不代表任何类型。
特点:无法获取数据,无法计算,但是可以接受任意地址。
不同类型的指针之间,是不能互相赋值的。但是 void * 是特殊情况。
void *p3 = p1;
void *p4 = p2;
作用:swap数据,不限定数据类型。让它更有通用性。
void swap(void *p1, void *p2, int len) {
// 把void类型,转char类型的。
char* pc1 = p1;
char* pc2 = p2;
char temp = 0;
// 以字节为单位,一字节一字节的交换。len字节的长度。
for(int i=0; i<len; i++)
{
temp = *pc1;
*pc1 = *pc2;
*pc2 = temp; // 交换字节中的数据。
pc1++;
pc2++;
}
}
int c = 100;
int d = 200;
swap(&c, &d, 4); // 4是int 占据的字节数。
6.4 二级指针,多级指针。
指针数据类型:跟指向空间中,数据的类型是保持一致的。
二级指针:指向指针的指针。
int a; int* a; // 指针 int** a; // 二级指针。
int a = 10; int b=20;
int* p = &a; // 定义一级指针
int** pp = &p; // 定义二级指针
*pp = &b; // 作用1:利用二级指针,修改一级指针里面记录的内存地址。
printf("%d\n", *pp); // 作用2:利用二级指针获取到变量中记录的数据。
6.5 数组和指针。
指向数组的指针,叫做数组指针。方便的操作数组中的各种数据。
include <stdlib.h>
include <time.h>
随机数
srand(time(NULL));
int num = rand() ;
// 生成一定范围的随机数。
int num = rand()%17 + 7; //7~24
int num = rand()%42+ 8; // 8~49
int num = rand()%100 +1;// 1~100
数组:
就是一个容器,可以用来存储同种数据类型的多个值。
数组作为参数传递的时候,传递的是数组的首地址。
int len = sizeof(arr)/ sizeof(arr[0]);
void printArr(int arr[], int len) {
for(int i=0;i<len;i++){
printf("%d\n", arr[i]);
}
}
// 利用数组的指针,遍历数组。
int main() {
int arr[] = {1,5,4,3,2}; // 升序排序。
int len = sizeof(arr)/sizeof(int);
int* p1 = arr; // 数组的首地址。
for(int i=0; i<len; i++) {
printf("%d\n", *(p+i));
}
}
6.6 数组指针的细节。
- arr参与计算时,会退化为指向第一个元素的指针。指向数组的首地址。
特殊情况: - sizeof 运算的时候,不会退化,arr还是整体;
- &arr 获取地址的时候,不会退化。
步长 = 数据类型 * 数组长度, // 每+1处理,跨越整个数组。比如二维数组遍历的场景。
printf("%d\n", &arr +1); // 5*4=20
6.7 索引遍历数组。
int arr[m][n] ={{}, {}, {}}; // m是二位数组的长度;n是一维数组的长度
int arr1[3] = {1,2,3}; // 先定义一维数组。在放入到二维数组中。
int* arr[3] = {arr1, arr2, arr3}; // 存放不同长度的数组。
int len1 = sizeof(arr1) / sizeof(int);// 先计算数组长度,保存到长度数组中。
int lenArr[3] = {len1, len2, len3}; // 数组长度数组。
for(int i=0; i<3; i++) {
for(int j=0; j<lenArr[i]; j++) {
printf("%d", arr[i][j]);
}
printf("\n");
}
6.8 指针遍历数组。
int (*p)[5] 二维数组里面的指针。
*p
int main() {
int arr[3][5] = {
{1,2,3,4,5},
{11,22,33,44,55},
{111,222,333,444,555},
};
// 二维数组里面存储的是一维数组 Int[5]
int (*p)[5] = arr; // 数组指针:指向数组的指针。 步长为int*5(20字节)
for(int i=0; i<3; i++) {
// 遍历一维数组
for(int j=0;j<5;j++) {
printf("%d ", *(*p+j));
}
printf("\n");
p++; // 移动二维数组的指针
}
}
// 第二种格式。二维数组中存放的是内存地址。
int arr1[3] = {1,2,3,4,5};
int* arr[3] = {arr1, arr2, arr3}; // 指针数组:存放指针的数组。
int ** p = arr;
for(int i=0; i<3; i++) {
// 遍历一维数组
for(int j=0;j<5;j++) {
printf("%d ", *(*p+j));
}
printf("\n");
p++; // 移动二维数组的指针
}
6.9 函数和指针。
利用函数指针,动态的调用函数。灵活。
void method1() {
printf("method1\n");
}
int method2(int num1, int num2) {
printf("method2\n");
return num1 +num2;
}
int main() {
// 1.定义指针,指向两个函数。
void (*p1)() = method1; // 指针p1指向method1.
void (*p2)(int, int) = method2;
// 2.利用函数指针,去调用函数
p1();
int result = p2(10, 20);
}
// 函数指针的使用。灵活使用。注意:只有形参完全相同,且返回值完全一样的函数,才能放到函数指针数组中。
int add(int num1,int num2) {
return num1+num2;
}
int subtract(int num1,int num2) {
return num1-num2;
}
int mutipy(int num1,int num2) {
return num1*num2;
}
int divide(int num1,int num2) {
return num1/num2;
}
int main() {
// 定义数组,装4个函数的指针。函数指针数组。
int (*arr[4])(int, int) = {add,subtract, mutipy,divide};
int num1 = 10;
int num2 = 20;
int choose = 2;
// 根据用户的选择,调用不同的函数。
int res = (arr[choose -1])(num1, num2); // 函数的指针。
printf("%d\n", res);
}
----End----