c语言—指针非常全面、详细

2023-11-20

目录

一、初步认识指针(一级)

二、数组指针

1.一维数组与指针

2.二维数组与指针

三、函数指针

四、指针数组

2.函数指针数组

五、指针函数

六、二级(多级)指针

七、指针定义的归纳


一、初步认识指针(一级)

        1.指针变量: 指针变量是一个特殊的变量,这个变量存储的是一个地址,也可以说指针就是一个地址。

        2.取值运算符: *(&a)取值运算符,他把后面跟的内存地址中的数据”取出来“

        3.说明符:int a = *P, 说明符,说明这个p是一个存放地址的整型指针变量

        定义格式:类型  *(说明符)  指针名    例如 int *a、char *b

#include<stdio.h>

int main()
{
	//指针变量 存放指针 地址的变量
	//指针变量==存放地址的变量
	int a  = 10;
	int *P;//(*标识符)标识作用声明与定义 整型的指针变量 保存别人地址
	p = &a;//p存放是地址,&a是a的地址
	printf("%d\n",a);
	printf("0x%p\n",&a);//0x  地址十六进制 
	printf("%d\n",*(&a));//取值(运算符)运算作用,他把后面跟的内存地址中的数据”取出来“
	printf("%d\n",*p);//取值运算符
	return 0;
}

      

         4.指针类型:既然存放的是地址那为什么要有类型呢?难道地址有类型?当然不是,不管什么类型,地址都是4个字节。取值运算符会根据指针变量类型,访问不同大小的空间。

        例如:int *P访问4个字节、char *p访问1个字节如果char *p存放int 类型的变量的地址,那么char类型指针只能访问int类型变量(int类型 四个字节)的一个字节,从而丢失了三个字节。

#include <stdio.h>

int main()
{
	int a = 1234;
	int *p = &a;
	char *c = &a;//char类型指针 存放int类型变量
	
	printf("p = %p\n",p);//输出地址
	printf("c = %p\n",c);
	
	printf("a = %d\n",*p);//类型相同,输出正确
	printf("a = %d\n",*c);//变量类型与存放地址指针类型不同,空间大小不匹配,输出错误数据
	
	printf("++p = %p\n",++p);//自加1 类型所以含有的空间大小为一(4个字节)
	printf("++c = %p\n",++c);//(一个字节)
	return 0;
}

      

         5.为什么需要指针:指针的使用使得不同区域的代码可以轻易的共享内存数据,因为操作的是同一个地址上的数据。

#including<stdio.h>

void chageData(int *pdata, int *pdata2)
//用指针 给到的是与main相同的地址 所以交换的数值是在同一个地址上的数据
//不用指针 操作的是在新的空间地址上
{
	int tmp;
	tmp   = *pdata;
	*pdata  = *pdata2;
	*pdata2 = tmp;
}

int main()
{
	int data  = 10;
	int data2 = 20;
	
	printf("why point\n");
	printf("交换前:data=%d,data2=%d\n",data,data2);
	
	chageData(&data,&data2);//操作的是同一个地址上的数据
	
	printf("交换后:data=%d,data2=%d\n",data,data2);
}

    

二、数组指针

        是一个指针,指向数组的指针。

    

1.一维数组与指针

        1.指针的增量与数组的关系

#include <stdio.h>

int main()
{
	
	int arr[3] = {1,2,3};
	int *p;

	//数组名就是数组的首地址,数组的首地址就是首个元素的地址相当于p = &arr[0];
	p = arr;
	for(int i = 0; i<3; i++){// 第一种输出各个指针
		//printf("address:0x%p,%d \n",(p+i),*(p+i));//输出地址与数值
		printf("%d \n",*p);
		p++;//指针偏移加1,指向下一个地址(下一个数组元素)
	}
	
	
	p = arr;//防止越界 p+4越界出错
	for(int i = 0; i<3; i++){//第二种输出各个指针
		//printf("address:0x%p,%d \n",(p+i),*(p+i));//输出地址与数值
		printf("%d ",*p++);
	}
	/*
	printf("0元素是:%d\n",*(p+0));// +i  地址偏移一个类型大小
	printf("1元素是:%d\n",*(p+1));
	printf("2元素是:%d\n",*(p+2));
	*/
	return 0;
}

       运行结果:

      

        2.指针与数组名的奇怪用法

