详解c语言指针(代码片段)

Yuucho Yuucho     2022-12-02     710

关键词:

文章目录


复习:

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

1.字符指针

在指针的类型中我们知道有一种指针类型为字符指针char*;
一般使用:

int main()

	char ch = 'w';
	char *pc = &ch;
	*pc = 'w';
	return 0;

还有一种使用方法如下:

int main()

	const char *pstr = "hello bit.";//这里是把一个字符串放到pstr指针变量里了吗?
	printf("%s\\n",pstr);
	return 0;

代码const char* pstr = "hello bit.";特别容易以为是把字符串"hello bit."放到字符指针pstr里了,但是本质是把字符串"hello bit."首字符的地址放到了pstr中。

上面代码的意思是把一个常量字符串的首字符h的地址放到指针变量pstr中。
下面请看一道例题:

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

这里最终输出的是:

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

2.指针数组

指针数组是一个存放指针的数组。

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

3.数组指针

3.1 数组指针的定义

数组指针是指针?还是数组?
答案是:指针。
我们已经熟悉:
整型指针:int * p;能够指向整型数据的指针。
浮点型指针:float * pf;能够指向浮点型类型数据的指针。
那数组指针应该是:能够指向数组的指针。
下面代码哪个是数组指针?

int *p1[10];
int (*p2)[10];
//p1,p2分别是什么?

解释:

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

3.2 &数组名和数组名的区别

对于下面的数组:

int arr[10];

arr&arr分别是啥?
我们知道arr是数组名,数组名表示首元素的地址。
那&arr数组名到底是啥?
我们看一段代码:

#include <stdio.h>
int main()

	int arr[10] = 0;
	printf("%p\\n",arr);
	printf("%p\\n",&arr);
	return 0;

运行结果如下:

可见数组名和&数组名打印的地址是一样的。
难道两个是一样的吗?
我们再看一段代码:

#include <stdio.h>
int main()

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


根据上面的代码我们发现,其实&arr和arr,虽然值是一样的,但是意义应该是不一样的。
实际上:&arr表示的是数组的地址,而不是数组首元素的地址。(细细体会一下)
本例中&arr的类型是:int(*)[10]是一种数组指针类型
数组的地址+1,跳过整数组的大小,所以&arr+1相对于&arr的差值是40.

3.3 数组指针的使用

那数组指针怎么使用呢?
既然数组指针指向的是数组,那数组指针中存放的应该是数组的地址。
看代码:

#include <stdio.h>
int main()

	int arr[10] = 1,2,3,4,5,6,7,8,9,0;
	int (*p)[10] = &arr;//把数组arr的地址赋值给数组指针变量p
	//但是我们一般很少这样写代码
	return 0;

一个数组指针的使用:

#include <stdio.h>
void print_arr1(int arr[3][5],int row,int col)

	int i = 0;
	for(i=0;i<row;i++)
	
		for(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(j=0;j<col;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,3,5);
	//数组名arr,表示首元素的地址
	//但是二维数组的首元素是二维数组的第一行
	//所以这里传递的arr,其实相当于第一行的地址,是一维数组的地址
	//可以用数组指针来接受
	print_arr2(arr,3,5);
	return 0;

学了指针数组和数组指针我们来一起回顾并看看下面代码的意思:

  1. int arr[5]; arr是一个整型数组,有5个元素,每个元素是int类型的。
  2. int *parr1[10];parr1是一个数组,数组有10个元素,每个元素的类型是int *;所以parr1是指针数组
  3. int (*parr2)[10];parr2先和*结合,说明parr2是一个指针,该指针指向一个数组,数组是10个元素,每个元素是int类型的,parr2是数组指针
  4. int (*parr3[10])[5];parr3和[]结合,说明parr3是一个数组,数组是10个元素,数组的每个元素是什么类型呢?是一种数组指针,类型是int( * )[5],该类型的指针指向的数组有5个int类型的元素。

4.数组参数,指针参数

4.1 一维数组传参

#include <stdio.h>
void test(int arr[])

void test(int arr[10]

void test(int *arr)

void test2(int *arr[20])

void test2(int **arr)

int main()

	int arr[10] =0;
	int *arr2[20] = 0;
	test(arr);
	test2(arr2);//数组指针首元素的地址是二级指针

注:以上一维数组传参的方式都是可以的。

4.2 二维数组传参

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);

二维数组的数组名是一个数组指针,应该用数组指针来接收。

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 test1(int *p)

//test1函数能接收什么参数?
void test2(char* p)

//test2函数能接收什么参数?

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;
 test(pp);
 test(&p);
 return 0; 

思考:

当函数的参数为二级指针时,可以接收什么参数?

  1. 二级指针
  2. 一级指针的地址
  3. 存放一级指针的数组的数组名
void test(char **p) 
 

int main()

 char c = 'b';
 char*pc = &c;
 char**ppc = &pc;
 char* arr[10];
 test(&pc);
 test(ppc);
 test(arr);
 return 0; 

5.函数指针

请看一段代码:

#include <stdio.h>
void test()

 printf("hehe\\n");

int main()

 printf("%p\\n", test);
 printf("%p\\n", &test);
 return 0; 

输出的结果:

注意:
数组名不等于&数组名 函数名 等于&函数名
输出的是两个地址,这两个地址是test函数的地址。
那我们的函数的地址要想保存起来,怎么保存?
下面我们看代码:

void test()

 printf("hehe\\n");

//下面pfun1和pfun2哪个有能力存放test函数的地址?
void (*pfun1)();
void *pfun2();

首先,能给存储地址,就要求pfun1或者pfun2是指针,那哪个是指针?
答案是:

pfun1可以存放。pfun1先和*结合,说明pfun1是指针,指针指向的是一个函数,指向的函数无参
数,返回值类型为void。

阅读两段有趣的代码:

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

代码1是将0强制转换为函数指针类型并调用
代码2:

代码2太复杂,如何简化:

typedef void(*pfun_t)(int);//对void(*)(int)的函数指针类型重命名为pfun_t
pfun_t signal(int, pfun_t);

6.函数指针数组

数组是一个存放相同类型数据的存储空间,那我们已经学习了指针数组
比如:

int *arr[10];
//数组的每个元素是int*

那要把函数的地址存到一个数组中,那这个数组就叫函数指针数组,那函数指针的数组如何定义呢?

int (*parr1[10])();
int *parr2[10]();
int (*)() parr3[10];

答案是:parr1
parr1 先和 [] 结合,说明 parr1是数组,数组的内容是什么呢?
int (*)() 类型的函数指针。
函数指针数组的用途:转移表
例子:(计算器)

#include <stdio.h>
int add(int a, int b) 
           return a + b; 
int sub(int a, int b) 
           return a - b; 
int mul(int a, int b) 
           return a*b; 
int div(int a, int b) 
           return a / b; 
int main()

	 int x, y;
     int input = 1;
     int ret = 0;
     int(*p[5])(int x, int y) =  0, add, sub, mul, div ; //转移表
     while (input)
     
          printf( "*************************\\n" );
          printf( " 1:add           2:sub \\n" );
          printf( " 3:mul           4:div \\n" );
          printf( "*************************\\n" );
          printf( "请选择:" );
      scanf( "%d", &input);
          if ((input <= 4 && input >= 1))
         
          printf( "输入操作数:" );
              scanf( "%d %d", &x, &y);
              ret = (*p[input])(x, y);
         
          else
               printf( "输入有误\\n" );
          printf( "ret = %d\\n", ret);
     
      return 0; 

7.指向函数指针数组的指针

指向函数指针数组的指针是一个 指针
指针指向一个 数组 ,数组的元素都是 函数指针 ;
如何定义?

void test(const char* str) 
 printf("%s\\n", str);

int main()

 //函数指针pfun
 void (*pfun)(const char*) = test;
 //函数指针的数组pfunArr
 void (*pfunArr[5])(const char* str);
 pfunArr[0] = test;
 //指向函数指针数组pfunArr的指针ppfunArr
 void (*(*ppfunArr)[5])(const char*) = &pfunArr;
 return 0; 

8.回调函数

回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)

[c语言]详解指针合集(代码片段)

文章篇幅较长,如有需要请先收藏❤❤❤文章目录一、指针是什么二、指针常见的错误1.未初始化就使用2.指针越界访问3.有趣的代码+习题三.动态内存管理1.malloc,free,calloc,realloc的基本使用2.malloc的使用2.callo... 查看详情

c语言结构体指针详解(代码片段)

结构体指针,可细分为指向结构体变量的指针和指向结构体数组的指针。指向结构体变量的指针前面我们通过“结构体变量名.成员名”的方式引用结构体变量中的成员,除了这种方法之外还可以使用指针。前面讲过,&s... 查看详情

c语言初阶笔记重点初识指针,详解!!(代码片段)

目录指针指针是什么指针和指针类型指针+-整数指针的解引用野指针野指针成因如何规避野指针指针运算1.指针+-整数2.指针-指针3.指针的关系运算 指针和数组 二级指针对二级指针的理解对于二级指针的运算指针数组总结... 查看详情

c语言中的指针详解(代码片段)

