C语言之指针进阶篇(1)

目录​​​​​​​

引言

字符指针

指针数组

数组指针

数组指针的定义

&数组名vs数组名

数组指针的使用 

一维数组使用 

二维数组使用

一维数组传参

二维数组传参

总结 

数组参数

一维数组传参

二维数组传参

指针参数

一级指针传参

二级指针传参


引言

今天学习指针的进阶版,那还是回顾一下我们前面学习过的指针知识。

  • 内存单元是有编号,编号 == 地址 == 指针
  • 指针变量就是个变量,用来存放地址,地址唯一标识一块内存空间。
  • 指针(地址/指针)的大小是固定的4/8个字节(32位平台/64位平台)。
  • 指针变量的大小是4/8个字节。
  • 指针有类型,指针的类型决定了指针的+-整数的步长,指针解引用操作的时候的权限。
  • 指针运算。

字符指针

%c   打印字符
%s   打印字符串

在指针初阶中我们学习过指针的类型,我们知道有一种指针类型为字符指针char*

#include<stdio.h>
int main()
{
	char c = 'x';
	char ch = "abcdef";
	char* pc = &ch;//存放字符串全部的地址
	char* p = &c;//存放字符的地址
	printf("%c", *p);
	return 0;
}

 除了上面的传统使用方法,我们还有另外一种使用方法:

#include<stdio.h>
int main()
{
	char* pc = "abcdef";
	printf("%c\n", "abcdef"[0]);
	printf("%s\n", pc);
	printf("%c", *pc);
	return 0;
}

  •  char * pc = "abcdef"本质是把字符串"abcdef"首地址放到了字符指针变量pc中。
  • "abcdef"是表达式,值就是a的地址。
  • 一个常量字符串的首地址+%s打印=字符串所有内容(根据首地址遍历字符串所有内容)
  • "abcdef"也可以理解为数组名 == 相当于 数组的首地址被存放。
  • *pc是只能访问一个字符。

但是上面的使用存在一定的风险,什么风险呢?

"abcdef"是常量字符串表达式,"常量"是不能改变的。

 常量字符串是不能被修改的。

那怎么解决呢?

 加上const即可。

#include<stdio.h>
int main()
{
	const char* pc = "abcdef";//不会被修改了
	printf("%s\n", pc);
	*pc = "g";
	return 0;
}

练习——剑指offer 

#include <stdio.h>
int main()
{
	char str1[] = "tangsiqi";
	char str2[] = "tangsiqi";
	char* str3 = "tangsiqi";
	char* str4 = "tangsiqi";
	if (str1 == str2)
		printf("str1 and str2 are same\n");
	else
		printf("str1 and str2 are not same\n");

	if (str3 == str4)
		printf("str3 and str4 are same\n");
	else
		printf("str3 and str4 are not same\n");

	if (&str3 == &str4)
		printf("str3 and str4 are same\n");
	else
		printf("str3 and str4 are not same\n");

	return 0;
}

  • str1和str2是在内存中开辟了不同的空间去存放字符串。
  • str3和str4是内存中不同的空间指向了同一个字符串的首地址。 

指针数组

 在前面我们学习过指针数组以及它的使用(模拟二维数组的作用)。

指针数组是指针还是数组呢?

是数组啦。是存放指针的数组。

字符数组——存放字符的数组

整型数组——存放整型的数组

指针数组——存放指针(地址)的数组,存放在数组中的元素都是指针类型的。

//例如:char * arr[5];   int * ch[6];

//arr[5]表明是数组

模拟二维数组

