【C的葵花宝典进阶篇】之指针进阶(一)

这篇具有很好参考价值的文章主要介绍了【C的葵花宝典进阶篇】之指针进阶(一)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


【C语言进阶技巧】指针掌握之道:解密指针的奇妙世界(第一部))
【C的葵花宝典进阶篇】之指针进阶(一),C语言进阶篇,指针,程序人生,c语言,算法,青少年编程,microsoft

❤️博客主页: 小镇敲码人
🍏 欢迎关注:👍点赞 👂🏽留言 😍收藏
🌞回来4天了,加油!!!🍎🍎🍎
💗当你的能力匹配不上你的梦想,当你需要实现的目标匹配不上你的圈子的时候,你就会出现错位。当我们的能力还匹配不上我们的梦想时,我们就需要沉淀学习。✡️✡️✡️

【C的葵花宝典进阶篇】之指针进阶(一),C语言进阶篇,指针,程序人生,c语言,算法,青少年编程,microsoft

1. 字符指针

字符指针,就是储存字符变量地址的指针,这是一种指针的类型。

  • 通常我们字符指针有如下两种用途
  1. 通过字符指针间接改变字符的值
    int main()
    {
      char ch = 'c';
     // ch = 'a';//直接通过变量赋值更改ch的值
      char *p = &ch;//将字符ch的地址存入p中
      //*p = 'a';//通过对p里面的地址解引用找到ch,间接的改变ch的值
    }
  1. 借助字符指针对字符串进行储存和打印
 int main()
 {
   const char* ptr = "abcdef";
   printf("%s\n",ptr);
   return 0;
 }
  • const修饰字符串表示"abcdef"不可修改,为常量字符串
  • 字符指针变量ptr只储存了字符'a'的地址。 (可类比字符数组理解,实际上字符串是字符数组的一种形式,在C语言中,字符串是由字符数组表示的,以空字符(‘\0’)作为字符串的结束标志,又因为数组名表示首元素地址,所以字符指针也只储存了首元素的地址)

2. 指针数组

      指针数组是由指针组成的数组。在C和C++等编程语言中,指针是一个变量,用于存储内存地址。指针数组是一个数组,其每个元素都是指针类型。
    int *arr1[10];    整形指针数组
    char *arr2[10];  一级字符指针的数组
    char **arr3[10]; 二级字符指针的数组

2.1 整形指针数组

int main()
{
    int a = 0;
        int b = 1;
    int c = 2;
    int* arr[] = { &a,&b,&c };
    for (int i = 0; i < 3; i++)
    {
        printf("%d ", *arr[i]);
    }
}

2.2 用指针数组模拟二维数组

int main()
{
	int arr1[] = { 1,2,3,4,5 };//arr1-int*
	int arr2[] = { 2,3,4,5,6 };//arr2-int*
	int arr3[] = { 3,4,5,6,7 };//arr3-int*
   //指针数组
	int* p[] = { arr1,arr2,arr3 };
	for (int i = 0; i < 3; i++)
	{
		for (int j = 0; j < 5; j++)
		{
		  //这两种方式打印都可以,两者择一
			printf("%d ", *(*(p + i) + j));
            printf("%d ",p[i][j]);
		}
		printf("\n");
	}
}

3. 数组指针

我们可以通过类比来理解

整型指针:int * a;能够指向整形数据的指针
浮点型指针:float* b;能够指向浮点型数据的指针。
那么数组指针应该是:能够指向数组的指针。

3.1 数组指针的表示方法

数组的类型表示是什么?通过简单的类比可以知道。

  1. int a = 3;类型为除变量名以外的部分int就是整形的类型表示形式。
  2. char b = 'a';类型为除变量名以外的部分char就是字符类型的的表示形式。
  3. int arr[10] = {0};类型为除变量名以外的部分int [10]就是这个数组的变量类型表示形式。
  4. 而数组指针的类型是指针,指针的类型是数组,通过前面的学习我们知道,变量类型+变量名为一个变量,所以一个数组指针变量就是,指向数组的指针变量,int [10] *p = &arr;,看起来似乎是这样,但是通过下面符号的优先级我们可以知道[]的优先级要比*号高,所以我们要给*p加上括号,让其成为指针,int [10] (*p) = &arr,那这样是否正确呢?还是不对,放进vs编译器会报错,数组的类型比较特殊,应该是这样int (*p) [10] = &arr;才正确。