#include <stdio.h>

int main()
{
	int arr[3] = {1,2,3};//数组
	int *p = arr;//指针变量
	
	//sizeof计算大小,单位字节
	printf("sizeof arr is %d\n",sizeof(arr));//3*4=12
	printf("sizeof arr is %d\n",sizeof(p));// 指针都用8个字节表示一个地址
	printf("sizeof int is %d\n",sizeof(int));//4个字节
	printf("sizeof pointer is %d\n",sizeof(int *));// 用8个字节表示一个地址
	printf("sizeof pointer is %d\n",sizeof(char *));// 用8个字节表示一个地址
	//printf("%d ",p[2]);
	
	printf("%d \n",*arr);//取首地址的元素
	for(int i = 0; i<3; i++){
		printf("%d ",p[i]);//(2)指针当作数组名用
	}
	
	putchar('\n');

	for(int i = 0; i<3; i++){//(3)数组拿来加
		printf("%d ",*(arr+i));
	}
	
	putchar('\n');
	/*
	for(int i = 0; i<3; i++){
		printf("%d ",*arr++);//编译不过,指针常量  数组为固定大小空间数值无法改变与指针不同
	}*/
	putchar('\n');
	return 0;
}

运行结果:

    

        3.指针代替数组函数分装

#include <stdio.h>

void initArray(int *parr, int size)//传递的数组用指针代替 parr为地址
//指针间接的在main空间里面操作
{
	int i;
	for(i=0;i<size;i++){
		printf("请输入第%i个元素的数据:\n",i+1);
		scanf("%d",parr++);//输完之后加(地址)
	}
}
void printArray(int *parr, int size)
{
	int i;
	for(i=0;i<size;i++){
		printf("%d ",*parr++);//取地址的值
	}
}

int main()
{
	int arry[5];
	int size = sizeof(arry)/sizeof(arry[0]);
	
	initArray(arry,size);//实际参数,数组的首地址: 名,首个元素的地址
	printArray(&arry[0],size);
	return 0;
}

 运行结果:

     

        4.例:将输入数据按倒序输出

                想法:数字是数组下标

                 

#include <stdio.h>
//name , params, returnValue

void initArray(int *parr, int size)
{
	int i;
	for(i=0;i<size;i++){
		printf("请输入第%i个元素的数据:\n",i+1);
		scanf("%d",parr++);
	}
}


void revangeArry(int *parr, int size)
{
	int i,j;
	int tmp;
	
	for(i=0;i<size/2;i++){//将数组元素按上图交换
			j = size-1-i;
			tmp = parr[i];
			parr[i] = parr[j];
			parr[j] = tmp;
	}
	
}

void printArray(int *parr, int size)
{
	int i;
	for(i=0;i<size;i++){
		printf("%d ",*parr++);
	}
	putchar('\n');
}

int main()
{
	int arry[5];
	int size = sizeof(arry)/sizeof(arry[0]);
	
	initArray(arry,size);//实际参数,数组的首地址: 名,首个元素的地址
	printArray(&arry[0],size);//输出数组
	revangeArry(arry,size);//把数组倒序
	printArray(&arry[0],size);//输出倒序数组
	return 0;
}

 运行结果:

      

2.二维数组与指针

          1.说明介绍

        二维数组本质还是数组,不同点是二维数组元素还是个数组(子数组)

        a表示父数组的地址,a[0],*a表示子数组的地址

        各种表示形式的含义

    

         2.数组名与偏移与地址

#include <stdio.h>

