IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> C语言 指针中的常见名称与用法 -> 正文阅读

[C++知识库]C语言 指针中的常见名称与用法

目录

前言

一、指针是什么

二、指针与数组

数组指针

指针数组

三、指针与常量

指针常量

常量指针

四、指针与函数

指针函数

函数指针


前言

指针是C语言中大家接触的比较早但是也是内容比较多和实用的一块知识点,之前虽然也大概知道是什么用途,但是指针衍生出来的内容还是比较容易混淆的,这次在7哥的激励下写篇博客总结一下指针方面易混淆的内容,如有错误欢迎指正。


一、指针是什么

指针变量其实就是存放地址的变量,我们知道变量具有三部分信息,即

  1. 变量对应的名称
  2. 所表示的数据类型
  3. 存放的值。


大家都知道指针变量存放的是一个内存地址,但是有时候却忽略了指针类型的重要性,根据存放的地址值我们可以知道要从什么位置开始读取数据,而类型信息则向系统补充说明了当指针移动时进行读写的长度,确定我们要读取到什么位置结束。

因为指针变量是存放地址的变量,所以一个指针变量所占的地址空间是固定的,是根据我们程序的寻址空间来确定的(比如寻址空间 4GB = 2^32 Byte,也就是32位的CPU可以访问的最大内存空间,字节在计算机中是最小的可寻址单位,所以用 4 个字节即 2^32 bits 表示指针就已经能指向任何内存空间了,同理64位系统默认指针大小为8个字节。),所以指针的大小根据不同的操作系统为4个或8个字节
上面说明的是指针变量本身的所占的空间大小,而指针变量的值,也就是存放的地址信息,本质上都是一段地址而已,而指针的类型则确定了要将地址中的值按照 int 型变量还是 double 型等不同的数据类型变量进行解释

那么了解了指针,接下来要面对的就是?数组指针 指针数组 指针常量 常量指针 函数指针 指针函数这些牛鬼蛇神,虽然名字看起来很唬人非常容易混淆,接下来将一一进行说明,先总结一下它们各自的定义如下:
?

常见指针变量的定义
定义含义
int ?i定义整形变量
int *p定义一个指向int的指针变量
int *const p定义一个指向int的指针常量指针常量
int const * p
const int * p
定义一个指向int的只读指针常量指针
int a[10]定义一个int数组
int *p[10]定义一个指针数组,其中每个数组元素指向一个int型变量的地址指针数组
int (*p)[10]定义一个数组指针,指向int [10]类型的指针变量数组指针
int func(? )定义一个函数,返回值为int型
int *func(? )定义一个函数,返回值为int *型指针函数
int (*p)(? )定义一个指向函数的指针,函数的原型为无参数,返回值为int函数指针
int **p定义一个指向int的指针的指针,二级指针


二、指针与数组

C语言规定,数组名代表数组中首个元素(即序号为 0 的元素)的地址,也就是说,如果 s 是一个数组,那么表达式 s 的值就与 s[0] 的地址,即 &s[0] 一致。所以当我们执行赋值语句 p = &s[0]??时,指针变量 p 与数组名 s 具有相同的值,可以写作 p = s?,指针 p 的行为就和数组 s 本身一样。p?在这里指向的是数组的首元素。

int s[3]={1,2,3};
int *p 
p = &s[0];

数组指针

数组指针就是指向数组的指针,声明形式为 类型(*变量名)[]
因为数组下标 [] 的优先级高于取值运算符 * ,所以必须用括号()将?* 与 arrPtr 先结合构成指针定义,然后指向一个数组。


第一种情况(指向二维数组名):

int a[4][10];  
int (*p)[10];  
p = a;

与一维数组的数组名是一个指针相似,二维数组的数组名是一个数组指针,若将二维数组名 a 赋值给数组指针 p ,则可以通过 p 直接控制数组 a

3ab4e13b406c408497d6870a8c3cf889.png

第二种情况(指向一维数组名的地址):

