指针
内存
分类:
运行内存 存储命令
注意
当我们程序运行时系统会在运行内存中开启一片空间给当前程序使用 32位机最多给一个程序开启4G的运行内存,64位8G 将开启的内存以1字节为单位进行划分,每个字节的内存都有其对应的地址编号 这些地址编号也是数据,其数据类型为指针
程序运行内存划分
栈 可读可写 可取地址 局部变量 堆 可读可写 静态全局区 可读可写 可取地址 全局变量,静态变量 文字常量区 可读 代码中编写的常量 如:10 'a' "Hello world"等 代码区 可读
B与b
B:字节 b:位
指针与指针变量
指针
概述
指针是地址的数据类型 指针变量是数据类型为指针的变量
注意
在32位平台下,地址总线是32位的,所以地址是32位编号,所以指针变量是32位的即4个字节。 在64位平台下,地址总线是64位的,所以地址是64位编号,所以指针变量是64位的即8个字节。
语法
数据类型* 注意:此时数据类型要和指向地址中存储的值的保持一致
指针变量
使用步骤
1.声明 extern 数据类型* 变量名; 2.定义 数据类型* 变量名; 3.赋值 变量名 = 值; 4.使用 变量名
注意:
指针变量的值是地址
空指针与野指针
空指针:指针变量存储的地址为空(即0) 野指针:指针变量存储的地址不知道指向哪里
运算符
& 取地址符 可以使用的区域,栈或静态全局区中 语法: &变量名 *:取值,改值 取值:获取指针变量存储的地址指向的(内存中存储的数据)值 *指针变量名 改值:修改指针变量存储的地址指向的(内存中存储的数据)值
注意
空指针或野指针操作会导致段错误 指针变量越界 *的含义 跟在数据类型后面表示这是一个指针类型 *在前表示解引用运算符,取指向地址中的值
void
作用
1.作为函数的返回值类型,表示返回值为NULL 2.万能指针
万能指针
作用
所有指针变量都可以赋值给万能指针的变量,无需强制转换
语法
void* 变量名;
注意
无法取值,因为没有指定跨度
使用
一般作为形参,可以接收任意类型的指针数据 作为函数的返回值类型,表示返回了一个地址
二级指针
概述
指向指针的指针
语法
数据类型* * 变量名 = &p; 注意:此时是指向一个一级指针变量的指针,所以为数据类型* 如: int a=10; int* p1 = &a; int* * p2 =&p1;
p2:p1的地址 *p2:p1中存储的地址 **p2:p1中存储的地址指向的地址中的值
#include<stdio.h> int main(int argc, char const *argv[]) { // printf("%d\n",); // printf("%p\n",); int num01=10; int num02=20; //p1:指向num的地址 int* p1=&num01; int* p2=&num02; //p2二级指针,指向的是指针p1的地址 int* * p3 = &p1; //此时*p3就是p1中指向的地址 printf("%p\n",*p3); printf("%p\n",p1); printf("%d\n",**p3); p3 = &p2; //此时*p3就是p2中指向的地址 printf("%d\n",**p3); // printf("p1 = %d\n",*p1); // printf("p2 = %d\n",**p2); // printf("%p\n",p1); // printf("%p\n",*p2); // printf("%p\n",p2); // int num02 = 1; // int* p3 = # // p3指向num的地址 // p2 = &p3; //p2记录的是指针p3的地址 // *p2 = # //让p3记录的地址变为num的地址 // **p2 = 1; //num=1 // printf("p2 = %d\n",**p2); // printf("num = %d\n",num); return 0; }
const与指针
概述
使用const与指针变量结合
常量指针(数据类型前+const)
概念
指向常量的指针,本质上是一个指针变量
语法
const 数据类型* 指针变量名 或 数据类型 const * 指针名称
特点
不能修改(其存储的地址指向的)值 但是可以修改其存储的地址
指针常量(变量名前+const)
概念
该指针变量是一个常量
语法
数据类型* const 指针名称;
特点
其存储的地址不可以被修改 但是(存储的地址指向的)值可以被修改
常量指针常量(都加)
概念
使用const修饰的指针变量指向一个常量
语法
const 数据类型* const 指针名称; 或 const 数据类型 * const 指针名称;
指针与数组元素
概述
数组是存储多个数据类型相同的数据的容器 故每一个数据都在数组中存储,每一个数据都有其存储的地址
如
#include<stdio.h> int main() { int nums[]={1,2,3,4,5}; nums[0]=10; printf("%d\n",nums[0]); int x=1; x=10; printf("x = %d\n",x); int* p1=&x; //获取数组中下标为0的元素的地址 int* p2=&nums[0]; printf("*p2 = %d\n",*p2); return 0; }
指针与数组
数组指针
概念
指向数组的指针,本质是个指针
如
int nums[]={1,2,3,4,5}; int* p1=nums;
指针数组
概念
存储(指针)地址编号的数组,本质上是一个数组
如
int n01 = 1; int n02 = 2; int n03 = 3; int n04 = 4; int n05 = 5; int* ps[] = {&n01,&n02,&n03,&n04,&n05}; int ns01[] = {11,12,13,14,15}; int ns02[] = {21,22,23,24,25}; int ns03[] = {31,32,33,34,35}; int* nss[] = {ns01,ns02,ns03}; for(int i = 0; i < 3; i++) { for(int j = 0; j < 5; j++) { printf("%d\t",nss[i][j]); } printf("\n"); }
函数与指针
函数指针
本质
一个指向函数的指针
定义语法
返回值类型(*变量名)(形参列表)=函数名; 注意:此时1形参列表中变量名可以省略不写
调用指向函数的语法
变量名(实参列表);
如
#include<stdio.h> void method1(); void method2(int num1,int num2); int main(int argc, char const *argv[]) { //定义一个p1指向method1 void (*p1)()=method1; //定义一个p2指向method2 void (*p2)(int,int)=method2; //调用method1 p1(); //调用method2 p2(2,3); return 0; } void method1() { printf("1111111\n"); } void method2(int num1,int num2) { printf("2222222\n"); }
小技巧
将声明Ctrl+c复制到定义的地方,然后将声明的函数名修改为(*指针名),后面再写=函数名; 即 void method1(); void (*p1)()=method1; void method2(int num1,int num2); void (*p2)(int,int)=method2;
返回值为指针的函数
本质
是一个返回值为指针的函数
语法
返回值类型* 函数名(数据类型 变量1,数据类型 变量2) { 函数体 }
使用函数指针记录当前函数
返回值数据类型* (*变量名)(形参列表) = 函数名;
函数的形参有指针
本质
是一个形参列表中有指针变量的函数
语法
返回值数据类型 函数名(...,void*,...) { 函数体 }
使用函数指针记录当前函数
返回值类型 (*变量名)(....,void*,...) = 函数名;
作用
将函数作为参数进行传递
如
#include<stdio.h> //加减乘除函数声明 extern int add(int num1,int num2); extern int sub(int num1,int num2); extern int mul(int num1,int num2); extern int div(int num1,int num2); //mymath函数声明 //传入num1,num2 以及带参函数的指针 //返回值为int型的指针 extern int* mymath(int num1,int num2,int(*p)(int,int)); int main(int argc, char const *argv[]) { //用p1指向add的地址 // int (*p1)(int,int)=add; // //用p3指向add的地址 // int (*p2)(int,int)=sub; // //用p3指向add的地址 // int (*p3)(int,int)=mul; // //用p4指向add的地址 // int (*p4)(int,int)=div; // int sum1=p1(10,2); // printf("%d\n",sum1); int* p5=mymath(10,2,add); printf("加:%d\n",*p5); int* p6=mymath(10,2,sub); printf("减:%d\n",*p6);//解引用地址 int* p7=mymath(10,2,mul); printf("乘:%d\n",*p7); int* p8=mymath(10,2,div); printf("除:%d\n",*p8); return 0; } int add(int num1,int num2) { return num1+num2; } int sub(int num1,int num2) { return num1-num2; } int mul(int num1,int num2) { return num1*num2; } int div(int num1,int num2) { return num1/num2; } int* mymath(int num1,int num2,int(*p)(int,int)) { // static int x = p(10,2); /*在 C 语言中,静态变量的初始化需要在程序启动时完成, 而且其初始化表达式不能依赖于函数调用的返回值(除非这个函数已经是一个常量表达式)。 具体来说,static int x = p(10, 2); 中,p 是一个函数指针,它的值在程序运行过程中是动态的。 由于静态变量的初始化是在程序加载时进行的, 而 p 的值直到运行时才会确定, 因此你不能在静态变量的初始化表达式中直接使用函数指针。*/ //如果不加static 则x(局部变量)在函数调用结束后销毁 static int x; x= p(num1,num2); //返回x的地址 return &x; }
总结
指针
地址编号的数据类型
指针变量
记录地址编号的变量
万能指针
可以接收任何类型地址变量的指针类型 语法: void *变量名; 注意: 不能取值
二级指针
存储一级指针地址的指针变量 语法: 数据类型** 变量名;
常量指针
指向常量的指针,本质是个指针 语法: const 数据类型 * 变量名; 数据类型 const * 变量名; 特点: 不能修改其指向的值 但是可以修改其存储的地址
指针常量
指针中存储的地址不能修改,顾本质上是一个常量 语法; 数据类型 * const 变量名; 特点: 可以修改其指向的值 但是不能修改其存储的地址
常量指针常量
指针中存储的地址与指向的值都不能修改 语法: const 数据类型 * const 变量名; 数据类型 const * const 变量名; 特点: 不能修改其指向的值 也不能修改其存储的地址
数组名的本质
数组中第一个元素的地址 数组地址不能修改,顾是一个常量指针
数组指针
数组中第一个元素的地址,本质是个指针 如: int nums[] = {1,2,3} //数组指针 int *p = nums; int x = 10; int* p2 = &x;
指针数组
存储指针的数组,本质是个数组
函数指针
指向函数的指针,本质是个指针
返回值类型为指针的函数
本质是个函数,返回值类型为指针类型
标签:p2,const,int,数据类型,C++,地址,指针 From: https://blog.csdn.net/2301_81457674/article/details/143816459const 数据类型 * 变量名;
数据类型 const * 变量名;
特点:
不能修改其指向的值
但是可以修改其存储的地址指针常量
指针中存储的地址不能修改,顾本质上是一个常量
语法;
数据类型 * const 变量名;
特点:
可以修改其指向的值
但是不能修改其存储的地址常量指针常量
指针中存储的地址与指向的值都不能修改
语法:
const 数据类型 * const 变量名;
数据类型 const * const 变量名;
特点:
不能修改其指向的值
也不能修改其存储的地址数组名的本质
数组中第一个元素的地址
数组地址不能修改,顾是一个常量指针数组指针
数组中第一个元素的地址,本质是个指针
如:
int nums[] = {1,2,3}
//数组指针
int *p = nums;int x = 10;
int* p2 = &x;指针数组
存储指针的数组,本质是个数组
函数指针
指向函数的指针,本质是个指针
返回值类型为指针的函数
本质是个函数,返回值类型为指针类型