int main()
{
	int arr[3][4] = {{11,22,33,44},{12,13,15,16},{22,66,77,88}};
	int arr1[4] = {11,22,33,44};
	
	//二维
	printf("1arr是父亲地址:%p,偏移1后是%p\n",arr, arr+1);//偏移4列*4字节=16
	
	printf("2arr[0]是子数组地址;%p,偏移1后是%p\n",arr[0],arr[0]+1);//4字节
	//对二维数组名取内容=数组的首地址
	printf("3arr[0]是子数组地址;%p,偏移1后是%p\n",*(arr+0),*(arr+0)+1);//4字节
	printf("4arr[0]是子数组地址;%P,偏移1后是%p\n",*arr,*(arr+1));
	printf("5arr[0]是子数组地址;%d,偏移1后是%d\n",*arr,*(arr+1));//不可取值
	
	//一维
	printf("6arr[0]是子数组地址;%p,偏移1后是%p\n",arr1[0],(arr1[0]+1));//错
	printf("7arr[0]是子数组地址;%p,偏移1后是%p\n",*(arr1+0),*((arr1+0)+1));
	printf("8arr[0]是子数组地址;%P,偏移1后是%P\n",*arr1,*(arr1+1));
	printf("9arr[0]是子数组地址;%d,偏移1后是%d\n",*arr1,*(arr1+1));//可取值
	
	return 0;
}

运行结果:

     

        3.输出数组地址数值

#include <stdio.h>

//arr,arr[0]
int main()
{
	int arr[3][2] = {{11,22},{32,44},{55,66}};
	int i;
	int j;
	
	for(i=0;i<3;i++){
		for(j=0;j<4;j++){
			printf("add:0x%p,data:%d \n",&arr[i][j],arr[i][j]);//输出方式一
			printf("add:0x%p,data:%d \n",arr[i]+j,*(arr[i]+j));//方式二
			printf("add:0x%p,data:%d \n",*(arr+i)+j,*(*(arr+i)+j));//方式三 arr[i]地址==*(arr+i)地址
			printf("===========================================\n");
		}
		putchar('\n');
	}
	
}

运行结果:

      

        4. 数组指针   

数组指针定义:int (*p2)[i];

int 类型,p2是数组指针名,i是二维数组列数

#include <stdio.h>

//arr,arr[0]
int main()
{
	int arr[3][4] = {{11,22,33,44},{12,13,15,16},{22,66,77,88}};
	int i,j;
	//int *p;
	//p++不等arr++
	//p = &arr[0][0];
	//p = arr;
	
	/*能不能定义一个指针,让指针偏移的时候,也偏移对应大小的数组?
	数组指针,定义一个指针,指向一个数组!
	数组指针才是真正等同于二维数组名 arr++=p2++*/
	
	int (*p2)[4];//数组指针
	p2 = arr;
	//printf("p2=%p\n",p2);
	//printf("++p2=%p\n",++p2);//先加 偏移16  arr++=p2++
	for(i=0;i<3;i++){
		for(j=0;j<4;j++){
			printf("%d\n",*(*(p2+i)+j));//*(*(arr+i)+j)=*(arr[i]+j)
		}
		
	}
}

运行结果:

       

        5.例:输入行列用指针取数组值

#include <stdio.h>

int getTheData(int (*p)[4],int hang,int lie)//了解二维数组对应的数组指针的用法
{
	int data;
	data = *(*(p+hang-1)+lie-1);
	return data;
	//return p[hang-1][lie-1];//把p当作数组名用
}
void tipsInputHangLie(int *pm, int *pn)
{
	printf("输入行列值:\n");
	scanf("%d%d",pm,pn);
	puts("done!");
}
//arr,arr[0]
int main()
{
	int arr[3][4] = {{11,22,33,44},{12,13,15,16},{22,66,77,88}};//arr+
	int ihang,ilie;
	int data;
	
	
	tipsInputHangLie(&ihang,&ilie);//输入行列值
	data = getTheData(arr,ihang,ilie);//找出对应行列值的那个数
	printf("%d行%d列的值是%d\n",ihang,ilie,data);//输出这个数
}

运行结果:

    

三、函数指针

        指向函数的指针,函数名就是地址。

 函数指针定义:int (*p2)(x);