int a[10];  
int (*p)[10] = &a; //此处是&a,不是a, &a的类型是int(*)[]  
int *q = a;

8a07ee1ff3474180bd8b7f3a7b42affa.png

p 与 q 虽然都指向数组的第一个元素,但由于 p 的类型和 q 的类型不同 ,数组指针 p 是指向10个元素整形数组的指针, *p 的大小是40个字节,所以?p+1 跳过40个字节;q 是指向整形的指针,*q 的大小是4个字节,所以 q+1 跳过4个字节。


指针数组

int * arrPtr[10]; //声明一个指针数组arrPtr,该数组有10个int类型的指针

元素为指针类型的数组,声明形式为 类型名 * 数组名[数组长度]
由于指针也是变量,所以指针也可以像其他变量一样存储在数组中。指针数组的每一个元素都是一个指针类型的数据,即元素的值为地址,可以指向某一个字符串常量。

指针数组最频繁的用处是存放具有不同长度的字符串,作为二维数组的一种便捷替代方式,适合用来存放多个字符串作为字符串数组来使用。
我们在遇到需要处理多个字符串的情况时,可以选择将它们存储在一个二维数组中,二维数组的元素 —— 一维数组 的大小必须足够存储下可能出现的最长字符串,如下表示:

#define ARRAY_LEN 12
#define STRLEN_MAX 25
char pStrings[ARRAY_LEN][STRLEN_MAX] = 
{
	"It's a wonderful day!",
	"do you like it?",
	"Yes"
};

for(i=0;i<ARRAY_LEN;i++)
{
	puts(pstr[i]);
}

但是,这个方式造成内存浪费,一方面,短字符串会让大部分的行是空的,另一方面,有些行根本没有使用到,但却得为它预留内存,使得分配的12*25=300 个字节只有一小部分被实际使用到。
对于这个问题,一个简单的解决方案就是使用指针数组,让指针指向字符串,然后只给实际存在的对象分配内存,未用到的数组元素则默认是空指针。

#define ARRAY_LEN 12
char *pStrings[ARRAY_LEN] =
{
	"It's a wonderful day!",
	"do you like it?",
	"Yes"
};

指针数组让各个指针内容可以按需要动态生成,避免了空间浪费,并且各个指针呈数组形式排列,索引起来非常方便。

指针数组另一个常见作用是作为main函数的形式参数

int main(int argc, char * argv[]);

argc 和 argv 主要用途为程序运行时,将命令行中的输入参数传递给调用函数。char * argv[]?是一个存储字符串的指针数组,会以字符串的形式保存用户调用程序时传入的参数,存放着指向每一个参数的指针。


三、指针与常量

const 是一个C语言的类型限定符,任何变量只要其类型限定符为 const ,则表示对于这些变量,我们无法在后续的程序中修改其对应或指针指向的值。因此,我们常称它们为常量,但其实应该说是个“只读变量”而非常量,只读变量和常量的共同点在于它们的值在第一次出现时便被确定,且无法在后续程序中被修改。但是只读变量和常量的一个最重要的不同点在于,要注意使用 const 修饰的只读变量不具有“常量表达式”的属性,因此无法用来表示定长数组大小,或者是使用在case语句中。常量表达式本身会在程序编译时被求知,而只读变量的值只能够在程序实际运行时才被得知。所以这边大家要知道他们之间是有一定的区别的,但是下面仍用常量来指代只读变量。

#include <stdio.h>
int main(void)
{
	const int temp = 10;
	int a;
	scanf("%d",&a);
	int s[temp] = {1,2,3} /* 错误,使用非常量表达式定义定长数组 */
	switch(a){
	case temp: printf("you have a trouble"); break; /* 错误:非常量表达式应用于case语句 */
	}
}

const 限定一个变量不允许被改变,使用在指针变量中可以用来限定指针本身的类型,或者是限定指针所指对象的类型,分别对应着我们的指针常量和常量指针。


指针常量

int * const p;