3.2 深度剖析&数组名和数组名

对于下面的数组

 int arr[10];
  • arr&arr分别是什么呢?
    对于arr,我们知道它是一个数组名,数组名表示首元素的地址,
    那&arr是什么呢,我们来看下面一段代码:
int main()
{
	int arr[10] = { 0 };
	printf("arr       = %p\n", arr);
	printf("arr+1     = %p\n", arr+1);

	printf("&arr[0]   = %p\n", &arr[0]);
	printf("&arr[0]+1 = %p\n", &arr[0]+1);
	
	printf("&arr      = %p\n", &arr);
	printf("&arr+1    = %p\n", &arr+1);
	return 0;
}

运行结果如下:
【C的葵花宝典进阶篇】之指针进阶(一),C语言进阶篇,指针,程序人生,c语言,算法,青少年编程,microsoft
可以看到数组名和&数组名打印的地址和首元素打印的地址是一样的,这是不是意味着它们是一样的呢?很明显通过代码我们也可以发现,它们并不是相同的,因为&arr+1打印的地址与arr+1打印的地址相差了40个字节,而arr+1打印的地址和首元素&arr+1的仍然一样,进一步说明,arr就表示首元素地址。

  • 实际上:&arr表示的是整个数组的地址,它的类型是int (*)[10],是一种数组指针类型。
                   而arr代表的是数组首元素的地址,它的类型是int *
                   数组的地址加1,跳过整个数组的大小,所以&arr+1与&arr的差值就是 10 ∗ 4 = 40 10*4 =40 104=40个字节。

3.3 数组指针的使用

3.3.1 在同一函数内直接将数组的地址赋给数组指针

#include<stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	//int[10] *p = &arr;err
	int (*p)[10] = &arr;
	for (int i = 0; i < 10; i++)
	{
	    //两种打印方式择一
	    printf("%d ",*((*p)+i));
		printf("%d ", (*p)[i]);
	}
	//int* p2 = &arr;//err
	return 0;
}

  • 可以看到,这样做简直就是多此一举,因为想打印一维数组,我们直接打印就行了,使用数组指针,反倒是比较麻烦。

3.3.2 数组指针在二维数组传参上的应用

