C语言--指针进阶

news2024/12/23 13:47:59

C语言–指针进阶


文章目录

  • C语言--指针进阶
  • 一、字符指针
    • 1.1 两种使用方式
    • 1.2 经典面试题
      • 1.2.1 最终结果
      • 1.2.2 总结
  • 二、数组指针
    • 2.1 数组指针的语法
    • 2.2 &数组名与数组名的区别
      • 2.2.1 运行结果
    • 2.3 一维数组笔试题
    • 2.4 字符数组笔试题
      • 2.4.1 字符数组
      • 2.4.2 字符串数组
      • 2.4.3 字符串指针
    • 2.4 二维数组笔试题
    • 2.5 数组指针的使用
    • 2.6 总结
  • 三、指针类型的甄别
  • 四、数组参数,指针参数
    • 4.1 一维数组传参
    • 4.2 二维数组传参
    • 4.3 一级指针传参
    • 4.4 二级指针传参
  • 五、函数指针
    • 5.1 函数指针的语法
    • 5.2 经典面试题
  • 六、函数指针数组
    • 6.1 函数指针数组的语法
  • 七、指针笔试题


一、字符指针

1.1 两种使用方式

第一种:

int main()
{
	char ch = 'w';
	char* pc = &ch;
	*pc = 'w';
	return 0;
}

第二种:

int main()
{
	const char* pstr = "hello";//这里是把一个字符串放到pstr指针变量里了吗?
	//加入const的原因是因为常量字符串不能更改,防止后面有人更改常量字符串导致程序崩坏
	printf("%s\n", pstr);//不能*pstr,因为这不是解引用打印,因为pstr里面放的是字符串的首地址
	return 0;
}
//printf("%c\n", *pstr);这样打印出来的结果就是h自己,不能把整个字符串打印出来

pstr是一个指针变量,‘ hello\0 ’这个字符串是没有办法被赋值到pstr这个指针变量中,所以pstr接收到的应该是字符串首元素的地址

在这里插入图片描述

1.2 经典面试题

#include <stdio.h>
int main()
{
	char str1[] = "hello bit.";
	char str2[] = "hello bit.";
	const char* str3 = "hello bit.";
	const char* str4 = "hello bit.";
	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");

	return 0;
}

在这里插入图片描述
在这里插入图片描述

1.2.1 最终结果

在这里插入图片描述

1.2.2 总结

这里str3和str4指向的是一个同一个常量字符串。C/C++会把常量字符串存储到单独的一个内存区域,当
几个指针。指向同一个字符串的时候,他们实际会指向同一块内存。但是用相同的常量字符串去初始化
不同的数组的时候就会开辟出不同的内存块。所以str1和str2不同,str3和str4相同。

二、数组指针

字符指针—存放字符地址的指针—指向字符的指针 char*
整形指针—存放整形地址的指针—指向整形的指针 int*
浮点型指针—存放浮点型地址的指针—指向浮点型的指针 float* double*
数组指针—存放数组地址的指针—指向数组的指针

2.1 数组指针的语法

int (*p2)[10];

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

2.2 &数组名与数组名的区别

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	printf("arr = %p\n", arr);
	printf("arr+1 = %p\n", arr + 1);
	printf("&arr= %p\n", &arr);
	printf("&arr+1= %p\n", &arr + 1);
	return 0;
}

2.2.1 运行结果

在这里插入图片描述
运行结果的意义:

  1. 数组名–数组首元素的地址
  2. &数组名-数组的地址
  3. 数组首元素的地址和数组的地址从值的角度来看是一样的,但是意义不一样
    在这里插入图片描述
    造成移位不同的原因
  4. 指针的类型不同:arr的类型是int* &arr的类型是int( * )[10]

2.3 一维数组笔试题

//一维数组
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a+0));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(a[1]));
printf("%d\n",sizeof(&a));
printf("%d\n",sizeof(*&a));
printf("%d\n",sizeof(&a+1));
printf("%d\n",sizeof(&a[0]));
printf("%d\n",sizeof(&a[0]+1));
  1. sizeof(数组名),此时数组名表示整个数组,计算的是整个数组的大小,单位是字节
  2. &数组名,数组名表示整个数组,取出的是整个数组的地址
  3. 除此之外,所有的数组名都是数组首元素的地址

在这里插入图片描述

2.4 字符数组笔试题

2.4.1 字符数组

#include<stdio.h>