//使用指针数组,模拟一个二维数组
#include<stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
     //指针数组
	int* arr[] = { arr1,arr2,arr3 };
	             //0   1     2
	int i = 0;
	int j = 0;
	for (i = 0; i < 3; i++)//找到首元素
	{
		for (j = 0; j < 5; j++)//遍历每一个数组的元素
		{
			printf("%d ", arr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

除了上面整型指针数组,还有字符指针数组。 

#include<stdio.h>
int main()
{
	char* arr[5] = { "tangsiqi","xueguodi","haha","hehe","xiaoxiao" };
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%s\n", arr[i]);
	}
	return 0;
}

 

int * arr1[10];//整形指针数组
char * arr2[4];//一级字符指针的数组
char * *arr3[5];//二级字符指针的数组

数组指针

数组指针是指针?还是数组?

指针。

指针数组——是数组,是存放指针的数组。

数组指针——是指针,指向数组的指针。

字符指针——指向字符的指针。

整型指针——指向整型的指针。

浮点型的指针——指向浮点型的指针。

int a=10;
int * pa=&a;

char b='x';
char * pb=&b;

 那么数组指针怎么写呢?

数组指针的定义

定义:数组指针是指针,指向数组的指针。 //type_t (* p) [const_n];

int arr[10];
int *p1[10];
int (*p2)[10];

 上面那个代码是指针数组,那个是数组指针?p1,p2分别代表什么?

int arr[10];
int *p1[10];//指针数组
int (*p2)[10];//数组指针
//解释:
//p先和*结合,说明p是一个指针变量,然后指着指向的是一个大小为10个整型的数组。
//所以p是一个指针,指向一个数组,叫数组指针。
//这里要注意:[]的优先级要高于*号的,所以必须加上()来保证p先和*结合。

&数组名vs数组名

//数组名 vs &数组名
// &数组名是整个数组的地址
//数组名是数组首元素的地址
//除了两个例外:
//1.sizeof(数组名),这里的数组名表示整个数组,sizeof(数组名)计算的是整个数组的大小,单位是字节
//2.&数组名,这里的数组名表示整个数组名,取出的是数组的地址。
#include<stdio.h>
int main()
{
	int arr[10];
	printf("%d\n", arr);//int*
	printf("%d\n", arr + 1);
	printf("%d\n", &arr);//这个是什么类型?
	printf("%d\n", &arr + 1);
    //指针类型决定了指针+1,到底+几个字节
	return 0;
}

根据上面代码运行结果我们发现,虽然&arr和arr值是一样的,但是意义应该是不一样的。

实际上:&arr表示的是数组的地址;arr表示的是数组首元素的地址。

数组的地址+1,跳过的整个数组的大小,所以&arr+1相对于&arr的差值是40

int*p[10]=arr    +1跳过4个字节

int(*p)[10]=&arr     +1跳过10个元素每个元素为int的数组也就是 40个字节

那应该怎样存放呢?

#include<stdio.h>
int main()
{
	int arr[10];
	int(*p)[10] = &arr;
	//p是用来存放整个数组的地址,p就是数组指针
	char* arr2[5];
	char* (*pc)[5] = &arr2;
	return 0;
}

 int (*p) [10];

//*p表明是指针

// 数组指针指向数组10个元素,每个元素的类型是int 

特别提醒: 以下写法均是错误的❌

int arr[10];
❌int (*p)[]=&arr;//不能省略
❌int [10]*p=&arr;

数组指针的使用 

一维数组使用 

一维数组的arr数组名就是首元素地址_用指针接收
int *p =arr;
一维数组的&arr取地址数组名是整个数组的地址_用数组指针接收
int (*p)[10]=&arr;
//一维数组使用
#include<stdio.h>
int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int(*p)[5] = &arr;
//值是放到p里面,不是*p,值是&arr
//&arr是整个数组的地址
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d ", (*p)[i]);//p[i]❌
                    //(*&arr) ==arr——*和&可以抵消
	}
}
//一维数组传参
#include<stdio.h>
void print(int(*p)[5])
{
	int i = 0;
	for (i = 0; i < 5; i++)
	{
		printf("%d", (*p)[i]);
	}
}
int main()
{
	int arr[5] = { 1,2,3,4,5 };
	print(&arr);
	return 0;
}

 把数组arr的地址赋值给数组指针变量p,但是我们一般很少这样写代码。 

二维数组使用