3.3.2.1 二维数组传参使用二维数组
#include<stdio.h>
void Print(int arr[][5], int r, int c)
{
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < c; 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;
}
3.3.2.2 二维数组传参使用数组指针
#include<stdio.h>
Print(int(*p)[5], int r, int c)
{
	for (int i = 0; i < r; i++)
	{
		for (int j = 0; j < c; j++)
		{
		  //两种打印方式择一
			printf("%d ", *(*(p + i) + j));
			printf("%d ", p[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 };
	//arr表示数组名,数组名表示首元素的地址,也就是那一行的地址
	Print(arr, 3, 5);
	return 0;
}
  • 因为二维数组的数组名代表首元素的地址,首元素地址就是那一行的地址,就等价于一维数组的地址(&一维数组数组名),故又称二维数组为一维数组的数组,所以可以用数组指针来接收。

4. 数组传参和指针传参

4.1 一维数组传参

  • 有以下主要两种方式
  1. 传递数组名,用一级指针的方式去接收,通过地址访问
  2. 传递数组的副本
#include<stdio.h>
void test1(int arr[])
{}
void test1(int arr[10])
{}
void test1(int* arr)
{}
void test2(int* arr[20])
{}
void test2(int** arr)
{}
int main()
{
	int arr1[10] = { 0 };
	int* arr2[20] = { 20 };
	test1(arr1);
	test2(arr2);
	return 0;
}

4.2 二维数组传参

  • 正确的方法已经在“3.3.2 数组指针在二维数组传参上的应用“版块给出,下面就几种错误的传参方法做一下阐述。
#include<stdio.h>
void test(int arr[3][5]){}
void test(int arr[][])//err,二维数组传参,如果是传递数组的副本,需要给出一行有多少数字,因为二维数组其实是一维数组的扩展,所以需要提供一行有多少元素,来正确进行内存访问
{}
void test (int arr[][5]){}
void test(int *arr)//err,传的是一行的地址,指针的类型是数组,int(*)[5]。
{}
void test(int *arr[5])//err,传的是第一行的地址,用指针数组接收不对,因为传的是一行的地址。
{}
void  test(int (*arr)[5]){}
void test(int **arr)//err,传的是数组的地址(第一行的地址),不是一级指针的地址,不能用二级指针接收。
{}
int main()
{
	int arr[3][5] = { 0 };
	test(&arr);
	return 0;
}
  • 注意:传二维数组的副本必须给定一行有多少个数,在C语言中,二维数组在内存中以连续的块存储,可以看作是一维数组的扩展。当传递二维数组作为参数时,需要提供一行有多少个元素的信息,以便在函数内部正确地进行内存访问。
    二维数组在内存中按行主序(row-major order)存储。也就是说,二维数组的每一行依次存储在内存中,并且相邻的元素在内存中也是相邻的。通过提供一行有多少个元素的信息,可以根据内存布局准确地计算出每个元素的地址,从而进行正确的访问。
  • 二维数组不能用二级指针来接收,因为二级指针是指向一级指针的,很明显,二维数组的数组名的类型是第一行即数组的地址应该用这个int (*)[5]即用数组指针接收。
  • 二维数组也不能用指针数组接收,类型不匹配。

4.3 一级指针传参

当函数形参为一级指针时,实参可以是同类型一维数组数组名、同类型变量的地址、同类型指针。

  • 请看如下代码加深理解:
#incldue<stdio.h>
void print(int* p, int sz)
{
    for (int i = 0; i < sz; i++)
    {
        printf("%d ", *(p + i));
        printf("%d ", p[i]);
    }
}
int main()
{
    int arr[5] = { 0,1,2,3,4 };
    int sz = sizeof(arr) / sizeof(arr[0]);
    print(arr, sz);
}
#include<stdio.h>
void test(char *p)
{}
int main()
{
  char ch = 'a';
  char *str = "abcdef";
  char* ptr = &ch; 
  test(str);
  test(&ch);
  test(ptr);
  return 0;
}

4.4 二级指针传参

当函数形参为二级指针时,实参可以是一级指针的地址、二级指针。

  • 请看下面代码,加深理解:
#incldue<stdio.h>
void test(int **ptr)
{
  printf("num = %d\n",**ptr);
}
int main()
{
  int a = 3;
  int* p = &a;
  int **pp = &p;
  test(pp);
  test(&p);
  return 0;
}

5. 函数指针

函数指针和数组指针、字符指针一样都是指针,只不过指针所指向的对象的类型不同,其中函数指针与数组指针最为相似。

5.1 函数的地址

我们先看下面代码,函数名的地址,类比数组的地址

int Add(int a, int b)
{
	return a + b;
}
int main()
{
    printf("%p\n", &Add);
	printf("%p\n", Add);
	return 0;
}

运行的结果:
【C的葵花宝典进阶篇】之指针进阶(一),C语言进阶篇,指针,程序人生,c语言,算法,青少年编程,microsoft
        可以看见输出的是一段地址,这两个地址都是函数Add的地址。既然函数的地址我们知道了,就可以用指针去保存它,我们只需要知道指针指向的对象的类型就行了。

5.2 函数指针的表示方法

  • *号代表变量是一个指针变量,函数指针所指向的类型就是函数声明去掉变量名和分号后保留的部分,包括返回值和参数的类型,与数组指针相似,函数指针的(*变量名)也要放在函数名的位置,而数组指针是放在数组名的位置。

请看如下代码:

void test()
{
  printf("i love programming!\n");
}
//除函数名以外的部分,就是的类型
void (*pf)();//函数指针,指向一个返回值为void,无参数的函数。
void* pf();//表示返回值类型为void*,无参数的函数。。
#incldue<stdio.h>
int Add(int a, int b)
{
	return a + b;
}
int main()
{
	int arr[10] = { 0 };
	int(*p)[10] = &arr;//数组指针
	//int (*)[10]是数组指针类型
	int(*pf) (int, int) = &Add;
	//int (*)(int,int)是函数指针类型
	return 0;
}

再给出一组代码帮助理解:

#include<stdio.h>
int Add(int x, int y)
{
	return x + y;
}
int main()
{
     //&Add和Add都可以
	int (*pf)(int, int) = &Add;
	int (*pf)(int, int) = Add;
	
	int r = Add(3, 5);
	printf("%d\n", r);

	int m = pf(3, 5);
	printf("%d\n", m);
    return 0;
}

运行结果如下:【C的葵花宝典进阶篇】之指针进阶(一),C语言进阶篇,指针,程序人生,c语言,算法,青少年编程,microsoft

  • 函数指针与数组指针有相似性,可以对比着去理解。

5.3 两行有趣的代码

5.3.1 将0强制转换为函数指针类型然后调用

void (*p)() -p是函数指针
void (*)()  -是函数指针类型 

(*  (   ( void(*)() )0 ) )();
//(((void(*)())0)是将0强制转换为函数至真
//(*(函数指针))是将函数指针解引用
//然后不传参数调用(*(函数指针))(); 

【C的葵花宝典进阶篇】之指针进阶(一),C语言进阶篇,指针,程序人生,c语言,算法,青少年编程,microsoft

5.3.2 数组指针、函数指针等特殊变量类型typedef的使用

#include<stdio.h>
typedef int* ptr_t;//一级指针别名
typedef unsigned int uint;//无符号整形的别名

typedef int(*parr_t)[10];//数组指针的别名
typedef int (*pf_t)(int, int);//函数指针的别名
int main()
{
	ptr_t p1;
	uint u2;
	parr_t p2;
	pf_t p3;
}
  • 函数指针与数组指针别名的创建与其变量的创建类似,可类比理解。

5.3.3 函数指针类型的函数的声明,并使用别名typedef化简

请看如下代码:

   void(* signal ( int, void(*)(int) ) )(int);

解析:
【C的葵花宝典进阶篇】之指针进阶(一),C语言进阶篇,指针,程序人生,c语言,算法,青少年编程,microsoft
          我们可以观察到signal函数的返回类型和其中一个参数是相同的都是函数指针类型,下面我们利用别名typedef来使这段代码看起来更加易懂:

typedef void (*pf_t)(int);
pf_t signal(int,pf_t);

          CSDN上的别名没有高亮,但VS2019里面是有高亮的,定义函数后,你会发现编译器是不会报错,说明这样是可行的。
【C的葵花宝典进阶篇】之指针进阶(一),C语言进阶篇,指针,程序人生,c语言,算法,青少年编程,microsoft

【C的葵花宝典进阶篇】之指针进阶(一),C语言进阶篇,指针,程序人生,c语言,算法,青少年编程,microsoft文章来源地址https://www.toymoban.com/news/detail-535089.html

到了这里,关于【C的葵花宝典进阶篇】之指针进阶(一)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【运维安全】运维界葵花宝典:Nginx配置与优化秘籍

    必要的原理介绍 ● Nginx 里有一个master进程和多个worker进程.master进程并不处理网络请求,主要负责调度工作进程: 加载配置,启动工作进程及非停升级.worker进程负责处理网络请求与响应. ● master进程主要用来管理worker进程,具体包括如下4个主要功能: 接收来自外界的信号 向各wo

    2024年02月21日
    浏览(40)
  • 【程序人生】纳瓦尔:确保稀缺性的最简单的方法就是这个东西来自你的个性 | 纳瓦尔宝典

    目录 纳瓦尔简介 每个人应该找到自己独一无二的专长 确保稀缺性 产品化的关键点

    2024年02月08日
    浏览(44)
  • 【进阶C语言】进阶指针--学会所有指针类型

     本节内容大致目录: 1.字符指针 2.指针数组(数组) 3.数组指针 (指针)--比较重要 4.函数指针--比较重要 5.函数指针数组--用的较少 6.指向函数指针数组的指针--只需要了解就可以 需要掌握每一种类型的符号和用处。 前言:字符指针是一种指针,是众多指针类型中的一种。

    2024年02月07日
    浏览(34)
  • 『C语言进阶』指针进阶(一)

    🔥 博客主页 : 小羊失眠啦 🔖 系列专栏 : C语言 🌥️ 每日语录 : 无论你怎么选,都难免会有遗憾。 ❤️ 感谢大家点赞👍收藏⭐评论✍️ 在C语言初阶中,我们对指针有了一定的了解,指针是个变量,是用来存放地址的,指针的大小是固定的4/8个字节,指针是有类型的

    2024年02月09日
    浏览(30)
  • 『C语言进阶』指针进阶(二)

    🔥 博客主页 : 小羊失眠啦 🔖 系列专栏 : C语言 🌥️ 每日语录 : 无论你怎么选,都难免会有遗憾。 ❤️ 感谢大家点赞👍收藏⭐评论✍️ 在上篇指针进阶中,我们对字符指针、指针数组、数组指针以及数组传参和指针传参有了一定的了解,你以为指针进阶就只有这些内

    2024年02月07日
    浏览(33)
  • 【C语言进阶】 指针进阶(二)

    🔥 博客主页: 小王又困了 📚 系列专栏: C语言 🌟 人之为学,不日近则日退  ❤️ 感谢大家点赞👍收藏⭐评论 ✍️ 目录 一.函数指针  1.1函数指针的认识 1.2函数指针的使用 二、函数指针数组 1.1函数指针的认识 1.2 函数指针数组实现计算器  三、指向函数指针数组的指针

    2024年02月15日
    浏览(37)
  • 【C语言】指针进阶:字符指针&&数组指针&&函数指针

    ✨作者:@平凡的人1 ✨专栏:《C语言从0到1》 ✨一句话:凡是过往,皆为序章 ✨说明: 过去无可挽回, 未来可以改变 🌹 感谢您的点赞与关注,同时欢迎各位有空来访我的 🍁平凡舍 回想之前,我们学了 指针 的一些基础👉 指针与结构体 我们知道了指针的概念: 指针就是

    2023年04月08日
    浏览(37)
  • 【C语言进阶】指针数组 —— 数组指针

    🎬 鸽芷咕 : 个人主页  🔥 个人专栏 : 《C语言进阶篇》 《C语言初阶篇》 ⛺️生活的理想,就是为了理想的生活!    🌈 hello! 各位宝子们大家好啊,前面我们已经把指针大部分内容给学完了,今天就给大家带来数组指针or指针数组!    ⛳️ 很多说这俩名字不是差不

    2024年02月14日
    浏览(30)
  • 进阶C语言-指针的进阶(上)

    通过前面的学习,我们了解了指针的概念: 指针变量就是一个变量,用来存放地址,地址唯一标识一块内存空间。 指针的大小是固定为 4/8 个字节( 32位平台/64位平台 )。 指针是有类型的,指针的类型决定了指针的±整数的步长,指针解引用操作的时候的权限。 指针的运算

    2024年02月09日
    浏览(27)
  • 进阶C语言-指针的进阶(二)

    数组指针 - 指向数组的指针 - 存放的是数组的地址 - 数组名就是数组的地址。 函数指针 - 指向函数的指针 - 存放的是函数的地址 - 函数名就是函数的地址。 🔭阅读下面这两段代码: 🔭 指针数组: 字符指针数组 - 数组 - 存放的是字符指针 整型指针数组 - 数组 - 存放的是整

    2024年02月07日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包