首页 > 其他分享 >深入浅出C语言指针(基础篇)

深入浅出C语言指针(基础篇)

时间:2024-07-21 23:54:08浏览次数:17  
标签:变量 指向 int 深入浅出 C语言 地址 ptr 指针

目录

引言

一、认识指针

指针是什么?

 二、指针变量和地址

1.取地址操作符

2.指针变量

3.解引用操作符 

4.指针变量的大小 

三、指针和指针类型

1.指针的类型

2.指针+-整数

3.指针的解引用

四、const修饰指针变量

 1.const 修饰指向的数据

2.const 修饰指针本身

3.const 同时修饰指针和指向的数据

五、野指针 

1.野指针概念

2.野指针成因

3.如何规避野指针

六、指针运算

1.指针+- 整数

2.指针-指针

3.指针的关系运算


引言

在 C 语言中,指针是理解内存管理和数据操作的核心概念。它就像一把钥匙,帮助我们打开数据存储的宝库,实现对数据的灵活访问和操作。本文将带你深入了解指针的奥秘,掌握指针的类型、运算和与数组的关系,并学习如何避免野指针导致的程序错误。

一、认识指针

指针是什么?

 在 C 语言中,指针 是一个非常重要的概念,它就像一把钥匙,可以帮助你访问内存中的数据,并进行灵活的操作。简单来说,指针就是地址,它指向内存中某个变量的存储位置。

 二、指针变量和地址

在C语⾔中创建变量其实就是向内存申请空间,⽐如:
#include<stdio.h>
int main()
{
	int a = 10;
	return 0;
}

上述的代码就是创建了整型变量a,内存中申请4个字节,⽤于存放整数10,其中每个字节都有地址,上图中4个字节的地址分别是: 

0x008FFC54  
0x008FFC55  
0x008FFC56  
0x008FFC57  

1.取地址操作符

那我们如何能得到a的地址呢? 这⾥就得学习⼀个操作符' & '-- 取地址操作符

 我们可以知道:&a取出的是a所占4个字节中地址较⼩的字节的地址。

2.指针变量

那我们通过取地址操作符'&'拿到的地址是⼀个数值,⽐如:0x00AF9C,这个数值有时候也是需要存储起来,⽅便后期再使⽤的,那我们把这样的地址值存放在哪⾥呢?答案是:指针变量中。

指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址。

#include<stdio.h>
int main()
{
	int a = 10;
	int* p = &a;//取出a的地址并存储到指针变量p中
	return 0;
}
//p是一个变量(指针变量),是一块空间,p左边写的是 int* 
//*是在说明p是指针变量,
//int 是在说明p指向的是整型(int)类型的对象

指针理解的2个要点:
1. 指针是内存中一个最小单元的编号,也就是地址
2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
总结:指针就是地址,口语中说的指针通常指的是指针变量。 

3.解引用操作符 

我们将地址保存起来,未来是要使⽤的,那怎么使⽤呢? 在现实⽣活中,我们使⽤地址要找到⼀个房间,在房间⾥可以拿去或者存放物品。 C语⾔中其实也是⼀样的,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针) 指向的对象,这⾥必须学习⼀个操作符叫解引⽤操作符' * '(也叫间接访问操作符)。 上⾯代码中第7⾏就使⽤了解引⽤操作符, *p 的意思就是通过p中存放的地址,找到指向的空间, *p其实就是a变量了;所以*p= 1,这个操作符是把a改成了1。 这⾥如果⽬的就是把a改成0的话,写成 a = 0; 不就完了, 为啥⾮要使⽤指针呢? 其实这⾥是把a的修改交给了pa来操作,这样对a的修改,就多了⼀种的途径,写代码就会更加灵活,后期慢慢就能理解了。

4.指针变量的大小 

指针变量的大小取决于机器架构 32位平台下地址是32个bit位,指针变量⼤⼩是4个字节 64位平台下地址是64个bit位,指针变量⼤⼩是8个字节 注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。

三、指针和指针类型

1.指针的类型

这里我们在讨论一下:指针的类型 我们都知道,变量有不同的类型,整形,浮点型等。那指针有没有类型呢? 当有这样的代码:
int a= 10; 
p = &a;

要将&a(a的地址)保存到p中,我们知道p就是一个指针变量,那它的类型是怎样的呢?

我们给指针变量相应的类型:

char  *pc = NULL;
int   *pi = NULL;
short *ps = NULL;
long  *pl = NULL;
float *pf = NULL;
double*pd = NULL;

这里可以看到,指针的定义方式是: type + *