直译是指针类型的常量,联系我们刚刚提到的“只读变量”,我们可以理解成指针类型的“只读变量”,它是一个指针变量但是这个变量是“只读变量”。

而变量是只读的说明了指针其本身存放的内容(值)也就是指向的位置(地址)不能改变,指针本身是一个常量,但是指针所指向的内容(值)是不受限制可以发生改变的。

int a = 1, b = 2;
int * const p = &a; //指针常量
*p = 3; // 可以赋值,指针指向的内容可以修改 
p = &b; /* 错误,指针常量本身的内容不可改变,其值即指向的地址是固定的 */

常量指针

//两种方式都可
const int *p = &a; 
int const *p = &a;

直译是指向常量的指针,同样的我们可以理解成指向“只读变量”的指针,首先是一个指针,其次这个指针是一个指向“只读变量”(具有const限定的变量)的指针,所以可以修改该指针本身的值,但是只能使用这样的指针来读取所指向的对象,不能修改所指向的对象,因此指向常量的指针也常常被称为“只读指针”。

int a = 1, b = 2;
const int *p1 = &a; //常量指针
p = &b; // 可以赋值,因为指针本身是变量,其本身存放的内容即指向的地址是可以改变的
*p = 3; /* 错误,不能修改指向的对象内容 */


四、指针与函数

指针函数

int * function(int x);

返回指针类型的函数,我们知道一个函数的返回值可以是各种数据类型的,我们常见的是整型的当然也可以是返回的指针类型的数据,即返回一个地址。


函数指针

指向函数的指针,声明形式为 函数返回值类型名(* 指针变量名)(函数参数列表)
与数组名代表数组首个元素地址类似,函数名会被隐式的转换成函数指针,可以说函数名本身就可以表示该函数的入口地址,赋值语句?p = function?将函数function()的地址赋值给指针变量 p ,然后就可以利用该指针调用这个函数。

int function(int a, int b);//假设这里定义了一个函数
int (* p)(int, int); //声明一个函数指针,该函数具有两个int类型的参数和int类型的返回值
p = function;        //把函数名赋值给函数指针 *p
int ret = function(10,100);//调用函数的方式1:通过函数名直接调用
int ret2 = (* p)(10,100);  //调用函数的方式2:通过函数指针调用 

函数指针的定义其实就是将“函数声明”中的“函数名”改成“(*指针变量名)”。但是这里需要注意的是:“(*指针变量名)”两端的括号不能省略,括号改变了运算符的优先级。如果省略了括号,就不是定义函数指针而是一个返回值类型为指针型的函数声明了。
函数指针变量既然是一个变量,当然也可以作为某个函数的参数来使用的,这就是我们函数指针的一个常见用法:回调函数
如果把函数指针作为参数传递给另一个函数,当这个函数指针被用来调用其所指向的函数时,就是一个回调函数。它使得用户可以把需要调用的方法的指针作为参数传递给一个函数,我们只要改变传进另一个函数的参数,就能够让另外这个函数实现不同的功能,相比普通的函数调用灵活很多。?

int Callback_1(int a)   ///< 回调函数1
{
    printf("Hello, this is Callback_1: a = %d ", a);
    return 0;
}

int Callback_2(int b)  ///< 回调函数2
{
    printf("Hello, this is Callback_2: b = %d ", b);
    return 0;
}

int Callback_3(int c)   ///< 回调函数3
{
    printf("Hello, this is Callback_3: c = %d ", c);
    return 0;
}

int Handle(int x, int (*Callback)(int)) ///< 注意这里用到的函数指针定义
{
    Callback(x);
}

int main()
{
    Handle(4, Callback_1);
    Handle(5, Callback_2);
    Handle(6, Callback_3);
    return 0;
}


?参考链接:

C语言-数组指针,指针数组与数组名的指针操作

C语言回调函数详解(全网最全)_小熊coder的博客-CSDN博客_c语言回调函数

C 语言中的指针与数组

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2022-09-13 10:54:41  更:2022-09-13 10:56:55 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/19 15:16:27-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码