前言

C语言有一个最重要的知识,是很多小白容易卡住并放弃的一个知识,并且是衡量一个C/C++程序员是否优秀的指标,那就是指针。
指针是C语言的核心,大家需要好好的学习。
本章内容是带领大家走进指针这个类型,希望大家学习后有很好的收获。

一、什么是指针

在学习C语言前大家都知道一个概念,就是低级语言和高级语言的区别,而C语言是属于高级语言,但又有低级语言的特点,那就是可以直接操作计算机底层,那C语言怎么可以操作底层的数据呢?就是靠C语言中的指针类型来进行操作。
而我们说的指针其实是属于C语言中的一个类型,和之前学习的类型一样,但只不过指针比较特殊,它是用来存放地址的一个类型。
比如前面学习的类型

int 变量;
float 变量;
double 变量;
char 变量;

int 就是存放整型的数据, floatdouble 存放浮点型的数据,char 存放的就是字符数据,每一个数据都能存放特定的内容。
指针类型也能存放一个具体的类型,那就是 地址
通过操作地址来实行对底层的操作。

二、一级指针的使用

在学习一个东西时,最重要的是知道如何使用和创建,在使用中学习,所以先对指针的创建进行一下了解。

1.一级指针的创建

在上面说过,指针是属于一种特殊的数据类型,所以创建的时候和数据类型的创建一样,特殊点就在于指针也是分类型的,有整型指针、浮点数指针、字符型指针等等==(还有一些高级指针操作,等后面高级指针再说)==
也就是说现在的数据类型都可以创建为指针类型。
比如说最常见的 int 型的数据,它的指针的创建就是

int*  变量名;

float 类型的指针

float* 变量名;

而上面的 int*float* 就是指针类型,分别表示整型指针和浮点数指针。
通过上面的创建可以推出以下的结论:

指针的创建:类型* 变量名;
C语言中的所有数据类型都有相应的指针类型
在创建指针类型时记得加上 *

2.指针的赋值

现在学会了创建指针类型,那如何对指针进行赋值就是这一小节需要学习的任务。
在前面对变量进行赋值时,使用的是下面的语句

类型 变量名 = 数值;

通过上面的语句可以对基础类型进行赋值,那对指针类型也采用上面的方法进行赋值可以吗?
可以想想,指针存储的是地址,该怎么给它地址,换句话来说,你怎么知道一个值的地址来给指针,显然没办法使用上面的方法进行赋值,因为上面给变量的都是具体类型的值,而给指针是需要地址,而不是 1234 这样的数。
所以指针的赋值方式是如下的

类型* 变量名 = &变量;

切记一定是变量名,而不是具体的值,如果使用具体的值会报错,下面演示一下。
用一个 int 型的指针来存放一个 int 型的变量

int a = 10;
int* pa = &a;

在电脑上跑一下程序


可以看到编译是没有任何问题的,如果使用具体的值会怎么样。
还是一样的,但我直接 &10 看看会发生什么


看到了吧, & 后必须是一个变量而不是一个数值,这样会报错的。

3.&是什么

可能会有很多人不知道 & 是什么东西,其实这个符号在使用输入语句时用到过,只不过没有细讲,这里说一下 & 到底是干嘛的。
& 其实是取址符,取它后面变量的地址的,比如上面2-1的代码
int* pa = &a;
的意思是将 a 变量的地址取出来然后赋值给指针变量 pa
大家记住即可。

4.一维指针的使用

4.1 变量*变量

在前面数据类型 + * 就变成了指针类型,后面跟着的变量名是用来存储地址的。
所以直接使用 变量 得到的是地址,而 *变量 得到的是地址对应的值。

4.2 输出指针变量内容

先通过指针类型来获得地址处对应的值,代码如下

int a = 10;
int* pa = &a;
printf("%d\n", *pa);

从4.1知道,指针变量名对应的是地址,而*变量名得到的是对应的值,运行一下该代码看看是不是


发现真的输出了对应的值,那如果输出 pa 会变成什么结果呢

发现这个输出的是一个负数,我们看不懂,其实这个地方输出的是它存储的地址,只不过是将十六进制翻译成了10进制。
如果想看它里面存放的地址,可以将转义符 %d 变成 %p ,这样就可以知道里面存放的地址了

4.3 改变存储内容

先看一个例子:交换内容
在没学指针和函数之前,代码是写成这样的

#include <stdio.h>
int main(){
int a = 5, b = 6, temp;
temp = a;
a = b;
b = temp;
printf("%d %d\n", a, b);
return 0;
}

在学了函数后写的代码为:

#include <stdio.h>
void swap(int a, int b){
int temp;
temp = a;
a = b;
b = temp;
printf("%d %d\n", a, b);
}
int main(){
int a = 5, b = 6;
swap(a, b);
return 0;
}

