移动微网站建设二维码,搜索引擎调词平台多少钱,网页设计选题,松江新城建设发展有限公司网站什么是指针#xff1f;
通俗来说指针就相当于地址#xff0c;因为我们写入的代码每个变量的数据类型不同#xff0c;字节大小不同#xff0c;在计算机内存中所开辟存储的大小自然不同#xff0c;且指针通常存储的是内存单元中最小单元的编号
比如#xff1a;int*指针的…什么是指针
通俗来说指针就相当于地址因为我们写入的代码每个变量的数据类型不同字节大小不同在计算机内存中所开辟存储的大小自然不同且指针通常存储的是内存单元中最小单元的编号
比如int*指针的大小是4个字节在内存存储地址分别为0x11223344 0x11223355 0x11223366 0x11223377那么int*指针存储的就是0x11223344内存地址(内存地址是我随便编的因为在计算机中内存存储中地址是计算机随机分配的地址)
#includestdio.h
int main()
{int a 10;//%p是用来打印地址的//a取地址符号printf(%p\n, a);return 0;
} 在内存中的分配 每个内存都有自己对应的唯一编号编号地址指针在写代码时创建的变量、数组、结构体等都要在内存上开辟空间。 为什么一个内存单元字节要设置为1个字节呢
经过前人的不断钻研和计算发现给内存单元一个字节大小是最优解。
在32位的计算机上有32根物理地址线在32根地址线在寻找地址的时候产生高电平(1)和低电平(0)
就是二进制的1和0 那么它们能产生的地址如下
也就是2^32次方 2^324294967296字节 能够管理4294967296内存字节空间
差不多4GB多一点内存空间
这就是为什么64位机器比32位机器内存空间大 比32位机器寻址能力强
在进行复杂的数据处理时性能强 速度快等优点。 #includestdio.h
int main()
{//在内存中开辟4个字节大小int a 10;//int*的指针变量 通过取地址将a中4个内存单元中最小字节存储到了指针变量p中int* p a;printf(%p\n, a);printf(%p\n, p);return 0;
}
p就是一个指针变量用来存放地址的变量。 指针大小
通过32位机器中32根地址线和64位机器中64根地址线就能发现指针大小不同。
一般在32位机器中不管指针变量存储的是什么数据类型的地址大小都为4个字节
一般在64位机器中不管指针变量存储的是什么数据类型的地址大小都为8个字节 VS2019 32位环境编译
#includestdio.h
int main()
{printf(%d\n, sizeof(int*));printf(%d\n, sizeof(char*));printf(%d\n, sizeof(short*));printf(%d\n, sizeof(double*));printf(%d\n, sizeof(long long*));return 0;
} VS2019 64位环境编译 指针和指针类型
在创建变量中系统提供的内置类型有很多比如int、short、double...等那么指针也有类型吗
指针也有对应的指针类型也是整形浮点型等因为指针存储的就是变量的地址。
#includestdio.h
int main()
{int a 0x11223344;int* pa a;*pa 0;return 0;
} 我们可以看见不同类型指针变量能够解引用*操作的空间与指针类型有关。
int*的指针解引用4个字节 char*的指针解引用1个字节
总结指针类型决定了指针解引用的操作权限。
(也就是指针解引用访问多少个字节空间取决于指针类型) Type*p
*p表明是一个指针变量 p指向的对象类型是Type p解引用的时候访问对象大小是sizeof(Type)。 int*paa 的意思是 *p表明是一个指针变量 p指向的对象类型是int
p解引用访问的对象大小字节是int(4个字节) #includestdio.h
int main()
{int a 0x11223344;int* pa a;char* pb (char*)a;printf(%p\n, pa);printf(%p\n, pa1);printf(%p\n, pb);printf(%p\n, pb1);return 0;
}
C4到C8是4个字节大小 C4到C5是一个字节大小 总结指针的类型决定了指针向前或者向后走一步有多大(距离) 。 什么是野指针
野指针野指针就是指针指向的位置是未知的(随机的不正确的没有明确限制的)。
比如野狗就是野指针没有主人它就没有温暖的家只能四处流浪。 *p就相当于野狗具有危险性
*p1NULL 相当于野狗拴上了狗链子危险性大大降低。
野指针是怎么形成的
1.指针没有初始化
#includestdio.h
int main()
{int* p;int a 10;*p a;return 0;
}
在*p没有初始化局部变量没有初始化希望默认为随机值一般不知道指针变量要指向哪个变量就置为空即可(NULL)。
在C语言中NULL就是0 2.指针越界访问
#includestdio.h
int main()
{int arr[5] { 0 };int* p arr;int i 0;for (i 0; i 5; i){*(p) i;}return 0;
} 当指针指向的范围超出了数组arr范围时就会造成野指针因为数组arr总共就5个数数组下标从0到4而for循环的判断条件却是小于等于5超出数组原本的范围形成了数组越界访问造成野指针的形成。在VS2019编译器会帮忙检查越界不同编译器不同如果你在工作时编译器不会帮你检查越界问题而代码运行不起来时,你就要开始自己疯狂挠头了所以要避免野指针的形成。 3.指针指向的空间释放
在动态内存管理malloc realloc calloc free忘记free开辟的动态内存空间一般情况下不用释放也行虽然不释放形成了野指针但只要没有人用就没有危险但一般动态内存开辟使用完记得free也是一个好习惯。 避免野指针的形成
1.指针初始化
2.小心指针越界
3.指针指向空间free释放置为空
4.指针使用之前检查有效性
#includestdio.h
#includestdlib.h
#includeassert.h
int main()
{//指向初始化int* p NULL;p (int*)malloc(sizeof(20));if (p NULL)//使用之前检查有效性(是否为空){perror(malloc fail);exit(-1);}//p!NULL*p 20;printf(%d\n, *p);free(p);p NULL;return 0;
} 指针和数组
指针和数组虽然是两个不同的东西但它们俩之间却有着千丝万缕的联系。
#includestdio.h
int main()
{int arr[10] { 0 };int* p arr[0];int i 0;for (i 0; i 10; i){*p i;p;}for (i 0; i 10; i){printf(%d , arr[i]);}return 0;
} 首先我们要注意数组名原则上会被解释为指向该数组起始元素的指针
但有两种情况下数组名不会为视为指向起始元素的指针。
1.作为sizeof运算符的操作数出现时
sizeof(数组名)不会生成指向起始元素的指针的长度而是生成数组整体的长度。
2.作为取地址运算符的操作数出现时
数组名不是指向起始元素的指针的指针而是指向数组整体的指针。 在数组中数组名arr和arr[0]的地址是相同既arr的值就是arr[0]的地址既arr[0]。
不管arr的元素有多少个只要元素类型有Type型arr的表达式类型就是Type*。
而在两个例外中可以发现sizeof数组名是整个数组的长度不再是指针。
而取地址数组名是指向的不是数组首个元素而是指向整个数组整体的指针。
arr1 可以发现B0到D8正好是数组10个元素的字节长度40。 将数组名视为指针也催生出了数组和指针的密切关系。 指针的初始值是arr[0],写成arr也是一样的指针p被初始化为指向数组arr的起始元素arr[0];
*p既然指数组首个元素的值通过指针本身p遍历数组将自己的值赋值给数组打印出了0~9。 指针运算符和下标运算符
#includestdio.h
int main()
{int arr[10] { 1,2,3,4,5,6,7,8,9,10 };int* p arr;int i 0;for (i 0; i 10; i){printf(a[%d]%p p%d%p\n,i,arr[i],i,pi);}printf(\n);return 0;
} 如果将指向数组内元素pi前写上解引用操作符*会怎么样呢
#includestdio.h
int main()
{int arr[10] { 1,2,3,4,5,6,7,8,9,10 };int* p arr;int i 0;for (i 0; i 10; i){printf(a[%d]%d p%d%d\n,i,*(arri),i,*(pi));}printf(\n);return 0;
} 我们看见arr[i]*(arri) pi*(pi)
由此我们可以推断 arr[0]*(arr0) arr[1]*(arr1) ....
通过交换律我们发现*(pi)*(ip) 这样一看是不是p[i]可以写成i[p]呢
答案是可以的
因为[]下标运算符是一个具有两个操作数的双目运算操作符下标运算符[]的操作数顺序是随意的
就像1221一样arr[1]和1[arr]也是等价的。
所以我们可以写成arr[i] i[arr] *(ia) *(ai) p[i] i[p] ....
但为了让别人看起来简洁高效最好不要使用i[arr]等容易出现错误的写法。 指针运算
指针-整数
#includestdio.h
int main()
{int arr[10] { 1,2,3,4,5,6,7,8,9,10 };int* p arr;int i 0;for (i 0; i 10; i){printf(a[%d]%d ,i,*p);}printf(\n);return 0;
} 指针-指针
#includestdio.h
int main()
{int arr[10] { 1,2,3,4,5,6,7,8,9,10 };printf(%d\n, arr[9] - arr[0]);return 0;
} 指针减去指针得到的数值是指针和指针之间的元素。
指针-指针的前提是指针和指针指向了同一块空间 。
指针之间可以相减但不能相加 二级指针
指针变量也是变量是变量就要开辟内存空间就会有所对应的地址。
#includestdio.h
int main()
{int a 20;int* p a;int** pp p;*(*pp) 100;printf(%d\n, a);return 0;
} int* *ppp *pp表示是一个指针变量 再通过解引用保存p的地址
*(*pp) *pp就是访问的就是p 再通过解引用操作 访问到*p的整个空间区域 就是a
因此最后结果是100。 指针数组
指针数组是指针还是数组
是数组是存放指针的数组。 整形数组和字符数组
整形数组存放整形的数组
字符数组存放字符的数组 指针数组
#includestdio.h
int main()
{char arr1[] abc;char arr2[] bcd;char arr3[] adas;char* parr[] { arr1,arr2,arr3 };int i 0;for (i 0; i 3; i){printf(%s\n, parr[i]);}return 0;
} parr存储三个字符数组再通过解引用保存各数组首元素值的地址再用for循环打印出来因为printf具有链接性只要知道首个元素的地址就能将后面直至\0之间的内容都自动打印出来。
指针数组就是存放指针(地址)的数组。 其实通过上面的例子我们可以变相的用指针数组模拟实现二维数组。
int* parr[3]是什么意思 parr[3]是一个数组里面有3个元素每个元素是一个整形指针。
#includestdio.h
int main()
{int arr1[] { 1,2,3 };int arr2[] { 4,5,6 };int arr3[] { 7,8,9 };int* parr[3] { arr1,arr2,arr3 };int i 0;for (i 0; i 3; i){int j 0;for (j 0; j 3; j){printf(%d , parr[i][j]);}printf(\n);}return 0;
} 数组指针
数组指针是数组还是指针
是指针
int*p是指向整形数据的指针
float*p1是指向浮点型数据的指针
那么数组指针就是指向数组的指针 以下两种哪个才是数组指针 int(*p1)[10]才是数组指针因为下标操作符[]的优先级高于解引用*的符号的优先级如果不加括号p就先会和[]先结合变成了指针数组。为了避免这种情况为了解引用先生效要先加括号。
因此int(*p1)[10]才是一个指向数组的指针指向的数组大小为10p1是一个指针变量指向一个数组。 数组指针既然指向的数组那数组指针存放的应该是数组的地址。 但一般我们写代码时很少这样使用数组指针。 一般是在二维数组传参时才使用数组指针。
#include stdio.h
void print_arr1(int arr[3][5], int row, int col)
{int i 0;for (i 0; i row; i){int j 0;for (j 0; j col; j){printf(%d , arr[i][j]);}printf(\n);}
}
void print_arr2(int(*arr)[5], int row, int col)
{int i 0;for (i 0; i row; i){int j 0;for (j 0; j col; j){printf(%d , arr[i][j]);}printf(\n);}
}
int main()
{int arr[3][5] { 1,2,3,4,5,6,7,8,9,10 };print_arr1(arr, 3, 5);print_arr2(arr, 3, 5);return 0;
} 二维数组其实本质就是一维数组。
二维数组的每一行可以理解为二维数组的一个元素而二维数组的每一行又是一个一维数组。
二维数组名也是就是首元素的地址也就是第一行的地址-一维数组的地址arr。
arr[0][0]是首元素地址 也就是arr 第一行的地址。 数组传参和指针传参
一维数组传参
#include stdio.h
void test1(int arr[])test1传的数组首元素地址而int arr[]本质上是指针 用来存放首元素地址 ok
{}
void test1(int arr[10])同理
{}
void test1(int* arr)以指针变量来存放首元素地址 ok
{}
void test2(int* arr[20])指针数组用来接收实参arr2 指针数组中首元素地址 ok
{}void test2(int** arr) 以二级指针来存储指针数组的地址 ok
{}
int main()
{int arr[10] { 0 };int* arr2[20] { 0 };test1(arr);test2(arr2);
}
二维数组传参
void test(int arr[3][5])数组int arr[3][5]本质上是数组指针 用来存储首元素地址 ok
{}
void test(int arr[][])错误 二维数组可以省略行 列不能省略
{}
void test(int arr[][5])同理 ok
{}void test(int *arr)错误 二维数组只能用数组指针接受
{}
void test(int* arr[5])错误 这是一个指针数组
{}
void test(int (*arr)[5])正确的 数组指针 用来存放数组的指针 可以通过(*arr)遍历来对应行的元素
{}
void test(int **arr)错误的 二维数组只能用数组指针接受
{}
int main()
{int arr[3][5] {0};test(arr);
} 一级指针传参
#include stdio.h
void print(int* p, int sz)
{int i 0;for (i 0; i sz; i){printf(%d\n, *(p i));}
}
int main()
{int arr[10] { 1,2,3,4,5,6,7,8,9 };int* p arr;int sz sizeof(arr) / sizeof(arr[0]);print(p, sz);return 0;
}
p实参其实就是arr 用指针变量当形参接受(没有任何问题)。
函数参数为一级指针时函数可以接受什么参数呢
#include stdio.h
void test(char* p)
{}
int main()
{char arr[] abcd;char ch 2;char* ptr ch;test(arr);test(ch);test(ptr);return 0;
} 二级指针传参
#include stdio.h
void test(int** ptr)
{printf(num %d\n, **ptr);
}
int main()
{int n 10;int*p n; //一级指针int **pp p;//二级指针test(pp);test(p);return 0;
} 当函数的参数为二级指针时函数可以接受什么参数呢
#include stdio.h
void test(char** p)
{}
int main()
{char* arr[5];char ch a;char* p ch;char* pp p;test(arr);test(p);test(pp);return 0;
}