C语言指针初进阶知识汇总

news2024/9/20 22:26:30

目录

1 指针

1.1 指针是乜嘢

1.2 指针的声明

1.3 运算符

1.4 简单的小例子们

1.5 指针的运算

1.5.1 指针加减运算

1.5.2 间址运算

1.5.3 指针的相减 = 两个地址之间的偏移量

2 指针与数组

2.1 指针和一维数组

2.1.1 定义数组及指针变量

2.1.2 能动手就不要瞎扯

2.2 指针和二维数组

2.2.1 定义

2.2.2 能动手就不要瞎扯

3 指针数组

3.1 定义

3.2 灵活的双手动起来

4 多级指针

5 字符指针与字符串

5.1 定义

5.2 打代码

6 指针型函数

6.1 定义

6.2 敲代码

7 指向函数的指针

7.1 定义

7.2 码代码

8 结构体指针

8.1 结构体变量指针

8.1.1 定义

8.1.2 干活

8.2 结构体数组指针

8.2.1 定义

8.2.2 搬砖

1 指针

1.1 指针是什么

指针(pointer):一个值为内存地址的变量。

char 类型变量的值是字符,int 类型变量的值是整数,指针变量的值是地址。

1.2 指针的声明

数据类型 *指针名,这里的 * 表明声明的变量是一个指针,没有访问指针目标的含义

int * pi; // pi是指向int类型变量的指针
char * pc; // pc是指向char类型变量的指针
float * pf; // pf是指向float类型变量的指针

指针存放的值是一个地址,在大部分系统内部,该地址由一个无符号整数表示,但不能把指针认为是整数类型。

  • 指针变量不能赋予常量值
  • 不能接受键盘输入的数据

1.3 运算符

间接运算符 *:访问地址的目标

val = *ptr; // 找出ptr指向的值

地址 & 运算符:取变量的存储地址

&num 表示变量 num 的地址。

1.4 简单的小例子们

#include <stdio.h>
 
int main ()
{
   int  num = 97;   
   int  *p;        // 指针变量的声明 
   p = &num;       //在指针变量中存储 num 的地址 
 
   printf("Address of var variable: %p\n", &num );
   // 指针变量中存储的地址 
   printf("Address stored in ip variable: %p\n", p );
   // 使用指针访问值 
   printf("Value of *ip variable: %d\n", *p );
   return 0;
}

结果:

图解

 

1.5 指针的运算

知识点:

  • 三个操作符的优先级:() > [] > *,没错 * 是最卑微的
  • *p++ 里,* 号和 ++ 属于同一优先级,且方向都是从右向左的,*p++ 和 *(p++) 作用相同。

1.5.1 指针加减运算

#include<stdio.h>
 
void main(){
	int a=10,b=20,c=30;
	int *p=&b;
	printf("%p\n",&a);
	printf("%p\n",&b);
	printf("%p\n",&c);
	p=p+1;
	printf("%p\n",p);
	p=p-2;
	printf("%p\n",p);	
} 

 结果:

 

1.5.2 间址运算 

*p++:* 与 ++ 同优先级,结合方向为自右向左,p++,先赋值再自加,因此结果等价于 *p 返回其值,p++

*++p:++p,先自加后赋值,等价于 p++, *p 返回其值

(*p)++:括号优先,等价于 *p 返回其值,然后将 *p 的结果 + 1

#include<stdio.h>
 
int main(){
	int a[] = {10,20,30}, *p=a; 
	printf("*p++ is %d\n", *p++);   
	p=a; 
	printf("*++p is %d\n", *++p); 
	p=a; 
	printf("++(*p) is %d\n", ++(*p)); 
	p=a;
	printf("a[0] is %d\n",a[0]);
	printf("(*p)++ is %d\n", (*p)++); 
	return 0;
}

结果:

两个指针相减

首先必须明确,通常只有当两个指针指向同一个数组时才对两个指针相减。

两个指针相减得到的整数是这两个指针所指向的元素索引值之差。

例如有如下代码:

int a[5] = {10, 20, 30, 40, 50};

int *p1 = a;

int *p2 = &a[3];

int x = p2 - p1;

所以表达式 p2 - p1的值是3

2 指针与数组

2.1 指针和一维数组

2.1.1 定义数组及指针变量

数组名 s 代表的是该数组首元素的首地址,&s 代表的是整个数组的首地址

s == &s[0],返回 ture