二维数组的arr是首元素地址,就是第一行数组(一维数组)整个的地址
用数组指针来接受
int(*p)[][5]=arr;
//此时不需要取地址
//二维数组
#include<stdio.h>
int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	int(*p)[5] = arr;//二维数组数组名 ==首元素 == 二维数组的第一行
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d", p[i][j]);
		}
		printf("\n");
	}
	return 0;
}
//二维数组
#include<stdio.h>
void print(int(*p)[5], int row, int col)//本质就是二维数组的首元素地址==第一行地址
//void print(int arr[3][5],int row,int col)
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d", arr[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	print(arr, 3, 5);
	return 0;
}

 上面我们指针数组涉及到一维数组传参和二维数组传参的点我们来学习下。(传arr首元素地址)

一维数组传参

//数组形式
#include<stdio.h>
void print(int arr[])
//void peintf(int arr[10])也可
{
	int i = 0;
	for (i = 0; i < 10;i++)
	{
		printf("%d ", arr[i]);//*(arr+i)
	}
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	print(arr);
	return 0;
}
//指针形式
#include<stdio.h>
void print(int* arr)
{
	int i = 0;
	for (i = 0; i < 10; i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	print(arr);
	return 0;
}

二维数组传参

//二维数组传参
//数组形式
#include<stdio.h>
void print(int arr[3][5],int row,int col)
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d", arr[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	print(arr, 3, 5);
	return 0;
}

//指针形式
#include<stdio.h>
void print(int (*arr)[3][5], int row, int col)//✔
//void print(int (*arr)[5], int row, int col)✔
//void print(int (*arr)[][5], int row, int col)❌——四不像
//void print(int arr[][5], int row, int col)✔
{
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for (j = 0; j < 5; j++)
		{
			printf("%d ", (*arr)[i][j]);
		}
		printf("\n");
	}
}
int main()
{
	int arr[3][5] = { {1,2,3,4,5},{2,3,4,5,6},{3,4,5,6,7} };
	print(arr, 3, 5);
//&arr——首元素地址的地址
	return 0;
}

总结 

 学了指针数组和数组指针我们来看下下面代码的意思:

  •  arr是一个能够存放5个整型数据的数组
  • parr1是一个指针数组,数组10个元素,每个元素的类型是int*
  • parr2是一个数组指针,指向数组,指向的数组有10个元素,每个元素的类型是int
  • parr3是一个数组,数组有10个元素,存放10个元素都是数组指针,数组指针是指针,    指向的数组有5个元素,每个元素是int类型。 

首先确定是指针还是数组——看符号的优先级,变量先与那个符号结合🆗🆗

把这部分除去,看剩下的就是变量的类型了。🆗🆗

数组参数

在写代码的时候难免要把【数组】或者【指针】传给函数,那函数的参数应该如何设计?

一维数组传参

#include <stdio.h>
void test(int arr[])//ok?✔
//不会创建一个新的数组,本质是指针所有[]里有无都可
{}
void test(int arr[10])//ok?✔
{}
void test(int *arr)//ok?✔
//本质是指针——首元素地址
{}
void test2(int *arr[20])//ok?✔
{}
void test2(int **arr)//ok?✔
//接收的是首元素地址的地址
{}
int main()
{
int arr[10] = {0};//数组
int *arr2[20] = {0};//指针数组
test(arr);//首元素地址
test2(arr2);//首元素_是指针的地址 
}

数组传参,参数是可以写成数组形式的,可以写成指针形式。

数组传参本质是:传递了数组首元素的地址。 

二维数组传参

void test(int arr[3][5])//ok?✔
{}
void test(int arr[][])//ok?❌
{}
void test(int arr[][5])//ok?✔
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int *arr)//ok?❌
{}
void test(int* arr[5])//ok?❌
{}
void test(int (*arr)[5])//ok?✔
{}
void test(int **arr)//ok?❌
{}

int main()
{
int arr[3][5] = {0};
test(arr);//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]);
//一级指针p,传给函数
print(p, sz);
return 0;
}

 思考:当一个函数的参数部分为一级指针的时候,函数能接受什么参数?

#include<stdio.h>
void test(int *p)
{
//
}

