1 指针基础
怎么获得变量地址
1 如何产生一个指针变量——>类型* 标识符;int* p1;char* p2;double* p3;//不同类型的基本指针占用内存是一样的都是4个字节(32位)/8个字节(64位),都是存的地址2 数组名是数组首地址但不是普通指针——>数组名绑定的一段内存可以影视转化为指针int array[4] = { 1,2,3,4};printf("array:%zd\n", sizeof(array));//打印16,整个数组的大小printf("array:%p\tarray[0]:%p", array, &array[0]);//值一样,内容不一样3 int*p1;未初始化是野指针p1=NULL;变成空指针。NULL-->(void*)0 把0强制转化地址一般定义指针 int*p=NULL;4 指针取值运算int num=666;int*P=#//*指针变量 ,指针变量[0];printf("%d\t%d\t%d\n", num, *pnum, pnum[0]);//都打印666*pume=888;printf("%d\t%d\t%d\n", num, *pnum, pnum[0]);//都打印888//操作指针变量*pume就相当于操作num,任意修改一个其他都会跟着变5 指针偏移——>移动数据位//p+n或者p-n是移动数据位//int* p要明白指针类型//指针的类型:int* -->去掉变量名//指针的所指向类型:int-->操作的数据的类型-->去掉变量名和*号(基类型)//知道操作的数据类型就知道偏移多少printf("p1=%p\n", p1); int p1=0000000000000000printf("p2=%p\n", p2); char p2=0000000000000000printf("p3=%p\n", p3); double p3=0000000000000000//+1,偏移一个数据位 printf("p1=%p\n", p1+1); int p1=0000000000000004printf("p2=%p\n", p2+1); char p2=0000000000000001printf("p3=%p\n", p3+1); double p3=0000000000000008printf("array=%p\n", array); array=000000A8A24FF4C8printf("array=%p\n", array+1); array=000000A8A24FF4CC //差4int(*p)[3] = NULL; printf("p=%p\n", p ); //操作的是int[3]printf("p=%p\n", p + 1); //int[3]——>偏移一个数组12个字节
2 const与指针变量
const后面不能被修改,把变量变成了常属性。 1.以下两种没区别
const int cnum1 = 0;
int const cnum2 = 0;2.在*前面,指针指向内容不可以修改——>修饰1
//以下两种没区别
//const描述的是指针所指向的内容
const int* p1 = &cnum1;
int const* p2 = &cnum1;
int data = 0;
*p1=666;(不行)
p1 = &data;
p1 = p2;(可以)3在*后面,指针不可以修改——>修饰2//让指针变量指向的地址固定
int* const p3 = &data;
//const修饰的是p3 不可修改
p3 = p2; (不行)
p2 = p3;(可以)4const int* const p4 = &data;
//都不能被修改
3 二级指针与多级指针
因为指针变量也有地址,也可以用指针来存放
类似与套娃
int num = 0;
int* p1 = #
int** p2 = &p1;
int*** p3 = &p2;num = 999;
printf("%d\n", ***p3);
printf("%d\n", p3[0][0][0]);//这两个也一样都打印999
4 指针操作一维数组
指针指向数组首地址,直接当数组名去用
1int array[4] = { 1,2,3,4 };int* p = array;//p = &array[0];for (int i = 0; i < 4; i++)
{printf("%d\t", p[i]); //推荐用法//printf("%d\t", *(p + i));//printf("%d\t", (p + i)[0]);
}//下面用法不推荐,但是要能看懂//改变指针指向,指针偏移到数组之后,任意越界用起来危险while (p != array + 4)
{printf("%d\t", p++[0]);
}printf("\n");2 负下标,不用首地址做初始化int data[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* pp = &data[5];
printf("%d\n", pp[0]);
for (int i = -4; i < 0; i++)
{printf("%d\t", pp[i]);
}
printf("\n");3 函数传参,传输组名等效于传一级指针(数字类必须还要传长度)在 C 语言中,数组作为函数参数时,实际上是传递数组的首地址,而不是整个数组。因此,函数内部无法直接获取数组的长度。这就是为什么我们需要额外传递数组的长度(元素数量)的原因void print_array(int array[], int arrayNum)
{for (int i = 0; i < arrayNum; i++) {printf("%d\t", array[i]);}printf("\n");
}
void print_array_2(int *array, int arrayNum)
{for (int i = 0; i < arrayNum; i++){printf("%d\t", array[i]);}printf("\n");
}4 传字符串一定要写const,C语言里可能没有影响,c++有影响(养成好习惯)
int my_strlen(const char* str)
{int count = 0;while (str[count] != '\0')count++;return count;
}
5 指针操控二维数组
1 一级指针操作二维数组——>列转换为序号void test_one()
{int array[3][4] = { 1,2,3,4,5,6,7,8,9,10,11,12 };int* p = &array[0][0];for (int i = 0; i < 3; i++) {for (int j = 0; j < 4; j++) {//行列转换为序号printf("%d\t", p[i * 4 + j]);}printf("\n");}2 数组指针操作void test_two()
{int array[3][4] = { 1,2,3,4,5,6,7,8,9,10,12 };//二维数组名偏移的一行printf("array:%p\n", array);printf("array+1:%p\n", array+1); //16//二维数组名+一个下标 转换为一个一级指针printf("array[0]:%p\n", array[0]);printf("a rray[0]+1:%p\n", array[0]+1); //4;2.1//推荐用法!!! //数组指针操作二维数组(直接当数组名)int(*p)[4] = array;for (int i = 0; i < 3; i++) {for (int j = 0; j < 4; j++) {printf("%d\t", p[i][j]);}printf("\n");}以下是多种写法,用A换元思想(书本上出现要认识)2.2 for (int i = 0; i < 3; i++){for (int j = 0; j < 4; j++){printf("%d\t", *(*(p+i)+j));//p[i]==>A A[j]==>*(A+j)//p[i]==>*(p+i)==>A}printf("\n");}2.3 printf("%d\t", *(p[i] + j));//p[i]==>A A[j]==>*(A+j)//p[i]==>A2.4 printf("%d\t", ((p + i)[0] + j)[0]);//p[i]==>A A[j]==>*(A+j)==>(A+j)[0]//p[i]==>(p+i)[0]==>A2.5 printf("%d\t", (p[i] + j)[0]);//p[i]==>A A[j]==>*(A+j)==>(A+j)[0]//p[i]==>(p+i)[0]==>A3 函数传参void print_array(int(*p)[4], int row, int cols)
{for (int i = 0; i < row; i++) {for (int j = 0; j < cols; j++) {printf("%d\t", p[i][j]);}printf("\n");}
}
void test_three()
{int array[3][4] = { 1,2,3,4,5,65,7,8,9,0,19,12 };print_array(array, 3, 4);
}
6 万能指针
#include <stdio.h>
#include <stdlib.h>
int main()
{//万能指针
void* p = NULL;
int num = 999;
p = #//万能指针不能*p直接访问
//要做目标数据类型的强制类型转换
printf("%d\n", *(int *)p);
double dNum = 8.98;
p = &dNum;
printf("%.2lf\n", *(double*)p);return 0;
7 指针函数
#include <stdio.h>
#include <stdlib.h>一. 传指针问题1 //值传递void modify_one(int a) //int a=实参{a = 100;}//没有返回值,打印出来还是0//址传递(函数不需要返回值就可以修改值)
2 //修改实参的值 ,传入实参的地址,修改是*地址void modify_two(int* p) //int* p=实参 {*p = 100;}3 //(为什么传二级指针)修改指针指向num指向g_num,用二级指针接收指针的地址int g_num = 999;void modify_point(int** p) //int** p=实参{*p = &g_num;}
//——————————————————————————————————————————————
int main(){1 int num = 0;modify_one(num);printf("%d\n", num);
//—————————————————————————————————————————————— 2 modify_two(&num);printf("%d\n", num);
//—————————————————————————————————————————————— 3 int* p = #modify_point(&p);//传入的指针变量printf("%d\n", p[0]);
}
二. 返回指针问题1 //C语言不允许返回局部变量的地址(不安全的)//内存会回收,变量地址给其他不知道的东西了//warning C4172: 返回局部变量或临时变量的地址: numint* get_num() {int num = 99999;return #(不行)}2 //需要学会区分什么叫做局部变量的地址int* get_num_one(int* p) {return p;(可以)——>因为在主函数里调用时,变量一直到结束时都是有效的}3 //可以返回堆区内存的地址(下章说)4 //返回一个数组的函数(返回数组首地址就可以了)int array[3] = { 1,2,3 };int* get_array() {return array;}int main()
{1 int* pp = get_num();printf("%d\n", *pp);printf("%d\n", *pp);
//—————————————————————————————————————————————— 2 get_num_one(p);//函数返回指针后还可以的继续操作//p[0];get_num_one(p)[0];//*p=199;*get_num_one(p) = 199;printf("%d\n", g_num);
//——————————————————————————————————————————————4 int* parray = get_array();for (int i = 0; i < 3; i++) {printf("%d\n", parray[i]);}printf("\n");return 0;
}
!!!这里先看一下指针函数和函数指针的区别,下篇会将到函数指针,容易弄混!!!