1.什么是指针C语言中指针是一种数据类型,指针是存放数据的内存单元地址。计算机系统的内存拥有大量的存储单元,每个存储单元的大小为1字节,为了便于管理,必须为每个存储单元编号,该编号就是存储... 查看详情

c语言从青铜到王者第六篇·详解c指针(代码片段)

本篇前言“指针是C语言的灵魂,精通指针也就基本精通了C语言。”指针确实非常重要,但是它只是一个工具,是帮助我们分配内存的工具,而了解指针,并且通过指针熟练的操作内存,这才是我们努力的... 查看详情

详解c语言那些可怕的野指针(代码片段)

????????关注后回复 “进群” ,拉你进程序员交流群????????作者丨李肖遥来源丨技术让梦想更伟大一、什么是野指针?指针是C语言的灵魂,同时也是很容易让人犯错的重难点,用错了指针将是一个灾难。指针变量... 查看详情

go语言学习笔记—基础—基本数据类型—指针:一文详解go指针(代码片段)

说到指针,许多人感到“谈指针色变”,对指针偏移、指针运算、指针转换非常恐惧。其实,指针是C族语言(C/C++)极高性能的根本,在操作大块数据和做内存地址偏移时,方便快捷。C族语言... 查看详情

c++编程知识:什么是万能指针?详解c语言万能指针的妙用(代码片段)

简单认识一下void今天跟大家介绍的知识是C语言中的void关键字的用法,void在大部分小伙伴的程序中都只是用于函数无参数传入,或者无类型返回。然而我们平时所定义的变量都会有具体的类型,int,float,char... 查看详情

c/c++学习笔记:智能指针详解(代码片段)

C++ 智能指针详解 一、简介由于 C++ 语言没有自动内存回收机制,程序员每次 new 出来的内存都要手动 delete。程序员忘记 delete,流程太复杂,最终导致没有 delete,异常导致程序过早退出,没... 查看详情

预处理过程详解(代码片段)

前言上一篇文章说到,这将是博主更新的最后一篇关于C语言知识点的博客,也确实,因为这篇文章是讲解的c的预处理,也是c语言最后的一部分知识了,还是老话,博主的所有文章几乎篇幅都比较长,大家可以根据目录进行选择性观看,同... 查看详情

c语言链表详解(代码片段)

前言 学习链表之前,先来看几个术语:  首节点:存放第一个有效数据的节点;  尾节点:存放最后一个有效数据的节点;  头节点:头节点的数据类型与首节点的数据类型相同,并且头节点是首节点前... 查看详情

[学习笔记]c语言中关于指针的详解1(代码片段)

[学习笔记]C语言中关于指针的详解1什么是指针指针变量指针的类型指针的定义指针的运算符数组的指针指针的运算指针数组指针的指针字符串和指针数组指针指针与函数的关系什么是指针操作系统给每个存储单元分(即一个... 查看详情

c语言---指针变量详解1(代码片段)

数据在内存中的地址也称为指针,如果一个变量存储了一份数据的指针,我们就称它为指针变量。在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。指针变量的值就是某份数据的地址,这样的一份数据可以是数... 查看详情

c语言从青铜到王者第六篇·详解c指针(代码片段)

本篇前言“指针是C语言的灵魂,精通指针也就基本精通了C语言。”指针确实非常重要,但是它只是一个工具,是帮助我们分配内存的工具,而了解指针,并且通过指针熟练的操作内存,这才是我们努力的... 查看详情

c语言从青铜到王者第六篇·详解c指针(代码片段)

本篇前言“指针是C语言的灵魂,精通指针也就基本精通了C语言。”指针确实非常重要,但是它只是一个工具,是帮助我们分配内存的工具,而了解指针,并且通过指针熟练的操作内存,这才是我们努力的... 查看详情

《c语言》——数组详解(代码片段)

书籍使我变成了一个幸福的人,使我的生活变成轻松而舒适的诗。——高尔基本文已经收录至我的GitHub,欢迎大家踊跃star和issues。https://github.com/midou-tech/articles前言 我本来准备C语言章节就写个指针就ok了,在我看来C语言的精... 查看详情

c语言一维数组详解(代码片段)

文章目录一维数组多角度看一维数组一维数组声明问题1:那么b的类型是什么呢?它所表示的又是什么?问题2:指针和数组是等价的吗?问题3:那么数组在什么情况下不能作为指针常量呢?下标引用问... 查看详情

c语言一维数组详解(代码片段)

文章目录一维数组多角度看一维数组一维数组声明问题1:那么b的类型是什么呢?它所表示的又是什么?问题2:指针和数组是等价的吗?问题3:那么数组在什么情况下不能作为指针常量呢?下标引用问... 查看详情