int返回值类型,p2函数指针名,x是传递参数的类型与名(例如int data)

        1.函数指针定义与调用

#include<stdio.h>

int invdata(int data)
{
	return ++data;
}

void printwelcome()
{
	puts("欢迎来到地球");
}

int main()
{
	int a = 10;
	int *pa;
	pa = &a;
	printf("%d\n",*pa);
	
	int (*p2)(int data);//定义一个函数指针变量
	p2 = invdata;
	(*p2)(10);//调用传递实参
	printf("%d\n",(*p2)(10));
	
	void (*P)();//定义一个函数指针变量
	P = printwelcome;//对指针给地址
	//void (*P)(printwelcome);//定义
	printwelcome();//调用
	(*P)();//调用
	return 0;
}

运行结果:

     

四、指针数组

        是一个数组 ,数组里每一个元素都是一个地址(相当于由一群指针变量组成的数组)

     

         1.指针数组的定义及使用

#include<stdio.h>

int main()
{
	int a = 10;
	int b = 20;
	int c = 30;
	int d = 40;
	
	int *p[4] = {&a,&b,&c,&d};//定义指针数组
	for(int i=0;i<4;i++)
	{
		printf("%d  ",*(p[i]));
		
	}
	return 0;
}

运行结果:

     

        2.函数指针数组

        由函数指针组成的数组。

函数指针数组定义:int (*pfunc[ i ])(x)

int是类型,pfunc函数指针数组名称,i是元素个数,x是传递参数的类型与名

#include <stdio.h>
#include <stdlib.h>

int getMax(int data1, int data2)//返回大值
{
	return data1>data2 ? data1:data2;
}

int getMin(int data1, int data2)//返回小值
{
	return data1<data2 ? data1:data2;
}

int getSum(int data1, int data2)//返回和值
{
	return data1+data2;
}
int main()
{	
	int a = 10;
	int b = 20;
	int ret;
	
	int (*pfunc[3])(int , int )={getMin,getMax,getSum};//定义函数指针数组
	
	for(int i=0;i<3;i++){//调用3个函数
		ret = (*pfunc[i])(a,b);
		printf("ret = %d\n",ret);
	}
	
	return 0;
}

运行结果:

    

五、指针函数

        返回值为指针的函数。

    

        1.例子

#include <stdio.h>

int *getPosPerson(int pos,int (*pstu)[4])//指针函数 返回一个指针
{
	int *p;
	p = (int *)(pos + pstu);//(int * )转成整型指针(ins)整型
	return p;//返回子数组的地址
}

int main()
{
	int scores[3][4]={
		{55,66,77,88},
		{66,55,99,100},
		{11,22,33,59},
	};
	int *ppos;
	int pos;
	printf("请输入你需要看的学生号数:0,1,2\n");
	scanf("%d",&pos);
	
	ppos = getPosPerson(pos, scores);
	for(int i=0;i<4;i++){//输出子数组数据
		printf("%d ",*ppos++);
		
	}
	return 0;
}

运行结果:

    

六、二级(多级)指针

        二级指针内存存放的是下一个指针的地址,而下一个指针内存存放数据的地址。

        如同俄罗斯套娃下一个指针存放上一个指针的地址

    

        1.二级(多级)指针的定义及使用

#include <stdio.h>

int main()
{
	int data = 100;
	int *p = &data;//一层
	printf("data的地址是:%p\n",&data);
	printf("p保存data的地址:%p,内容是%d\n",p,*p);
	printf("p的地址是:%p\n",&p);
	
	/*可以用一级指针变量存放指针变量的地址,但是使用有缺陷,无法获得最终地址内容
	printf("p的地址是:%p\n",&p);
	int *pp = &p;
	printf("pp保存p的地址:%p\n",pp);
	printf("*pp是%p\n",*pp);*///得到的是dara的地址不是data的值   *p存放的地址
	
	printf("\n");
	int **p2;//定义二级指针
	p2 = &p;//二层,二级指针存放一级指针的地址
	printf("p2保存p的地址:%p\n",p2);
	printf("*p2是%p\n",*p2);
	printf("**p2来访问data:%d\n",**p2);
	printf("p2的地址是:%p\n",&p2);
	
	printf("\n");
	int ***p3;//定义三级指针
	p3 = &p2;//三层,三级指针存放二级指针的地址
	printf("p3保存p2的地址:%p\n",p3);
	printf("*p3是%p2\n",*p3);
	printf("***p3来访问data:%d\n",***p3);
	return 0;
}

 运行结果:

    

        2.例子