int main()
{
  int a=10;
  int * pa=&a;
  int arr[5];
//传参
  test(&a);//传整型变量的地址
  test(pa);//传整型的指针
  test(arr);//传整型一维数组的数组名
  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(int **p)
{
//
}
int main()
{
   int n=10;
   int*p=&n;
   int **pp=&p;
   int* arr[5];//指针数组
//传参
   test(&p);
   test(pp);
   test(arr);
   return 0;
}

关于数组和指针以及它们传参相信大家有了深刻的理解,函数指针我们会在下篇博文讲到。🙂🙂

✔✔✔✔✔最后,感谢大家的阅读,若有错误和不足,欢迎大家指正!

代码-------→【gitee:https://gitee.com/TSQXG】

联系-------→【邮箱:2784139418@qq.com】

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【爬虫练习之glidedsky】爬虫-基础1

题目 链接 爬虫的目标很简单&#xff0c;就是拿到想要的数据。 这里有一个网站&#xff0c;里面有一些数字。把这些数字的总和&#xff0c;输入到答案框里面&#xff0c;即可通过本关。 思路 找到调用接口 分析response 代码实现 import re import requestsurl http://www.…

设计模式——桥接模式

引用 桥我们大家都熟悉&#xff0c;顾名思义就是用来将河的两岸联系起来的。而此处的桥是用来将两个独立的结构联系起来&#xff0c;而这两个被联系起来的结构可以独立的变化&#xff0c;所有其他的理解只要建立在这个层面上就会比较容易。 基本介绍 桥接模式&#xff08;Br…

【数据结构与算法】普里姆算法

普里姆算法 最小生成树 最小生成树&#xff0c;简称MST。 给定一个带权的无向连通图&#xff0c;如何选取一棵生成树&#xff0c;使树上所有边上权的总和为最小&#xff0c;这就叫最小生成树。N 个顶点&#xff0c;一定有 N - 1 条边半酣全部顶点N - 1 条边都在图中举例说明…

CAS 一些隐藏的知识,您了解吗

目录 ConcurrentHashMap 一定是线程安全的吗 CAS 机制的注意事项 使用java 并行流 &#xff0c;您要留意了 ConcurrentHashMap 在JDK1.8中ConcurrentHashMap 内部使用的是数组加链表加红黑树的结构&#xff0c;通过CASvolatile或synchronized的方式来保证线程安全的,这些原理…

Ubuntu 20.04(服务器版)安装 Anaconda

0、Anaconda介绍 Anaconda是一个开源的Python发行版本&#xff0c;包含了包括Python、Conda、科学计算库等180多个科学包及其依赖项。因此&#xff0c;安装了Anaconda就不用再单独安装CUDA、Python等。 CUDA&#xff0c;在进行深度学习的时候&#xff0c;需要用到GPU&#xf…

Lnton羚通算法算力云平台在环境配置时 OpenCV 无法显示图像是什么原因?

问题&#xff1a; cv2.imshow 显示图像时报错&#xff0c;无法显示图像 0%| | 0/1 [00:00<…

【java】为什么文件上传要转成Base64?

文章目录 1 前言2 multipart/form-data上传3 Base64上传3.1 Base64编码原理3.2 Base64编码的作用 4 总结 1 前言 最近在开发中遇到文件上传采用Base64的方式上传&#xff0c;记得以前刚开始学http上传文件的时候&#xff0c;都是通过content-type为multipart/form-data方式直接…

易服客工作室:Houzez主题 - 超级房地产WordPress主题/网站

Houzez主题是全球流行的房地产经纪人和公司的WordPress主题。 Houzez Theme是专业设计师创造一流设计的超级灵活起点。它具有您的客户&#xff08;房地产经纪人或公司&#xff09;甚至可能做梦也想不到的功能。 网址&#xff1a;Houzez主题 - 超级房地产WordPress主题/网站 - …

Java请求Http接口-hutool的HttpUtil(超详细-附带工具类)

概述 HttpUtil是应对简单场景下Http请求的工具类封装&#xff0c;此工具封装了HttpRequest对象常用操作&#xff0c;可以保证在一个方法之内完成Http请求。 此模块基于JDK的HttpUrlConnection封装完成&#xff0c;完整支持https、代理和文件上传。 导包 <dependency>&…

[保研/考研机试] KY96 Fibonacci 上海交通大学复试上机题 C++实现

题目链接&#xff1a; KY96 Fibonacci https://www.nowcoder.com/share/jump/437195121692000803047 描述 The Fibonacci Numbers{0,1,1,2,3,5,8,13,21,34,55...} are defined by the recurrence: F00 F11 FnFn-1Fn-2,n>2 Write a program to calculate the Fibon…

基于chatgpt动手实现一个ai_translator

动手实现一个ai翻译 前言 最近在极客时间学习《AI 大模型应用开发实战营》&#xff0c;自己一边跟着学一边开发了一个进阶版本的 OpenAI-Translator&#xff0c;在这里简单记录下开发过程和心得体会&#xff0c;供有兴趣的同学参考&#xff1b; ai翻译程序 版本迭代 在学习…

物联网设备的分类和功能阐述

物联网是将各种物理设备和传感器通过互联网进行连接和交互的网络&#xff0c;物联网的核心思想是让各种设备能够通过互联网实现智能化、自动化和远程控制。物联网设备是指连接到物联网中的各种设备&#xff0c;可以通过传感器、处理器、通信模块等技术实现与互联网的连接和数据…

vue3 简易用对话框实现点击头像放大查看

设置头像悬停手势 img:hover{cursor: pointer;}效果&#xff1a; 编写对话框 <el-dialog class"bigAvatar"style"border-radius: 4px;"v-model"deleteDialogVisible"title"查看头像"top"5px"><div><img src&…

大数据Flink学习圣经:一本书实现大数据Flink自由

学习目标&#xff1a;三栖合一架构师 本文是《大数据Flink学习圣经》 V1版本&#xff0c;是 《尼恩 大数据 面试宝典》姊妹篇。 这里特别说明一下&#xff1a;《尼恩 大数据 面试宝典》5个专题 PDF 自首次发布以来&#xff0c; 已经汇集了 好几百题&#xff0c;大量的大厂面试…

SpringBoot部署到腾讯云

SpringBoot部署到腾讯云 此处默认已经申请到腾讯云服务器&#xff0c;因为本人还没有申请域名&#xff0c;所以就直接使用的ip地址 XShell连接到腾讯云 主机中填写腾讯云的公网ip地址 公网ip地址在下图中找到 接下来填写服务器的用户名与密码 一般centOS用户名为root&#xff…

【设计模式】订单状态流传中的状态机与状态模式

文章目录 1. 前言2.状态模式2.1.订单状态流转案例2.1.1.状态枚举定义2.1.2.状态接口与实现2.1.3.状态机2.1.4.测试 2.2.退款状态的拓展2.2.1.代码拓展2.2.2.测试 2.3.小结 3.总结 1. 前言 状态模式一般是用在对象内部的状态流转场景中&#xff0c;用来实现状态机。 什么是状态…

rabbitmq的消息应答

消费者完成一个任务可能需要一段时间&#xff0c;如果其中一个消费者处理一个长的任务并仅只完成 了部分突然它挂掉了&#xff0c;会发生什么情况。RabbitMQ 一旦向消费者传递了一条消息&#xff0c;便立即将该消 息标记为删除。在这种情况下&#xff0c;突然有个消费者挂掉了…

jvm-类加载子系统

1.内存结构概述 类加载子系统负责从文件系统或网络中加载class文件&#xff0c;class文件在文件开头有特定的文件标识 ClassLoader只负责class文件的加载&#xff0c;至于它是否运行&#xff0c;则由Execution Engine决定 加载的类信息存放于一块称为方法区的内存空间&#xff…

SHELL 基础

echo 打印命令 &#xff1a; 显示字符串 [rootserver ~]# echo this is SHELL language this is SHELL language [rootserver ~]# echo this is SHELL language this is SHELL language [rootserver ~]# echo "this is SHELL language" this is SHELL language…

【java毕业设计】基于ssm+mysql+jsp的个性化影片推荐系统设计与实现(程序源码)-个性化影片推荐系统

基于ssmmysqljsp的个性化影片推荐系统设计与实现&#xff08;程序源码毕业论文&#xff09; 大家好&#xff0c;今天给大家介绍基于ssmmysqljsp的个性化影片推荐系统设计与实现&#xff0c;本论文只截取部分文章重点&#xff0c;文章末尾附有本毕业设计完整源码及论文的获取方式…
最新文章