但这个在函数运行结束后就被释放,不是真正的交换。
而现在在学指针,就可以使用指针的方式来写
指针的写法

#include <stdio.h>
void swap(int* a, int* b){
int temp;
temp = *a;
*a = *b;
*b = temp;
}
int main(){
int a = 5, b = 6;
swap(&a, &b);
printf("%d %d\n", a, b);
return 0;
}

使用指针的写法就可以完成数据的真正交换,执行一下程序


可以看到完成了交换,那为什么会这样呢?
用图的方式给大家解释一下,当把地址传入函数后,在函数 swap 中会有下面的形式

a下是存放着地址,b下也是存放地址,然后运行下一个代码 int temp = *a

在内存中创建了一个空间给 temp 用来存放指针a对应的值,也就是5
再进行下一步

将指针a地址对应的空间中的值改变成b地址对应的空间中的值

然后再对b指针对应的值进行一下修改
该函数只是对变量的地址进行操作,函数释放后只是把在函数中创建的指针a和b释放了,但地址还是存在的,所以在交换完后输出该地址中存放的元素就是交换后的元素。

三、野指针

从上面的介绍,大家是不是对指针有很大的喜爱,觉得指针的使用很好,但有没有思考过一个问题,为什么现在的编程语言没有指针这个知识了?
因为指针这个东西是把双刃剑,用得好就万事大吉,但用不好你根本就找不到你的程序到底错哪了。
导致这个原因的很大一个因素就是野指针。

1.野指针的危害

在前面学数据类型的时候,创建一个变量

int a;

如果没有进行赋值,它里面的值不可能永远是不可预知的是一个随机值,大家不信的话可以去调试一下。
那指针类型也是一样的,如果你只创建了指针变量,但没有给它赋值,它会产生一个随机值,指向内存中的未知空间。
而C语言可以直接操作底层,如果指向了一个重要的参数的空间,而你又不清楚直接就直接对其修改会造成程序或则是系统不可逆的错误。

2.野指针的产生

要避免野指针的产生最重要的是知道野指针是如何产生的。
野指针的产生最主要的三点

  • 初始化指针没赋值
  • 指针越界
  • 指针指向的空间释放
    指针没赋值在上面已经说过了,那剩下两条是什么呢

2.1 指针越界

在数组中最容易出现指针越界的情况的,看下面的代码

#include <stdio.h>
int main(){
int arr[10] = 0;
int *p = arr;
for(int i = 0; i <= 11; i++){
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p + i) = 1;
}
return 0;
}

已经超出了数组的范围,然后指向了数组后的空间,这个空间是未知的,使用就造成了野指针

2.2 空间释放

在使用指针指向一个函数中的一个变量,使用完函数,函数就被释放,函数中的变量的空间也会释放,并且地址也会重新分配,那这个时候指向函数中变量的指针也就变成了野指针。

3.避免野指针

这里给大家四条建议

  • 指针初始化
  • 小心指针越界
  • 指针指向空间释放即用null占位
  • 指针使用之前检查有效性

四、指针和数组

学数组的时候知道,数组名就是首元素的地址,在查找数组中的元素时用的是索引 [] 位置,也可以用 + 的移动来找到元素。
很多人说数组就是指针,其实不对的,但你可以理解成指针,更多数组和指针的内容在后面。

五、指针的关系运算符

1.指针大小

每个类型都有每一个类型的大小,比如 char 是一个字节, int 为4个字节,
每个类型的指针也有大小的。

int* pa;//4个字节
float* pa;//4个字节
double* pa;//8个字节
char* pa;//1个字节

2.指针的移动

有了上面的知识我们可以对指针进行移动,移动的方法就是 +移动的长度-移动的长度
例如有一个 int 型的指针需要让它向前移动一位,就可以 +1 ,如果要让它后退一位就 -1
int 型进一位是移动4个字节
char 型进一位是移动1个字节

总结

  • 数据类型* 变量 = &变量
  • *变量 是指针中存放的具体的值,而变量存放的是地址
  • 每个类型移动的字节是不一样的,要注意类型的长度
  • 避免野指针的产生