#include <stdio.h>


void getPosPerson(int pos, int (*pstu)[4],int **ppos)//二级指针
{
	*ppos = (int *)(pstu+pos);//把地址存放在一级指针的内存里
}

int main()
{
	int scores[3][4]={
		{55,66,77,88},
		{66,55,99,100},
		{11,22,33,59},
	};
	
	int *ppos;//野指针 空指针 不指向任何地址
	int pos;
	printf("请输入你需要看的学生号数:0,1,2\n");
	scanf("%d",&pos);
	
	getPosPerson(pos,scores,&ppos);
	for(int i=0;i<4;i++){
		printf("%d ",*ppos++);
	}
	return 0;
}

运行结果:

     

七、指针定义的归纳

        1.各种指针的定义

     

         2.各种指针变量的含义

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

c语言—指针非常全面、详细 的相关文章

  • elasticsearch query bool nested对象 聚合

    复杂的查询 下面的查询是小编自己研究写的一个比较复杂 的语句 涉及到查query bool must 已经过滤条件term 关键词介绍 from 表示从第几条开始返回 size 表示返回的题目数大小 query 查询语句 bool must
  • 抢「.AI」域名,成了 3000 万美元的大生意

    作者 汤一涛编辑 靖宇 1848 年 一个木匠在加利福尼亚州科罗马地区的一条河中偶然发现了金箔 尽管他曾试图保守这个秘密 但是金子不会说谎 一场席卷全美的淘金热就此开始 许多人怀揣着一夜暴富的梦想涌入加利福尼亚 1847 到 1870 年间
  • tensorflow-GPU版本安装,RTX3060

    首先需要安装好pycharm和anaconda 开始安装tensorflow GPU版 一 查看版本 1 首先检查自己电脑的cudn版本 开始 gt 设置 gt 应用 gt 安装的应用 搜cuda 我的是cuda 11 6 2 查看显卡型号