其实: char* 类型的指针是为了存放 char 类型变量的地址。 short* 类型的指针是为了存放 short 类型变量的地址。 int* 类型的指针是为了存放 int 类型变量的地址。

2.指针+-整数

#include <stdio.h>
int main()
{
	int n = 10;
	char* pc = (char*)&n;
	int* pi = &n;
	printf("%p\n", &n);
	printf("%p\n", pc);
	printf("%p\n", pc + 1);
	printf("%p\n", pi);
	printf("%p\n", pi + 1);
	return 0;
}

运行结果如下:

我们可以看出, char* 类型的指针变量+1跳过1个字节, int* 类型的指针变量+1跳过了4个字节。 这就是指针变量的类型差异带来的变化

总结:指针的类型决定了指针向前或者向后走一步有多大(距离)。

3.指针的解引用

我们可以看到: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。

总结:指针的类型决定了对指针解引用的时候有多大的权限(能操作几个字节)。

四、const修饰指针变量

 1.const 修饰指向的数据

声明形式:const 类型 *指针名;
这意味着指针指向的数据是常量,即不能通过指针来修改数据的内容,但指针本身可以改变,指向其他的数据。
示例:

//常量指针(Pointer to Constant):指针指向的是常量,指针本身可以改变。
int a = 10;
const int *ptr = &a; // ptr 是指向 int 类型的常量指针
*ptr = 20; // 错误,不能通过 ptr 修改 a 的值
ptr = &a; // 正确,可以改变 ptr 指向的地址

2.const 修饰指针本身

声明形式:类型 *const 指针名;
这意味着指针本身是一个常量,一旦被初始化,它的值(即指向的地址)就不能被改变。但是,可以通过指针来修改它所指向的数据。
示例:

//指针常量(Constant Pointer):指针本身是常量,指针指向的数据可以改变。
int a = 10;
int *const ptr = &a; // ptr 是一个指向 int 类型的常量指针
*ptr = 20; // 正确,可以通过 ptr 修改 a 的值
ptr = &a; // 错误,不能改变 ptr 的值

3.const 同时修饰指针和指向的数据

声明形式:const 类型 *const 指针名;
这意味着指针本身是一个常量,且它指向的数据也是常量。因此,既不能改变指针的值,也不能通过指针修改它所指向的数据。
示例:

nt a = 10;
const int *const ptr = &a; // ptr 是一个指向 int 类型的常量指针,且它指向的数据也是常量
*ptr = 20; // 错误,不能通过 ptr 修改 a 的值
ptr = &a; // 错误,不能改变 ptr 的值

五、野指针 

1.野指针概念

野指针(Wild Pointer)是指在程序中没有进行正确初始化的指针。野指针指向的内存地址是不确定的,因此它可能指向任何地方,包括有效的内存地址、非法的内存地址,甚至可能指向操作系统或其他程序正在使用的内存。由于野指针的不确定性,使用它们可能会导致不可预测的行为,包括程序崩溃和数据损坏。

2.野指针成因

①未初始化的指针:在声明指针后,如果没有对其进行初始化,它将是一个野指针。

int *p; // p 是一个野指针

②指针释放后未置空:在释放(例如使用 free() 或 delete)指针指向的内存后,如果没有将指针置为 NULL,它将成为野指针。

int *p = malloc(sizeof(int));
free(p); // 此时应该将 p 置为 NULL,否则 p 变成野指针

③指针超出作用域:在函数中返回局部变量的地址,当函数返回后,局部变量被销毁,原来的地址变成野指针。

int* getPtr() {
    int x = 10;
    return &x; // 返回局部变量的地址,这是一个野指针
}

3.如何规避野指针

初始化指针:在声明指针后立即对其进行初始化,可以是具体的地址,也可以是 NULL。

int *p = NULL; // 初始化为 NULL

释放后置空:在使用 free() 或 delete 释放指针指向的内存后,立即将指针赋值为 NULL。

int *p = malloc(sizeof(int));
free(p);
p = NULL; // 置空指针

避免返回局部变量的地址:不要从函数中返回局部变量的地址,如果需要返回地址,应该使用动态分配的内存或全局变量。

int* getPtr() {
    int *p = malloc(sizeof(int));
    *p = 10;
    return p; // 返回动态分配内存的地址
}

作用域检查:确保指针的使用不会超出其作用域。

六、指针运算

1.指针+- 整数

因为数组在内存中是连续存放的,只要知道第⼀个元素的地址,顺藤摸⽠就能找到后⾯的所有元素。

#include <stdio.h>
//指针+- 整数
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = &arr[0];
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));//p+i 这⾥就是指针+整数
	}
	return 0;
}

2.指针-指针

指针-指针可以得到两个指针之间的元素个数。

