C语言基础指针知识点总结

指针入门,这一篇文章应该就够了。如有错误或不足,还望多多指正。

下面这个链接是博主录的讲解视频,本文配合视频食用更佳哦

点击查看B站指针视频讲解

一、 基本概念

1.1 地址


地址:这里以int类型为例,

因为一个int类型4个字节Byte(32位和64位机器中)
所以可以简单理解为这个int占了四个绿色小方框。

那么此时,这个int变量的地址就是1000,即第一个绿色小方框右边的数字。
即就是:这个绿色方框在内存中所处的位置。

1.2 指针

指针:还是这个例子

指针就是一个变量,比如这个变量名就叫p吧。
那这个p是个啥东西呢?
把这个1000存到p里去,即,把int类型变量a的地址存进新的变量p中。
那么p这个变量就是一个指向a的指针。

通俗理解下就是:
小明是个舔狗,喜欢小a,但是他比较害羞,不敢直接找小a。
所以小明就找到了小p。为什么找小p呢,因为小p有小a家的地址。
这样小明就可以把给小a送的东西放小a家门口了。

这里的小a和小p就相当于例子中的int类型的a变量和指向a的指针p。
我只要找到了p就相当于找到了a

1.3 为什么要使用指针这个东西呢

首先我们要明白一直知识点:

需要说明一下的是指针类型存储的是所指向变量的地址:
所以32位机器只需要32bit,而64位机器需要 64bit

由此可知,在一台电脑上,所有指针大小都是一样的。
那么,当我们定义了一些非常复杂庞大的结构体,比如这个结构体有50个字节(Byte)。
这时候,向函数中传入参数的时候,是直接把这个50个字节的数据的副本传递到形参上快?
还是,直接传递32bit或者64bit的指针快呢??

很明显,传入指针可以大大大大提高运算的效率。

1.4 指针变量的定义

刚才说了,指针变量是来保存别的变量地址的一种变量。那该如何定义呢?
如何让计算机一眼认出来这一串数字是地址,而不是普通的数字呢?

基本格式:
基本类型 *指针变量名 = &变量名;
(也可以先省略=后面的内容,先只定义,不赋值,但是还是建议定义的时候就进行赋值)

//定义变量:
int a = 666;
char b = 'b';

//定义指针变量:
//1.告诉计算机这是一个保存int类型数据地址的指针变量
int *pa = &a;

//2.告诉计算机这是一个保存char字符类型数据地址的指针变量
char *pb= &b;

上面的代码,我定义了两个指针变量,一个是p一个是p2
看见了两个奇怪的符号*&

1.4.1 *

这个符号,为取值运算符
啥是取值运算符呢?

就是,我现在有了指针,指针里存的是一个变量的地址,
我现在要通过地址取到这个变量的值。

注意:指针里存的是地址,就是通过这个地址找到这个地址住的谁。
举例
比如上面的代码中,pa这个变量存的是a的地址,变量a中存的是666。
那么*pa就可以相当于666。

所以可以明确一点。取值运算符会和指针变量连用。
为啥呢?
因为我们要通过地址取地址所指向的变量中存放的值呀!

如果还不明白没关系,在说完&后我对上面的代码解释说明一下。

一个概念:野指针

先看下面代码

#include <stdio.h>

int main(void){
	int *p;
	*p = 10;
	return 0;
}

这里我们首先定义了一个int类型的指针p,当然,这个p里面保存的是个地址,且这个地址所对应的存储空间只能存int数据,所以叫做int类型的指针。
注意
我们是没有个这个p初始化的,所以,开始p里面存的可能是任何一个数,也就是任何一个地址,这个地址,我们有可能有权限访问,也有可能没有权限访问,这个是我们无法控制的。
然后,将这个地址上存的内容取出来*p,再对这个内容进行修改,这样能成功吗?大概率是不能成功的。
因为我们没有初始化p,所以,如果p中开始给随机分配了一个我们没有权限访问的地址,然后,后面我们还要对这个位置的内容进行修改,那就肯定会报段错误。
所以,指针,一定要记得初始化。
这种,没有初始化的指针,就可以称为:野指针。

下面是上面代码运行的结果:
在这里插入图片描述

1.4.2 &

这个符号,为取地址运算符

这个就很简单了。一个int变量名叫a存的是666
我给a前面加上&,变成
&a
那么这个&a就不是666啦。而是,存666的那个内存的地址。

所以我们又可以明确一点。取地址运算符会和一般变量(非指针变量)连用。
为啥呢?
因为指针变量已经是地址了,一般情况我们不需要取地址的地址,即指针的地址。
我们要的是什么。是普通变量的地址。

所以,下面这个代码可以这么理解:

int a = 666;
int *pa = &a;

先定义一个int变量a 里面存着666.
第二行咱们丛后往前看:
&a是取地址,所以说,=后面就是一个地址。
然后我们理解pa = &a:
这其实就相当于pa里面存的是变量a的地址。
然后,pa前面加上*构成了*pa
*pa是什么,是把pa地址中存的整型变量a取出来了。
所以*pa其实就是666,相当于一个整型变量。
然后,最前面是int。int后面的*pa也可以理解为一个int类型的变量。

