C语言中指针的说明

什么是指针?

在C语言当中,我们可以将指针理解为内存当中存储的地址,就像生活当中,一个小区里面,在小区里面有很单元,每一栋单元,单元内的房间有着不同的房间号,我们可以同过几栋几单元去寻找我们想要找到的房间,同样的,我们在C语言当中,指针就是我们寻找想要的房间的一种手段。

指针变量和地址

上面那一段话,已经让我们初步理解了指针

我们可以看一下这一段代码

其中 0x0115F95C 0x0115F95D 0x0115F95E 0x0115F95F这4个字节代表着a=10所代表的地址,并且每一个字节都是代表着地址

下面我们将通过&(取地址操作符来得到a的地址)

这边显示的结果是同上面通过内存调试出来的结果是相同的

这里我们通过&符号将a的地址存储到指针变量p 并且类型为int*的指针

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

上面那张图我们可以看到 int*p=&a;

那么*p=&a有什么用处呢?

#include<stdio.h>
int main()
 {
     int a = 100;
     int* pa = &a;
     *pa = 0;
     return 0;
 }

上面那一段代码我们通过*pa来改变了a的值,结果如下图

有同学肯定在想,这⾥如果⽬的就是把a改成0的话,写成 a = 0; 不就完了,为啥⾮要使⽤指针呢? 其实这⾥是把a的修改交给了pa来操作,这样对a的修改,就多了⼀种的途径,写代码就会更加灵活, 后期慢慢就能理解了。

指针变量的大小(在VS2022中)

分别为x86以及x64环境下面

我们可以看到指针变量的大小在统一环境下面他们的大小是相同的

结论:

• 32位平台下地址是32个bit位,指针变量⼤⼩是4个字节

• 64位平台下地址是64个bit位,指针变量⼤⼩是8个字节 X64环境输出结果

• 注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。

指针变量的意义以及和地址的关系

那么指针变量大小有什么意义呢?

我们看下面两段代码

 //代码1 
#include <stdio.h>
 int main()
 {
     int n = 0x11223344;
     int *pi = &n; 
    *pi = 0;   
    return 0;
 }

 //代码2 
#include <stdio.h>
 int main()
 {
     int n = 0x11223344;
     char *pc = (char *)&n;
     *pc = 0;
     return 0;
 }

这两个代码的结果是代码1会将n的4个字节全部改为0,但是代码2只是将n的第⼀个字节改为0。

结论:指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)。 ⽐如: char* 的指针解引⽤就只能访问⼀个字节,⽽ int* 的指针的解引⽤就能访问四个字节。

指针变量的地址变化

 #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个字节。

这就是指针变量的类型差异带来的变化。

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

指针运算 指针的基本运算有三种:

• 指针+-整数

• 指针-指针

• 指针的关系运算

野指针

概念:野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

导致野指针的原因:1. 指针未初始化 2. 指针越界访问 3. 指针指向的空间释放

避免野指针的方面:1.指针初始化 2.⼩⼼指针越界 3.指针变量不再使⽤时,及时置NULL,指针使⽤之前检查有效性 4.避免返回局部变量的地址

assert断言包含在assert.h 头⽂件定义了宏 assert() ,⽤于在运⾏时确保程序符合指定条件,如果不符合,就报错终⽌运⾏。这个宏常常被称为"断⾔"。

assert(p != NULL);

用来 验证变量p是否等于NULL 。如果确实不等于 NULL ,程序继续运⾏,否则就会终⽌运⾏,并且给出报错信息提⽰。

传值调用和传址调用

传值调用

 #include <stdio.h>
 void Swap1(int x, int y)
 {
     int tmp = x;
     x = y;
     y = tmp;
 }
 int main()
 {
     int a = 0;
     int b = 0;
     scanf("%d %d", &a, &b);
     Swap1(a, b);
     printf("交换前: a=%d b=%d\n", a, b);
     printf(" 交换后:a=%d b=%d\n", a, b);
     return 0;
 }

结论:实参传递给形参的时候,形参会单独创建⼀份临时空间来接收实参,对形参的修改不影响实 参。 所以Swap1是失败的了。

传址调用

#include<stdio.h>
void Swap2(int*px, int*py)
 {
     int tmp = 0;
     tmp = *px;
     *px = *py;
     *py = tmp;
 }
int main
 {
     int a = 0;
     int b = 0;
     scanf("%d %d", &a, &b);
     printf("交换前:a=%d b=%d\n", a, b);
     Swap2(&a, &b);
     printf("交换后:a=%d b=%d\n", a, b);
     return 0;
 }

我们可以看到实现成Swap2的⽅式,顺利完成了任务,这⾥调⽤Swap2函数的时候是将变量的地址传 递给了函数,这种函数调用方式叫:传址调用。

传址调⽤,可以让函数和主调函数之间建⽴真正的联系,在函数内部可以修改主调函数中的变量;所 以未来函数中只是需要主调函数中的变量值来实现计算,就可以采⽤传值调⽤。如果函数内部要修改 主调函数中的变量的值,就需要传址调用。

相关推荐
XiaoLeisj2 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
励志成为嵌入式工程师3 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉3 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer3 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq3 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
记录成长java5 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
前端青山5 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
hikktn5 小时前
如何在 Rust 中实现内存安全:与 C/C++ 的对比分析
c语言·安全·rust
睡觉谁叫~~~5 小时前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust
音徽编程5 小时前
Rust异步运行时框架tokio保姆级教程
开发语言·网络·rust