&s == &s[0], 是错误的比较,尽管 &s 与 &s[0] 的地址值相同,但两者的含义不同

 

2.1.2 能动手就不要瞎扯

移动指向数组的指针

 数组元素的引用方法:

因为 ps 是变量,s 是符号常量,不能给 s 赋值, s = ps(×) 

#include<stdio.h>
 
int main(){
	int i;
	int a[5] = {1,2,3,4,5}, *p = a;   // p作为一维数组 a 的指针
	// 通过 a[i] 方式输出所有元素
	for(i=0; i<5; i++){
		printf("%d ",a[i]);
	} 
	printf("\n");
	// 通过 *(a+i)方式输出所有元素
	for(i=0; i<5; i++){
		printf("%d ",*(a+i));
	}
	printf("\n");
	// 通过 *(p+i)方式输出所有元素,p不移动 
	for(i=0; i<5; i++){
		printf("%d ",*(p+i));
	}
	printf("\n");
	printf("a is %p\n", a);
	printf("p is %p\n", p);
	// 通过 *p方式输出所有元素,p移动 
	for(i=0; i<5; i++){
		printf("%d ",*p++);
	}
	printf("\n");
	printf("%p", p);
}

结果:

2.2 指针和二维数组

参考:C/C++二维数组总结

2.2.1 定义
(1) 二维数组定义

定义了一个Array[3][4],那就指的是定义了一个三行四列的矩阵形状的二维数组,如下图所示。这样的矩阵在内存中是以箭头右边的方式存放的,也就是说实际上我们定义的二维数组在内存中仍然像是一维数组那样连续存储的。可以想象为把一个矩阵一层层伸展铺平。

int array[3][4];   // 内含 int 数组的数组

数组名 array 是该数组首元素的地址,一维数组首元素的地址,而 array[0] 本身是一个内含 4 个整数的数组,

array = &array[0]。

&array 是整个数组的首地址。

array,&array,&array[0],&array[0][0],虽然地址值相同,但有各自的含义。
例子:

#include<stdio.h>
 
int main(){
	
	int array[3][4] = {{1, 2, 3, 4}, {5, 6, 7, 8}, {9, 10, 11, 12}};
	
	printf("array is %d\n",array);
	printf("*array is %d\n",*array);
	printf("array[0] is %d\n",array[0]);
	printf("*array[0] is %d\n",*array[0]);
	printf("array[0][0] is %d\n",array[0][0]);
	printf("&array[0][0] is %d\n",&array[0][0]);
}

 结果:

 

(2) 二维数组指针定义

基类型 (*指针变量)[整形表达式]

int a[2][3], (*p)[3]  = a;

() 优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是 n,也可以说是 p 的步长。执行 p+1 时,p 要跨过 n 个整型数据的长度。

p[i]:指向 a[i] 的元素

p++:指向数组 a 的后一个一维数组元素

p--:指向数组 a 的前一个一维数组元素

p+1:等价于 a+1

当 p 指向 a 数组的开头时,可以通过以下形式来引用 a[i][j]

*(p[i]+j):对应于 *(a[i]+j)

*(*(p+i)+j):对应于 *(*(a+i)+j)

*(*(p+i))[j]:对应于 *(*(a+i)+j)

p[i][j]:对应于 a[i][j]

数组指针 p 与对应的二维数组 a 的区别是:二维数组 a 是一个常量,数组指针 p 是一个变量

2.2.2 能动手就不要瞎扯

#include<stdio.h>
 
void main(){
	int i, j;
	int a[2][3] = {{1,2,3},{4,5,6}}, (*p)[3] = a;
	
	// 通过 a[i][j] 方式输出所有元素 
	for(i=0; i<2; i++)
		for(j=0; j<3; j++)
	printf("%d ",a[i][j]);	
	printf("\n");	
	
	// 通过 *(*(a+i)+j) 方式输出所有元素 
	for(i=0; i<2; i++)
		for(j=0; j<3; j++)
	printf("%d ",*(*(a+i)+j));	
	printf("\n");
	
	// 通过 *(p[i]+j) 方式输出所有元素 
	for(i=0; i<2; i++)
		for(j=0; j<3; j++)
	printf("%d ",*(p[i]+j));	
	printf("\n");
	
	// 通过 *(*(p+i)+j) 方式输出所有元素 
	for(i=0; i<2; i++)
		for(j=0; j<3; j++)
	printf("%d ",*(*(p+i)+j));	
	printf("\n");
				
}