int main()
{
	char arr[] = { 'a','b','c','d','e','f' };
	printf("%d\n", sizeof(arr));
	printf("%d\n", sizeof(arr + 0));
	printf("%d\n", sizeof(*arr));
	printf("%d\n", sizeof(arr[1]));
	printf("%d\n", sizeof(&arr));
	printf("%d\n", sizeof(&arr + 1));
	printf("%d\n", sizeof(&arr[0] + 1));
	return 0;
}

在这里插入图片描述
使用strlen函数求字符串长度的时候,不同类型的值应该是什么呢?
strlen函数的结束标志是 " \ 0" ,没有出现就会一直计算,直到出现结束标志

printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));

在这里插入图片描述

2.4.2 字符串数组

  1. sizeof是操作符,只关注占用内存空间的大小,单位是字节,不关心内存中存放的是什么
  2. strlen是库函数,是求字符串长度的,统计的是 ‘\0’ 之前 出现的字符个数,一定要找到\0 才算结束,所以可能存在越界访问的情况
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));

在这里插入图片描述

	char arr[] = "abcdef";
	printf("%d\n", strlen(arr));
	printf("%d\n", strlen(arr + 0));
	//printf("%d\n", strlen(*arr));
	//printf("%d\n", strlen(arr[1]));
	printf("%d\n", strlen(&arr));
	printf("%d\n", strlen(&arr + 1));
	printf("%d\n", strlen(&arr[0] + 1));
	return 0;

在这里插入图片描述

2.4.3 字符串指针

sizeof与strlen计算指针

	char* p = "abcdef";
	printf("%d\n", sizeof(p));
	printf("%d\n", sizeof(p + 1));
	printf("%d\n", sizeof(*p));
	printf("%d\n", sizeof(p[0]));
	printf("%d\n", sizeof(&p));
	printf("%d\n", sizeof(&p + 1));
	printf("%d\n", sizeof(&p[0] + 1));

在这里插入图片描述

printf("%d\n", strlen(p));
printf("%d\n", strlen(p+1));
printf("%d\n", strlen(*p));
printf("%d\n", strlen(p[0]));
printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p+1));
printf("%d\n", strlen(&p[0]+1));

在这里插入图片描述

2.4 二维数组笔试题

二维数组在内存中的存储方式
在这里插入图片描述

	//二维数组
	int a[3][4] = { 0 };
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(a[0][0]));
	printf("%d\n", sizeof(a[0]));
	printf("%d\n", sizeof(a[0] + 1));
	printf("%d\n", sizeof(*(a[0] + 1)));
	printf("%d\n", sizeof(a + 1));
	printf("%d\n", sizeof(*(a + 1)));
	printf("%d\n", sizeof(&a[0] + 1));
	printf("%d\n", sizeof(*(&a[0] + 1)));
	printf("%d\n", sizeof(*a));
	printf("%d\n", sizeof(a[3]));

在这里插入图片描述
关于sizeof是否参与计算的问题

#include<stdio.h>

int main()
{
	//二维数组
	short s = 3;
	int a = 10;
	printf("%d\n", sizeof(s = a + 2));
	printf("%d\n", s);
}

在这里插入图片描述

2.5 数组指针的使用

数组指针对于一维数组来说不是很友好,主要还是在二维数组中使用