C语言指针--一级指针的更多相关文章

  1. C语言用一级指针处理字符串的反思

    1.一级指针处理字符串的常见方式 如果使用单个指针,不方便对字符串本身进行操作,只适合遍历. 使用两个指针, 两个指针和字符串大致有两个常见处理方式: (1)两个指针从字符串首部开始向后移动,同时处理 ...

  2. (C++)函数参数传递中的一级指针和二级指针

    主要内容: 1.一级指针和二级指针 2.函数指针传递的例子 3.什么时候需要传递二级指针? 4.二级指针在链表中的使用 1.一级指针和二级指针 一级指针:即我们一般说的指针,就是内存地址: 二级指针: ...

  3. C语言复习:字符串和一级指针

    字符串基本操作 字符数组初始化方法 int main() {     //1 {}号法 初始化列表     //数组初始化有2种方法 默认元素个数.指定元素个数     char buf1[] = { ...

  4. C语言指针系列 - 一级指针.一维数组,二级指针,二维数组,指针数组,数组指针,函数指针,指针函数

    1. 数组名 C语言中的数组名是一个特殊的存在, 从本质上来讲, 数组名是一个地址, 我们可以打印一个指针的值,和打印一个数组的值来观察出这个本质: int nArray[10] ={ 0 }; in ...

  5. C语言一级指针与二级指针

    指针的概念 指针就是地址, 利用这个地址可以找到指定的数据 指针就是地址, 那么在使用的时候, 常常会简单的说 指针变量为指针 指针变量就是存储地址的变量 int *p1;// 申请了一个变量, 即在 ...

  6. 深入理解C语言中的指针与数组之指针篇

    转载于http://blog.csdn.net/hinyunsin/article/details/6662851     前言 其实很早就想要写一篇关于指针和数组的文章,毕竟可以认为这是C语言的根本 ...

  7. 深入理解C语言中的指针与数组之指针篇(转载)

    前言 其实很早就想要写一篇关于指针和数组的文章,毕竟可以认为这是C语言的根本所在.相信,任意一家公司如果想要考察一个人对C语言的理解,指针和数组绝对是必考的一部分. 但是之前一方面之前一直在忙各种事情 ...

  8. 由链表初始化看C语言的二级指针

    先来看C语言创建链表.插入节点和遍历链表的一段代码: #include <stdio.h> #include <stdlib.h> typedef int ElemType; ...

  9. 论C语言中二级指针和二维数组之间的区别

    刚开始学习C语言的时候,觉得一个数组可以定义一个一级指针去访问,想当然的就觉得可以定义一个二级指针去访问二维数组.很显然这是错误的. 我们来看看C语言的数组在内存中的存储方式. 实际上C语言中的数组, ...

  10. C语言二级指针(指向指针的指针)

    转载:http://c.biancheng.net/cpp/html/85.html 指针可以指向一份普通类型的数据,例如 int.double.char 等,也可以指向一份指针类型的数据,例如 in ...

随机推荐

  1. Shell脚本编程(二)

    Shell脚本编程(二) shell脚本编程中if.if else的使用以及一些常用到的操作符 if.if else使用方式:      1) if条件          if [ condition ...

  2. YOLO2论文中文版

    文章目录 YOLO9000中文版 摘要 1. 引言 2. 更好 3. 更快 4. 更强 5. 结论 参考文献 YOLO9000中文版 摘要 我们引入了一个先进的实时目标检测系统YOLO9000,可以检 ...

  3. ToF技术全解读

    文章目录 ToF技术全解读 1. 什么是ToF 2. ToF的原理 3. ToF 优缺点 ToF技术全解读 1. 什么是ToF ToF: Time of flight. 飞行时间.当然这只是一种翻译的 ...

  4. MySQL概述与安装

    MySQL数据库 概要: 一.MySQL数据库的概述 二.MySQL数据库的搭建 三.MySQL数据库软件的使用 四.MySQL数据类型 五.MySQL数据库数据的操作 一.初始MySQL数据库 1. ...

  5. [Pytorch框架] 1.4 Autograd:自动求导

    文章目录 Autograd: 自动求导机制 张量(Tensor) 梯度 Autograd: 自动求导机制 PyTorch 中所有神经网络的核心是 autograd 包. 我们先简单介绍一下这个包,然后 ...

  6. Django笔记三十六之单元测试汇总介绍

    本文首发于公众号:Hunter后端 原文链接:Django笔记三十六之单元测试汇总介绍 Django 的单元测试使用了 Python 的标准库:unittest. 在我们创建的每一个 applicat ...

  7. .NET 6学习笔记(8)生成自签证书

    上一篇我们通过导出IIS Express的自签证书,供ASP.NET Core程序启用HTTPS.本篇我们讨论如何生成自签证书.自签证书的生成,有多种方式.比如OpenSSL或PowerShell都可 ...

  8. 2021-01-30:redis中,Pipeline有什么好处?

    福哥答案2021-01-30:可以将多次 IO 往返的时间缩减为一次,减少多次IO延迟的开销.前提是 pipeline 执行的指令之间没有因果相关性. 多个指令之间没有依赖关系,可以使用 pipeli ...

  9. MySQL中字符串查询效率大比拼

    背景 最近有个同事对字符串加索引,加完后,发现多了个奇奇怪怪的数字执行的SQL如下: alter table string_index_test add index `idx_name` (`name ...

  10. GPT大模型下,如何实现网络自主防御

    GPT大模型下,如何实现网络自主防御 本期解读专家  李智华 华为安全AI算法专家    近年来,随着GPT大模型的出现,安全领域的攻防对抗变得更加激烈.RSAC2023人工智能安全议题重点探讨了人工 ...