(这是我本人的一种理解方式,只希望大家能更快理解这个式子,可能底层并不是我这样的,哈哈)

1.4.3 定义的关键

  • 最好定义的时候就对指针进行赋值
  • int类型的指针只能存int类型变量的地址,char类型的指针只能存char类型的地址,要对应起来

二、指针与数组

指针与数组分成两个方面:

一、指向数组元素的指针
二、指针数组(数组元素是指针)

2.1 指向数组元素的指针

2.1.1 数组元素地址赋值给指针变量

首先我们要知道

数组名,其实就是这个数组下标为0的数组元素的地址。
这个地址也就是这个数组的地址。

所以,数组给指针复试和数组元素给指针赋值很不一样。
因为,指针只能存地址。
数组名本身就保存的地址,所以就直接可以给指针变量赋值。
数组的元素保存的就是具体的内容了,可能为int可能为char。
所以,数组元素赋值的时候,就要先用取地址符&得到这个元素的地址,然后再给指针变量赋值。

int c[10], d[20];
int *p, *q=&c[0];

p = &c[3];//数组c下标为3的元素赋值给指针P
p = d;//整个数组的地址赋值给指针p

2.1.2 指针运算

众所周知,数组,存在内存中一片地址连续的区域中
即就是说,数组,在逻辑上是一个元素挨着一个元素,在物理存储上也是一个元素挨着一个元素的。

2.1.2.1 数组元素指针加或减

所以,当指针变量指向数组元素时,指针变量加/减一个整数n,表示指针向后/前移动n个元素!

注意,这里+n是移动一个元素,不是移动n个字节
当然,移动几个元素就指针的具体值就增加或减少这个几个元素所占的字节数
比如:int 数组中,指针指向某个元素,然后给这个指针+2
那么这个指针本身的值就应该加几?
一个int元素占4个字节,俩就是8个字节,
那么地址字节值就要加8

看例子:

int c[20];
int *p1 = &c[9];
p1--;
p1+=3;
2.1.2.2 数组元素指针之间加减(注意,只有加减没有乘除)

相减得到的这个数字是这两个地址之间相差的字节数。
然后,用这个字节数,除以每个元素所占的字节数。
就可以得出这两个地址之间相差几个元素。

2.1.2.3

我们在复习一下:
数组名,就是这个数组的首地址,也是这个数组中下标为0元素的地址。

好了,那如果我们把这个数组的首地址保存在一个指针变量中,然后对这个指针变量直接+或者-
这样是不是就可以遍历数组了呢?

C语言将数组元素中的[ ]称为变址运算符
一维数组元素b[j]的表示形式等价于*(b+j)
(大家不要忘记*是取值运算符哦,就是把这个地址中存的具体的值取出来)

举例:

int b[20],*p = b;
//数组元素b[j]还有下面两种表示方式
//*(p+j)
//*(b+j)
//p[j]  注意:指针变量也是可以带下标的,但是一般只有在数组上才这么用

**注意:**数组名b是符号常量,所以,不能给b赋值。这个b就类似于函数的名字一样,我们不能随便改变已经定义好的函数的名字。

空指针:

C语言设置了一个指针常量NULL,称为空指针。空指针不指向任何存储单元。
空指针可以赋值给任何指针类型的变量。

2.2 指针数组

整形数组中,数组元素都是整型
字符数组中,数组元素都是字符
指针数组中,数组元素都是指针

int i = 1;
int j = 2;
int k = 3;
int l = 4;
int *p[4] = {&i, &j, &k, Kl};

//单目运算符,同优先级的情况下,丛右往左进行运算
//[]先与p结合,表示这是一个数组
//*再与p[]结合,表示这个数组元素类型是指针
//最后加上前面的int,表示这个int类型的指针

注意区分两个概念:

int (*p)[5];
//这里*先与p结合,表示p是一个指针,这个指针指向这个数组,这个数组是int类型的数组


int *p[5];//这个表示指针数组,每个元素都是指针,上面说过了

三、指针与函数

3.1 返回值是指针的函数

指针函数:
函数的返回值的类型既可以是整型(若没有设定,则默认为整型),实型,字符型,也可以是指针型。
返回值为指针类型的函数又称为指针类型的函数,建成指针函数。

具体定义格式如下:
类型名 *函数名(参数表)

int *a(int x)
//解释一下
//这里函数名先和()结合,表示这是一个函数
//然后a()再和前面的*结合,表示这是一个指针类型的函数。
//最后与前面的int结合,表示这是int指针类型的函数,返回值是指针
 //(这个过程有点类似前面的指针数组,可以翻上去复习一下)
    

3.2 指向函数的指针变量

c语言的每个函数的编译的时候,都分配了一段连续的内存空间和一个入口地址,
这个入口地址就称为“指向函数的指针”即函数指针

可以用一个变量来存储函数指针,这个变量就称为“指向函数的指针变量”或者函数指针变量。

通过函数指针变量 就可以调用所指向的函数,改变它的值就可以动态调用不同的函数。