#include <stdio.h>
int my_strlen(char* s)
{
	char* p = s;
	while (*p != '\0')
		p++;
	return p - s;
}
int main()
{
	char arr[] = "abcdefg";
	printf("%d\n", my_strlen(arr));
	return 0;
}

3.指针的关系运算

指针和指针比较大小即地址和地址比较大小。

#include <stdio.h>
int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = &arr[0];
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	while (p < arr + sz) //指针的⼤⼩⽐较
	{
		printf("%d ", *p);
		p++;
	}
	return 0;
} 

标签:变量,指向,int,深入浅出,C语言,地址,ptr,指针
From: https://blog.csdn.net/2302_81410974/article/details/140593381

相关文章

  • 学习C语言的第一二天
    今天学了C语言的核心语法,对它们有了初步的了解。1.首先是注释,它分为两类,一个是单行注释一个是多行注释。格式的话单行注释就是在所写的注释前加上//,多行注释的话就是用/和/把所需的多行内容框起来。当然了它也有快捷键ctrlk+ctrlc是我们的注释快捷键,而ctrlk+ctrlu是我们的注......
  • C语言-选择结构
    在C语言中,一共有三种程序结构:顺序结构、选择结构(分支结构)和循环结构。C语言提供2种类型的选择语句:if语句和switch语句。C语言由一个分号;隔开的就是一条语句。在C语言中0表示假,非0表示真。if语句可以是任何语句,也可以是有若干语句组成的一个语句组,在这种情况下,这组语句需要......
  • c++ 引用和指针
      c++引用和指针在C++中,引用和指针是两个非常重要的概念,它们有一些相似之处也有一些不同之处。相似之处:引用和指针都可以指向一个对象。引用一旦被初始化指向一个对象后,就不能再指向其他对象,而指针可以在任何时候指向其他对象。不同之处:引用在......
  • C语言:键盘录入案例
    主要使用了scanf;scanf的使用方法和注意事项:1.作用:用于接收键盘输入的数据并赋值给对应的变量2.使用方式;scanf("占位符",&变量名);3.注意事项;占位符后面的的变量要对应第一个参数中不写换行案例1:键盘录入求和#include<stdio.h>intmain(){ inta;//......
  • 【c语言】数组
    一:数组的概念数组是⼀组相同类型元素的集合;•数组中存放的是1个或者多个数据,但是数组元素个数不能为0。•数组中存放的多个数据,类型是相同的。二:⼀维数组的创建和初始化1.数组创建存放在数组的值被称为数组的元素,数组在创建的时候可以指定数组的⼤⼩和数组的元素类型。......
  • C语言数据类型和变量
    1.数据类型介绍C语言提供了丰富的数据类型来描述生活中的各种数据。所谓“类型”,就是相似的数据所拥有的共同特征,编译器只有知道了数据的类型,才知道怎么操作数据。1.1数据类型下面盘点一下C语言提供的各种数据类型,本章节主要探讨内置数据类型:具体解释:1.2各种数据类型......
  • GCC在C语言中内嵌汇编 asm __volatile__
    from: https://www.cnblogs.com/hiveme/p/8194832.html在内嵌汇编中,可以将C语言表达式指定为汇编指令的操作数,而且不用去管如何将C语言表达式的值读入哪个寄存器,以及如何将计算结果写回C变量,你只要告诉程序中C语言表达式与汇编指令操作数之间的对应关系即可,GCC会自动插入代码......
  • 俊昭c语言笔记
    c语言——指针c语言——指针-CSDN博客c语言——函数格式和语句c语言——函数格式和语句_c语言副函数格式-CSDN博客c语言——break和continuec语言——break和continue_break的作用c语言-CSDN博客c语言——常用的输出函数c语言——常用的输出函数-CSDN博客c语言——运算符c......
  • 深入浅出WebRTC—DelayBasedBwe
    WebRTC中的带宽估计是其拥塞控制机制的核心组成部分,基于延迟的带宽估计是其中的一种策略,它主要基于延迟变化推断出可用的网络带宽。1.总体架构1.1.静态结构1)DelayBasedBwe受GoogCcNetworkController控制,接收其输入并返回带宽估计值。2)DelayBasedBwe内部使用InterAr......
  • IO多路复用-select的使用详解【C语言】
    1.多进程/线程并发和IO多路复用的对比IO多路转接也称为IO多路复用,它是一种网络通信的手段(机制),通过这种方式可以同时监测多个文件描述符并且这个过程是阻塞的,一旦检测到有文件描述符就绪(可以读数据或者可以写数据)程序的阻塞就会被解除,之后就可以基于这些(一个或多个)就绪的文件......