#include <stdio.h>
void print_arr1(int arr[2][5], int row, int col)
{
	int i = 0;
	for (i = 0; i < row; i++)
	{
		for (int 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++)
		{
			for (int j = 0; j < col; j++)
			{
				printf("%d ", (*(arr+i))[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, 2, 5);
	//数组名arr,表示首元素的地址
	//但是二维数组的首元素是二维数组的第一行
	//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
	//可以数组指针来接收
	print_arr2(arr, 2, 5);
	return 0;
}

在这里插入图片描述

2.6 总结

数组名的意义:一维二维一样

  1. sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
  2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址。
  3. 除此之外所有的数组名都表示首元素的地址。
  4. 二维数组的首元素的地址是第一行的地址

三、指针类型的甄别

要考虑优先级再判断指针的类型

int arr[5]//整形数组,数组是5个元素
int *parr1[10]//指针数组,数组10个元素,每个元素是int* 类型

这里就涉及到优先级的问题,[ ]优先级是高于 *,所以parr1是先与[ ]结合形成数组,然后这个数组的类型是int * ,代表这个数组里面包含的元素是指针类型

int (*parr2)[10]//数组指针

*parr2代表其为一个指针,该指针指向一个数组,数组是10个元素,每个元素是int类型的

int*parr3[10][5]

parr3是数组,数组有10个元素,数组的每个元素的类型是:int(*)[5]的数组指针类型在这里插入图片描述

四、数组参数,指针参数

4.1 一维数组传参

#include <stdio.h>
void test(int arr[])//数组传参,数组接收,接收时数组可以不指定大小
{}
void test(int arr[10])//数组传参,数组接收,接收时指定大小
{}
void test(int* arr)//数组传参,指针接收,数组为int类型,指针为int* 类型
{}
void test2(int* arr[20])//指针数组传参,指针数组接收,指定大小或者不指定大小都可以
{}
void test2(int** arr)//指针数组传参,指针接收,数组类型是int* 那么对应接收的指针类型就是int**
{}
int main()
{
	int arr[10] = { 0 };
	int* arr2[20] = { 0 };
	test(arr);
	test2(arr2);
}

4.2 二维数组传参

二维数组传参,要不就是二维数组接收,要不然就使用数组指针接收

void test(int arr[3][5])
{}
void test(int arr[][])//错误
{}
void test(int arr[][5])//
{}
//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。
//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。
//这样才方便运算。
void test(int* arr)//数组名表示首元素的地址,二维数组的首元素的地址是第一行数组的地址,而不是首元素的地址,不能用一级指针表示
{}
void test(int* arr[5])//指针数组  arr[5]表示一维数组,数组里面存放的还是int* 类型的指针,所以错误
{}
void test(int(*arr)[5])//数组指针,并且每个数组是5个元素,类型是int
{}
void test(int** arr)//二级指针是用来接收一级指针的地址,类型无法匹配,所以错误
{}
int main()
{
	int arr[3][5] = { 0 };
	test(arr);

4.3 一级指针传参

#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;
}

一级指针传参就用一级指针接收

当函数的参数部分为一级指针的时候,函数能接收什么参数

void test(int* p)
{

}

int a = 10;
int* p = &a;
test(&a);//整形变量地址可以使用一级指针接收
test(p);//直接传一级指针

int arr[10];
test(arr)//一维数组名是首元素的地址,数组是int类型,所以指针就是int*

4.4 二级指针传参

#include <stdio.h>
void test(int** ptr)
{
	printf("num = %d\n", **ptr);
}
int main()
{
	int n = 10;
	int* p = &n;
	int** pp = &p;
	int* arr[10];
	test(pp);//二级指针传参
	test(&p);//一级指针的地址传参
	test(arr);//指针数组的数组名
	return 0;
}

五、函数指针

5.1 函数指针的语法

int Add(int x, int y)
{
	return x + y;
}
int main()
{
	int(*pf)(int, int) = &Add;//函数指针语法
	int(*pf)(int, int) = Add;//函数指针语法
    //pf是一个存放函数地址的指针变量--函数指针
//******************************************
//函数指针的调用
    int ret = (*pf)(2,3);
    int ret = pf(2,3);//不写*也可以的
}

&函数名和函数名都是函数的地址

5.2 经典面试题

//代码1
int main()
{
	(* ( void (*)() ) 0  )  ();
	return ;
}

void ()() 这是函数指针类型
( void (
)())0 就是要对0进行强制类型转换成函数指针类型,就是0被当成一个函数的地址
(* ( void (*)() ) 0 ) 对地址进行解引用操作,无参数传参,后面只写()即可
总结下来就是对0地址处的函数进行调用

//代码2
void (*signal(int, void(*)(int)))(int);

该代码是一次函数的声明,声明的函数名字叫signal ,signal函数的参数有2个,第一个是int类型,第二个是函数指针类型,该函数指针 void(*)(int) 指向的那个函数的参数是int,返回类型是void

六、函数指针数组

数组里面的元素是函数指针的数组就是函数指针数组

6.1 函数指针数组的语法

int (*pfa[5])(int,int)
//pfa[5]代表pfa是一个数组
//int (*)(int,int)代表数组元素的类型 

七、指针笔试题

int main()
{
    int a[5] = { 1, 2, 3, 4, 5 };
    int* ptr = (int*)(&a + 1);
    printf("%d,%d", *(a + 1), *(ptr - 1));
    return 0;
}

在这里插入图片描述

struct Test
{
	int Num;
	char* pcName;
	short sDate;
	char cha[2];
	short sBa[4];
}*p;
//假设p 的值为0x100000。 如下表表达式的值分别为多少?
//已知,结构体Test类型的变量大小是20个字节
int main()
{
	printf("%p\n", p + 0x1);
	printf("%p\n", (unsigned long)p + 0x1);
	printf("%p\n", (unsigned int*)p + 0x1);
	return 0;
}

在这里插入图片描述

int main()
{
    int a[4] = { 1, 2, 3, 4 };
    int *ptr1 = (int *)(&a + 1);
    int *ptr2 = (int *)((int)a + 1);
    printf( "%x,%x", ptr1[-1], *ptr2);
    return 0;
}

在这里插入图片描述

#include <stdio.h>
int main()
{
    int a[3][2] = { (0, 1), (2, 3), (4, 5) };
    int *p;
    p = a[0];
    printf( "%d", p[0]);
 return 0;
}

在这里插入图片描述

#include <stdio.h>
int main()
{
	char* a[] = { "work","at","alibaba" };
	char** pa = a;
	pa++;
	printf("%s\n", *pa);
	return 0;
}

在这里插入图片描述

#include <stdio.h>
int main()
{
 char *a[] = {"work","at","alibaba"};
 char**pa = a;
 pa++;
 printf("%s\n", *pa);
 return 0;
}

在这里插入图片描述

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

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

相关文章

Postgresql数据类型-数组类型

PostgreSQL支持一维数组和多维数组&#xff0c;常用的数组类型为数字类型数组和字符型数组&#xff0c;也支持枚举类型、复合类型数组。 数组类型定义 先来看看数组类型的定义&#xff0c;创建表时在字段数据类型后面加方括号“[]”即可定义数组数据类型&#xff0c;如下所示…

集成Line、Facebook、Twitter、Google、微信、QQ、微博、支付宝的三方登录sdk

下载地址&#xff1a; https://githubfast.com/anerg2046/sns_auth 安装方式建议使用composer进行安装 如果linux执行composer不方便的话&#xff0c;可以在本地新建个文件夹&#xff0c;然后执行上面的composer命令&#xff0c;把代码sdk和composer文件一起上传到项目适当位…

【Unity插件】2D模拟绳子的插件——Rope 2D Editor

文章目录 前言资源unity商店地址&#xff1a;我这里有一个比较老旧的版本&#xff1a; 使用创建绳子场景使用时效果 参考完结 前言 最近发现一个很有意思的插件Rope 2D Editor&#xff0c;这是一个简单而强大的 2d 绳索编辑器。这是我为我的游戏&#xff08;Dabdob&#xff09…

郑州大学2020级信息安全专业——保研小结

最终上岸 夏令营&#xff1a; 夏令营开始的时间一般比较早&#xff0c;在期末考试之前就已经开始了&#xff0c;需要提前联系导师&#xff0c;有的学校是弱com&#xff0c;导师愿意要你入营的概率和优营的概率就会比较大&#xff0c;因此要提前联系导师&#xff0c;复习好项目…

打开word文档报错,提示HRESULT 0x80004005 位置: 部分: /word/comments.xml,行: 0,列: 0

某用户遇到这样一个奇怪的问题&#xff0c;就是回复完word的批注后&#xff0c;保存文档再打开就会报错&#xff0c;提示很抱歉&#xff0c;无法打开XXX&#xff0c;因为内容有问题。&#xff0c;详细信息提示HRESULT 0x80004005 位置: 部分: /word/comments.xml,行: 0,列: 0 c…

docker简易入门(极简,纯干货)

简介 Docker是一种容器化平台&#xff0c;它可以用来轻松地创建、部署和运行应用程序和服务。Docker使用容器技术来管理应用程序的运行环境&#xff0c;它将应用程序和服务打包到一个易于移植的容器中&#xff0c;然后在任何地方运行这个容器&#xff0c;无需担心不同环境之间…

动作捕捉系统通过VRPN与ROS系统通信

NOKOV度量动作捕捉系统支持通过VRPN与机器人操作系统ROS通信&#xff0c;进行动作捕捉数据的传输。 一、加载数据 打开形影动捕软件&#xff0c;加载一段后处理数据。 这里选择一段小车飞机的同步数据。在这段数据里面&#xff0c;场景下包含两个刚体&#xff0c;分别是小车和…

基于注解的声明式事务

1.什么是事务 数据库事务(transaction)是访问并可能操作各种数据项的一个数据库操作序列&#xff0c;这些操作要么全部执行要么全部不执行&#xff0c;是一个不可分割的工作单位。事务由事务开始与事务结束之间执行的全部数据库操作组成。 2.事务的特性 A&#xff1a;原子性(A…

互联网Java工程师面试题·微服务篇·第一弹

目录 ​编辑 1、您对微服务有何了解&#xff1f; 2、微服务架构有哪些优势&#xff1f; 3、微服务有哪些特点&#xff1f; 4、设计微服务的最佳实践是什么&#xff1f; 5、微服务架构如何运作&#xff1f; 6、微服务架构的优缺点是什么&#xff1f; 7、单片&#xff0c…

【MySQL】事务(上)

文章目录 事务概念什么是事务&#xff1f;为什么要有事务&#xff1f;事务的版本支持事务的提交方式事务常见操作方式基本操作 事务概念 mysql 本身内部采用 多线程的方式&#xff0c;来实现数据存储 相关的工作 就注定对数据 有并发访问的场景 为了解决这类问题&#xff0c;就…

RestCloud AppLink已支持的数据源有哪些?

RestCloud AppLink是什么&#xff1f; 首先&#xff0c;我们需要了解RestCloud AppLink是什么&#xff0c;AppLink是一款由RestCloud公司推出的超级应用连接器。不需要开发&#xff0c;零代码&#xff0c;低成本即可快速打通数百款应用之间的数据。通过流程搭建&#xff0c;可…

【ATTCK】MITRE Caldera 简介

一、什么是Caldera caldera是一个基于MITRE ATT&CK™构建的网络安全框架。其目标是创建一种工具&#xff0c;通过提供自动化安全评估来增强网络从业者的能力&#xff0c;从而节省用户的时间、金钱和精力。为此&#xff0c;需要几个关键组件才能使 Caldera 成为行业领先的平…

在Gradio实现两个下拉框进行联动案例解读:change/click/input实践(三)

本文的代码来自ChuanhuChatGPT&#xff0c;通过拆解写得比较好的gradio项目&#xff0c;可以更快理解gradio的一些使用。 ChuanhuChatGPT整体页面效果是比较合理的&#xff1a; 1 下拉框联动效果的解读 本篇是将一个其中【对话】中的【Prompt加载】小模块抽取出来并稍稍修改…

电商平台api接口,淘宝/天猫、1688、拼多多、亚马逊等电商数据平台api接口演示案例

API简单来说是一种数据的传输方式&#xff0c;使用已经开发好的API接口可以缩短项目时间&#xff0c;减少开发成本。 比如说数据宝平台提供的实名认证API接口&#xff0c;像这种实名认证类的API接口是无法自行开发的&#xff0c;如果自行对接部委&#xff0c;能否成功不说&…

【已解决】vscode 配置C51和MDK环境配置

使用命令 gcc -v -E -x c - 看自己gcc 有没有安装好 也可以在自己的vscode中新建一个终端 gcc -v g -v 首先把自己的C51 和MDK 路径 设置好 vscode 中设置 C51 和 MDK 的路径 这是你keil 中写 51单片机和 STM32 的 如果你出现什么include 的什么波浪线&#xff0c;那估计…

力扣511. 游戏玩法分析 I

答案&#xff1a; select player_id,min(event_date) as first_login from Activity a group by player_id我最开始写的错误答案是这样的&#xff1a; select player_id,event_date as first_login from Activity a group by player_id having event_date min(event_date…

一篇揭秘Linux高性能服务epoll 的本质

导语 epoll接口是为解决Linux内核处理大量文件描述符而提出的方案。该接口属于Linux下多路I/O复用接口中select/poll的增强。其经常应用于Linux下高并发服务型程序&#xff0c;特别是在大量并发连接中只有少部分连接处于活跃下的情况 (通常是这种情况)&#xff0c;在该情况下能…

copilot 产生 python工具函数并生成单元测试

stock.py 这个文件&#xff0c;我只写了注释&#xff08;的开头&#xff09;&#xff0c;大部分注释内容和函数都是copilot # split a string and extract the environment variable from it # input can be , pathabc, pathabc;pathdef, pathabc;pathdef;pathghi # output i…

快速拉取聚水潭单据的ETL工具

聚水潭介绍 聚水潭平台则是国内较为出名的电商ERP平台&#xff0c;为企业提供了便捷的销售和管理服务&#xff0c;专注于提高交易效率&#xff0c;但是如何将数据快速同步到其他系统一直是很多企业的痛点。 ETLCloud数据集成平台提供了丰富的数据分析工具和算法模型&#xff…