一般定义形式
类型说明符 (*变量名)()
eg:

int (*p)();
//说明一下:
//p先和*结合(括号的运算优先级高),表明该变量名是指针变量
//然后(*p)后面的括号表示该指针变量指向一个函数
//最前面的int 表明这个函数返回值类型为Int
//这个变量p是专门用来存放整型函数的入口地址的
  • 函数指针变量定义时,并不指向哪一个具体的函数,而是指向空指针。

那如何通过函数指针变量调用函数呢?

那就是让函数指针变量p指向一个具体的函数,即将某个函数的入口地址赋值给这个指针变量。

那一般函数的入口地址在哪里呢?

函数名就代表函数的入口地址,是函数指针类型的符号常量。(这里和数组有点类似,数组名也是数组的地址。)

//ok 比如现在有个函数,函数名叫abc,就可以对函数指针变量p赋值了
p = abc;

那赋值给函数指针变量p以后,如何使用p来调用这个函数呢?

还记得我们的取值运算符*吗?
本来调用函数应该是这样的
函数名();
那现在,函数名的位置用*函数指针变量名来代替就可以啦
剩下的就和普通函数的使用方式基本一样啦。

举个例子

这是linux多线程编程中,创建线程的函数,这里值看他的倒数第二个参数,根据上面的知识点返回值是指针的函数指向函数的指针,来分析一下这个参数要传入的是什么?

#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
				  void *(*start_routine)(void *), void *arg);
//倒数第二个参数
//void * (函数名)(void*) 这个函数返回值是一个指针,函数的参数是void *
//void (*指针名)(void*) 这是一个指针,这个指针指向一个函数,函数的返回值为void,参数为void*
//void *(*指针名)(void*) 
//这是一个指针,这个指针指向一个函数,函数的返回值为void*,函数的参数为void
//所以,对于这个参数,我们要先定义一个函数,这个函数的返回值是void*
//这个函数的参数是void*。然后,将这个函数的函数名传入进来。

四、指针和结构体

4.1 结构体变量成员的引用

typedef struct student{
    char name[21];
    int age;
    char sex;
    float score;
};

student st1;
//现在给第一个学生的年龄赋值
st1.age = 20;
//普通结构体引用成员的时候使用.
//.前面是定义的结构体变量,.后面是要引用的结构体中的成员

4.2 指针与结构体

结构体类型是一种构造类型,与简单类型(int、float和char等)一样,也可以定义为指针变量,定义方法也相同。

typedef struct student{
    char name[21];
    int age;
    char sex;
    float score;
}student,*stu;
//这里解释一下*stu,就是student类型的指针变量,即这个指针指向一个student结构体
stu q = null;
//这里定义的这个p直接就是一个指针类型的变量,因为stu在typedef语句中就背定义为指针了

student s;
student *p = s;
q = p;
//下面有这几种方法可以访问student的成员age(别的成员访问方式类似)
s.age = 10;
(*p).age = 10;
p->age = 10;
q->age = 10;

**注意:**如果用结构体指针在不加取值运算符*的情况下想直接访问结构体成员要用到->

  • 44
    点赞
  • 203
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
计算机二级C语言知识点主要包括语法基础、数据类型、运算符、控制语句、函数、数组、字符串、结构体、指针等内容。 首先是语法基础,包括标识符的命名规则、注释的使用方法、变量的定义和声明等。掌握这些基础知识是理解后续知识的基础。 其次是数据类型,C语言中主要包括基本数据类型(如int、float、char等)和派生数据类型(如数组、结构体等)。对各种数据类型的特点、表示范围以及存储方式的了解,是编写程序的基础。 运算符包括算术运算符、关系运算符、逻辑运算符、位运算符等。掌握运算符的优先级和结合性,能够正确理解和使用运算符,编写出更加高效、准确的程序。 控制语句主要包括条件语句和循环语句。条件语句使用if-else语句和switch语句进行条件判断和多分支选择。循环语句包括for循环、while循环和do-while循环,用于重复执行一段代码。了解这些控制语句的用法和特点,能够正确地控制程序的执行流程。 函数是C语言的重要组成部分,能够提高代码的可重用性和可读性。学习函数的定义、调用和参数传递方式,能够编写出结构清晰、模块化的程序。 数组是一种能够存储多个相同类型数据的容器,了解数组的定义、使用和特点,能够处理大量数据,并进行各种操作。 字符串是一组字符的集合,掌握字符串的定义、初始化和操作函数的使用,能够处理字符串相关的操作,如拼接、比较等。 结构体是一种自定义的数据类型,可以包含多个不同类型的数据成员,了解结构体的定义和使用方式,能够处理更加复杂的数据结构。 指针C语言的重要概念,通过指针可以直接访问内存地址,了解指针的定义、初始化和使用方式,能够灵活地处理内存中的数据。 综上所述,掌握二级C语言知识点,对于理解计算机程序的执行原理、提高编程能力具有重要意义。通过实践和不断的积累,能够有效地应用这些知识点编写出高质量的C语言程序。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

辛伯达岛

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值