3 指针数组
3.1 定义
存储指针的数组。它的每个元素都是指针变量,指向相同的数据类型。

数组类型 *指针数组名[元素个数];

int *p[3]; 

[] 比 * 的优先级高,p 先与 [3] 结合,形成 p[3] 的数组形式,它有 3 个元素,然年再与 * 结合,表示是指针类型的数组。

int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2]

 

使用指针数组更方便地操作长短不一的字符串,而使用二维数组来操作字符串相对浪费空间,如下:

 

3.2 灵活的双手动起来

#include <stdio.h>
 
const int MAX = 3;
 
int main ()
{
   const char *words[] = {"wyd","is","smart"};
   
   int i = 0;
   for ( i = 0; i < MAX; i++)
   {
      printf("Value of words[%d] = %s\n", i, words[i] );
   }
   return 0;
}

4 多级指针

二级指针:就是对一级指针再取地址

三级指针:就是对二级指针再取地址

#include <stdio.h>
 
const int MAX = 3;
 
void main ()
{
   int *p, **pp, ***ppp, s = 100;
 
   p = &s;
   // q 存 p 的地址,p 存 s 的地址
   pp = &p;
   ppp = &pp;
   
   printf("Value of p = %d\n", *p); 
   printf("Value of pp = %d\n", **pp);
   printf("Value of ppp = %d\n", ***ppp);
   
}

结果

5 字符指针与字符串

5.1 定义

指向 char 型数据的指针

 

 

 

 

5.2 打代码

#include <stdio.h>
 
void main ()
{
	char s [] = "wyd is smart";
	char *p = "wyd is handsome";
	
	*(s+3) = 0; // 在第4个字符位置加结束标志
	puts(s); 
	puts(p);
	//*(p+3)=0; 运行报错,字符串常量不能赋值 
	p=s;   // 没有这行,下面输入字符回车会报错 
	printf("%p",p); 
	gets(p);
	puts(p);
}

 

6 指针型函数

6.1 定义

函数返回值的数据类型决定了该函数的数据类型。

数值型函数:返回值为数值类型

字符型函数:返回值为字符类型

指针型函数:返回值是地址

数据类型 *函数名(参数)

如:int *func(int x,float y)

6.2 敲代码

例 1

#include <stdio.h>
 
int *f(int *x, int *y){// x与y都是指针变量,存的地址
    if(*x < *y)
        return x;
    else
        return y;
}
 
void main ()
{
    int x=7, y=8, *p;
    p=f(&x, &y);
    printf("%p\n", p);
    printf("%d\n", *p);
}

结果:

#include <stdio.h>
 
char *f(char *b){
	b += 3;
	return b;
}
 
void main ()
{
	char a[] = "abcdefg", *p;
	p = f(a);
	printf("%s\n",p);
}

 

7 函数指针(指向函数)
7.1 定义
 C 语言中,指针变量除了保存数据的存储地址外,还可以保存函数的存储首地址。函数的存储首地址又称为函数的执行入口地址。

数据类型 (*函数指针名)();

如:int (*func)();                                                                                                                                 

7.2 码代码

#include <stdio.h>
 
int f1(int x){
	return x*x;
}
int f2(int x){
	return x*x*x;
}
// f1 和 f2 形参均为函数指针,存放函数的首地址
int f(int (*f1)(), int (*f2)(), int x){ 
	return f2(x) - f1(x);
}
void main(){
	int i;
	i = f(f1, f2, 2);
	printf("%d\n",i);
}

结果: 4  

8 结构体指针

8.1 结构体变量指针

8.1.1 定义

指向结构体变量的指针

struct 结构体类型名 *结构体指针名;
 
struct Student st,*p = &st; // p 是指向结构体变量 st 首地址的指针
  • p 不是结构体变量,不能写成 p.age,要写成(*p).age,或 p -> age
  • -> 的优先级最高,如:p->age+1,相当于(p->age)+1,即返回 p->age 的值+1

8.1.2 干活

#include<stdio.h> 
 
struct Student{
	int no;
	char name[20];
}st={101,"apple"},*p;
 
int main(){
	p = &st;
	printf("num=%d,name=%s\n",st.no,st.name);
	printf("num=%d,name=%s\n",(*p).no,(*p).name);
	printf("num=%d,name=%s\n",p->no,p->name);
}

结果:

8.2 结构体数组指针

8.2.1 定义

指向结构体数组,即将该数组的起始地址赋值给该指针变量。

struct Student  s[40], *p=s;

8.2.2 搬砖

(1)第一回合

#include<stdio.h> 
 
struct Student{
	int no;
	char name[20];
}st[2]={{101,"apple"},{102,"peach"}}, *p;
 
int main(){
	p = &st;
	printf("num=%d,name=%s\n",p->no,p->name);
	printf("num=%d,name=%s\n",(p++)->no,p->name); // 先取得p->no的值,再进行p自增1,指向下一个元素st[1] 
	printf("num=%d,name=%s\n",p->no,p->name);
}

结果:

(2)第二回合

#include<stdio.h> 
 
struct Student{
	int no;
	char name[20];
}st[2]={{101,"apple"},{102,"peach"}}, *p;
 
int main(){
	p = &st;
	printf("num=%d,name=%s\n",p->no,p->name);
	printf("num=%d,name=%s\n",(++p)->no,p->name); // 先进行p自增1,指向下一个元素st[1] ,再取成员 no 的值 
}

结果:

本章终! 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/659913.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

当 Rokid 遇上函数计算

作者&#xff1a;王彬&#xff08;阿里云解决方案架构师&#xff09;、姚兰天&#xff08;Rokid 技术专家&#xff09;、聂大鹏&#xff08;阿里云高级技术专家 &#xff09; 公司背景和业务 Rokid 创立于2014年&#xff0c;是一家专注于人机交互技术的产品平台公司。Rokid 通…

2023/6/18周报

目录 摘要 论文阅读 1、题目和现有问题 2、工作流程 3、图神经网络模块 4、注意力网络 5、实验结果和分析 深度学习 1、GNN和GRU的融合 2、相关公式推导 总结 摘要 本周在论文阅读上&#xff0c;对基于图神经网络和改进自注意网络的会话推荐的论文进行了学习&#…

DPdisPCA算法原理笔记

概要 本文简单理顺《Differentially Private Distributed Principal Component Analysis》论文中的算法原理&#xff0c;它主要提出了一种基于差分隐私的分布式PCA算法&#xff0c;研究了该算法在实验数据以及真实数据中的表现&#xff0c;在参数相同的情况下本算法取得了和没…

OpenGL之深度测试

文章目录 深度测试深度测试函数源代码 深度测试 深度缓冲就像颜色缓冲(Color Buffer)&#xff08;储存所有的片段颜色&#xff1a;视觉输出&#xff09;一样&#xff0c;在每个片段中储存了信息&#xff0c;并且&#xff08;通常&#xff09;和颜色缓冲有着一样的宽度和高度。深…

Python3 条件控制与循环语句 | 菜鸟教程(八)

目录 一、Python3 条件控制 &#xff08;一&#xff09;Python 条件语句是通过一条或多条语句的执行结果&#xff08;True 或者 False&#xff09;来决定执行的代码块。 &#xff08;二&#xff09;if 语句 1、Python中if语句的一般形式如下所示&#xff1a; 2、注意&#…

<Linux开发>驱动开发 -之-platform 驱动

&#xff1c;Linux开发&#xff1e;驱动开发 -之-platform 驱动 交叉编译环境搭建&#xff1a; &#xff1c;Linux开发&#xff1e; linux开发工具-之-交叉编译环境搭建 uboot移植可参考以下&#xff1a; &#xff1c;Linux开发&#xff1e; -之-系统移植 uboot移植过程详细记…

基于SpringBoot+Vue的民宿管理平台系统设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架下…

某音短视频评论采集翻页(移动安全篇)

本章测试一个作品的评论及翻页&#xff1a; 以及前面的抓x包方式&#xff0c;在专栏里也有很多&#xff0c;xposed抓包过sslping&#xff0c;通用版本等&#xff1b; https://codeooo.blog.csdn.net/category_11500477.html 翻页通过页码来控制&#xff1a; # -*- coding:…

Docker部署(1)——将jar包打成docker镜像并启动容器

在代码编写完成即将部署的时候&#xff0c;如果采用docker容器的方法&#xff0c;需要将jar包打成docker镜像并通过镜像将容器启动起来。具体的步骤如下。 一、首先下载java镜像 先使用docker search java命令进行搜索。 然而在拉取镜像的时候要注意不能直接去选择pull java ,…

kubernetes(k8s)理论篇