随机推荐

  • qt线程

    qt4 7 之前的线程操作 代码 需要重写一个类 继承QTread class mythread public QThread Q OBJECT 必须加 否则出现一些奇怪问题 public mytherad protected void r
  • 自动化办公-3.python自动化之word操作

    一 课前准备 python 处理 Word 需要用到 python docx 库 终端执行如下安装命令 pip3 install python docx 备注 可能word用的少 这块并没认真 但是后面的邀请函这块可以作为有用参考 没准会用
  • 什么是SSL协议?

    转自 微点阅读 https www weidianyuedu com 什么是SSL协议 SSL协议是一种安全传输协议 SSL是SecureSocketLayer的缩写 即安全套接层协议 该协议最初由Netscape企业发展而来 目前已经成为
  • 在本地部署自己的漏洞文章武器库(详细步骤说明)

    在本地部署自己的漏洞文章武器库 在平时我们进行web渗透的过程中 有些poc CVE详细情况需要进行查询之后才能进行利用 对于初级渗透人员来说 对漏洞情况和利用方式掌握不足的情况下 打造属于自己的漏洞武器库就至关重要了 可以提高平时工作和h
  • 调试osgEarth(十五)分页瓦片加载器在更新遍历时对请求处理过程(1)

    感谢 hankern 学习链接https blog csdn net hankern article details 84195754 继续调试 我感觉有两点 1 处理能处理的请求 2 摒弃处理过的请求和超时的请求 从上图可以看到 每帧可以
  • Mybatis进阶--批量新增数据

    一 传统JDBC进行批处理操作 package jdbc import java io InputStream import java sql import java util Properties public class jdbcUti
  • maven更换阿里云镜像

    1 setting文件增加maven节点
  • numpy索引与切片

    一 整数索引 作用 要获取数组的单个元素 指定元素的索引即可 例子 x np array 1 2 3 4 5 6 7 8 print x 2 3 x np array 11 12 13 14 15 16 17 18 19 20 21 22
  • listview item设置点击跳转_Flutter之路由及页面跳转与返回

    1 路由跳转到页面思路 设计三个按钮 然后三个点击事件 利用Navigator of context push MaterialPageRoute builder context gt page 进行跳转新页面2 页面折回到路由在跳转的页面
  • 小程序v-for与key值使用

    小程序中的v for和key与Vue中的用法基本相同 v for用于循环渲染列表 key用于给每个循环项分配一个唯一的标识 使用v for时 通常建议使用wx for代替 例如
  • SQL Server 中给字段设置默认值的方式

    1 在创建表的时候创建默认值 if object id T U is not null drop table T GO create table T ID int Name varchar 20 LoginTime datetime def
  • RecyclerView应用 —— 好友列表实现

    实现的效果类似于QQ好友列表 点击可展开 再次点击收起 两个Item的布局都很简单 这里就不给布局代码了 值得一提的是 RecyclerView本身并没有ListView那样的点击效果 想要类似效果可以为Item的根布局写个Selector
  • JS混淆加密的代码如何解密

    科普简介 混淆是指将 JavaScript 代码变得难以理解的过程 这可以通过更改变量名 函数名和类名 以及将代码压缩到一行来实现 混淆的主要目的是使代码难以被盗用 并保护代码的知识产权 功能作用与常用的解决方案 混淆后的代码很难阅读 但是
  • Maven进阶-配置仓库

    1 1maven介绍 Maven是一个项目管理和综合工具 Maven提供了开发人员构建一个完整的生命周期框架 开发团队可以自动完成项目的基础工具建设 Maven使用标准的目录结构和默认构建生命周期 主要服务于基于Java平台的项目构建 依赖
  • Linux系统如何看目录属于哪个磁盘分区

    Linux是先有目录 再有磁盘分区 df h 目录 例如 没有挂载磁盘的目录 显示在系统盘 root iZ2ze57v3n0zma46zqiq8nZ sh 1 5 5 df h alidata Filesystem Size Used Av
  • Unity使用spine动画

    Unity使用spine动画 在 Unity 中 常常使用 Spine 来制作一些动画 引擎本身并不能直接播放 Spine 动画 需要额外导入一个 RunTime 插件库才能支持 官网插件导入 当然 也可以到 Spine 官网关于 Unit
  • 机器学习原理(1)集成学习基本方法

    一 什么是集成学习 集成学习 ensemble learning 通过将多个学习器进行组合来完成学习任务 下图显示集成学习的一般结构 取自周志华老师的西瓜书 个体学习器通常由一种现有的学习算法从训练数据产生 例如决策树 C4 5 CART
  • C语言之——自定义数据类型

    目录 前言 什么是自定义数据类型 一 自定义数据类型之 数据类型命名 1 深入应用typedef 二 自定义数据类型之 结构体类型命名 1 深入理解struct结构体 三 自定义数据类型之 联合体类型命名 1 union与struct的区别
  • FreeRTOS多任务调度器基础

    Cortex M4中SysTick调度器核心 Cortex M4中的中断管理 Cortex M4中影子栈指针 Cortex M4中SVC和PendSV异常 1 Cortex M4中SysTick调度器核心 systick每一次中断都会触发内
  • c语言—指针非常全面、详细

    目录 一 初步认识指针 一级 二 数组指针 1 一维数组与指针 2 二维数组与指针 三 函数指针 四 指针数组 2 函数指针数组 五 指针函数 六 二级 多级 指针 七 指针定义的归纳 一 初步认识指针 一级 1 指针变量 指针变量是一个特