注意&#xff1a;kubeadm与docker是有版本要求的。 如果版本不兼容&#xff0c;初始化 kubeadm是会出现以下问题。 学习k8s掌握知识 基础概念 什么是 Pod 控制器类型 K8S 网络通讯模式 Kubernetes 构建 K8S 集群 资源清单 资源 掌握资源清单的语法 编写 Pod 掌握 Pod 的…

C国演义 [第四章]

第四章 全排列题目理解步骤树形图递归函数递归结束条件单层逻辑 代码 全排列II题目理解步骤递归函数递归结束条件单层逻辑 代码 全排列 力扣链接 给定一个不含重复数字的数组 nums &#xff0c;返回其 所有可能的全排列 。你可以 按任意顺序 返回答案。 示例 1&#xff1a; 输…

简要介绍 | 深度学习中的自注意力机制:原理与挑战

注1&#xff1a;本文系“简要介绍”系列之一&#xff0c;仅从概念上对深度学习中的自注意力机制进行非常简要的介绍&#xff0c;不适合用于深入和详细的了解。 注2&#xff1a;"简要介绍"系列的所有创作均使用了AIGC工具辅助 深度学习中的自注意力机制&#xff1a;原…

android jetpack databinding的基本使用(java)

目录 databing的基本使用二级页面的绑定自定义BindingAdapter自定义BinddingAdapter的可选旧值双向绑定使用ObservableField来进行双向绑定 在recycleview中使用databinding databing的基本使用 开启databing android {........dataBinding{enable true} }修改布局文件 为布…

Unity UGUI6——UGUI进阶

一、UI 事件监听接口 ​ 目前所有的控件都只提供了常用的事件监听列表 ​ 如果想做一些类似长按&#xff0c;双击&#xff0c;拖拽等功能是无法制作的&#xff0c;或者想让 Image 和 Text&#xff0c;RawImage 三大基础控件能够响应玩家输入也是无法制作的 ​ 而事件接口就是…

Elasticsearch 基本使用(三)条件查询

条件查询 单条件查询matchdebug 查看分词结果match_phrase 多条件查询bool 子元素区别 单条件查询 match match 匹配字段&#xff0c;会对条件分词&#xff0c;然后每个词以or的关系在文档倒排索引内进行查询 GET bank/_search {"query": {"match": {&q…

一起学SF框架系列6.1-模块core-Resource

Java虽然提供了java.net.URL类和各种URL前缀处理程序来负责处理对各种资源的访问&#xff0c;但对于低级别资源的访问来说还是不够充分。例如&#xff0c;没有标准化的实现可用于访问需要从类路径中获取或者相对于一个ServletContext的资源&#xff1b;也没有检查所指向的资源是…

火影手游 问答题小抄

文章目录 Part.I IntroductionPart.II 一些常识Chap.I 基础常识Chap.II 人物相关Chap.III 原作相关Chap.III 游戏相关 Part.III 奥义 & 技能Chap.I S 忍Chap.II A 忍Chap.III B 忍Chap.IV C 忍 Part.IV 针对活动Chap.I 组织樱花祭Chap.II 樱花问答 Pary.V 名言Reference Pa…

群辉DSM7.2安装svn服务

Part1前言 今天研究了一晚上使用群辉安装svn&#xff0c;确实挺多坑的&#xff0c;总结记录一下。我的型号是DS220&#xff0c;版本为DSM 7.2 Part2安装docker 首先打开套件中心&#xff0c;如下图&#xff0c;检索docker 然后点击安装&#xff0c;安装之后打开如下&#xff1a…

Clickhouse之物化视图分享

前言 ClickHouse广泛用于用户和系统日志查询场景中&#xff0c;主要针对于OLAP场景&#xff0c;为业务方提供稳定高效的查询服务。在业务场景下&#xff0c;数据以不同的格式、途径写入到clickhouse。用传统JOIN方式查询海量数据&#xff0c;通常有如下痛点: 每个查询的代码冗…

【TOP生物信息】基于Scanpy的单细胞数据质控、聚类、标注

扫码关注下方公粽号&#xff0c;回复推文合集&#xff0c;获取400页单细胞学习资源&#xff01; 「写在前面」 Python作为一种高级编程语言&#xff0c;被广泛用于单细胞数据分析&#xff0c;有着以下的优势&#xff1a; 「大量的生物信息学库&#xff1a;」 Python拥有大量的…