C语言指针从入门到基础详解(非常详细)

1.内存和地址

我们知道电脑中的CPU在处理数据的时候需要在内存中读取数据处理后的数据也会放在内存中。把内存划分为一个个的内存单元每个单元的大小是一个字节。每个字节都有它对应的编号也就是它的地址,以便CPU可以快速的找到一个内存空间。C语言中我们把地址叫做指针,所以内存单元的编号==地址==指针。

2.指针变量和地址

取地址操作符(&):

C语言中,创建变量就是在向内存申请空间,下例在创建a变量的过程中就是向内存申请了四个字节的空间,那么这四个字节都有地址,取地址操作符&就可以把a的地址(四个地址的较小的一个)取出来,这样就可以知道其他三个地址了。

int a=10;
printf("%p\n",&a);

指针变量:

我们把a的地址用&取出来了,那么我们把&a放在哪呢?放在指针变量中。指针变量是一种用来存放地址的变量,放在指针变量中的值都会理解成地址。

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

如何理解指针类型:

pa的类型是int*,*是说明pa是一个指针变量,int说明pa指向的是一个int类型的对象。那么如果对象是char类型的,对应的指针变量的类型就是char*。

解引用操作符(*):

有了一个元素的地址,我们要使用他的时候就要用到解引用操作符*

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

这里pa是一个地址,使用*pa系统就会寻找并读取这个地址中的数据。

指针变量的大小:

根据计算机的相关知识,我们可以知道32位机器上有32根地址总线,每根地址线只有两种输出0/1(低电平和高电平)那么一个地址就是32个bit位就是四个字节,所以在32环境下指针变量的大小是4个字节,同理64位环境下就是8个字节。

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

3.指针变量类型的意义

指针的解引用:

int*类型指针和char*类型指针的可操作空间不一样,我们可以观察一下下面的两段代码。

int main()
{
    int n = 0x11223344;
    int *pi = &n;
    *pi = 0;
    return 0;
}
/************************/
int main()
{
    int n = 0x11223344;
    char *pc = (char *)&n;
    *pc = 0;
    return 0;
}

第一段代码会将n的四个字节全部改为0,第二段代码只是将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个字节。
这就是指针变量的类型差异带来的变化。
结论:指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。

 void* 指针:

在指针类型中有⼀种特殊的类型是 void* 类型的,可以理解为⽆具体类型的指针(或者叫泛型指
针),这种类型的指针可以⽤来接受任意类型地址。但是也有局限性, void* 类型的指针不能直接进⾏指针的+-整数和解引⽤的运算。
⼀般 void* 类型的指针是使⽤在函数参数的部分,⽤来接收不同类型数据的地址,这样的设计可以
实现泛型编程的效果。使得⼀个函数来处理多种类型的数据

 4.const修饰指针

const修饰变量:

被const修饰的变量不能被修改。

#include <stdio.h>
int main()
{
    int m = 0;
    m = 20;//m是可以修改的
    const int n = 0;
    n = 20;//n是不能被修改的
    return 0;
}

但是如果我们绕过n本身,用n的地址就能够修改n,这样做就在打破语法规则。

#include <stdio.h>
int main()
{
    const int n = 0;
    printf("n = %d\n", n);
    int*p = &n;
    *p = 20;
    printf("n = %d\n", n);
    return 0;
}

这本身是不合理的所以我们应该让n的地址也不能修该n。

const修饰指针变量:

根据下面的代码我们可以得到结论。

#include <stdio.h>

void test1()
{
    int n = 10;
    int m = 20;
    int *p = &n;
    *p = 20;//ok?
    p = &m; //ok?
}
void test2()
{
    int n = 10;
    int m = 20;
    const int* p = &n;
    *p = 20;//ok?
    p = &m; //ok?
}
void test3()
{
    int n = 10;
    int m = 20;
    int *const p = &n;
    *p = 20; //ok?
}
void test4()
{
    int n = 10;
    int m = 20;
    int const * const p = &n;
    *p = 20; //ok?
    p = &m; //ok?
}
int main()
{
    //测试⽆const修饰的情况
    test1();
    //测试const放在*的左边情况
    test2();
    //测试const放在*的右边情况
    test3();
    //测试*的左右两边都有const
    test4();
return 0;

结论:const修饰指针变量的时候
• const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。
但是指针变量本⾝的内容可变。
• const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指
向的内容,可以通过指针改变。

5. 指针运算

指针+-整数:

我们这里直接以例子来说明

#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;
}

指针-指针:

指针-指针的结果是两个指针之间的元素个数。可以借这个属性来模拟实现strlen:

size_t my_strlen(char* s)//指针-指针
{
	char* tmp = s;
	while (*(++s))
		;
	return s - tmp;
}

模拟实现C语言库函数(strlen,strcpy,strcat)-CSDN博客三种方法。https://blog.csdn.net/2301_80194476/article/details/136588134?spm=1001.2014.3001.5502

指针的关系计算:

直接上例子:

#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;
}

6.野指针

野指针成因:

未初始化的指针

#include <stdio.h>
int main()
{
    int *p;//局部变量指针未初始化,默认为随机值
    *p = 20;
    return 0;
}

指针越界访问

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

指针指向的空间释放

#include <stdio.h>
int* test()
{
    int n = 100;
    return &n;
}
int main()
{
    int*p = test();
    printf("%d\n", *p);
    return 0;
}

如何避免野指针

指针初始化

如果明确知道指针指向哪⾥就直接赋值地址,如果不知道指针应该指向哪⾥,可以给指针赋值NULL。NULL 是C语⾔中定义的⼀个标识符常量,值是0,也是地址,这个地址是⽆法使⽤的,读写该地址会报错。

#include <stdio.h>
int main()//初始化
{
    int num = 10;
    int*p1 = &num;
    int*p2 = NULL;
    return 0;
}

小心指针越界

指针变量不再使⽤时,及时置NULL,指针使⽤之前检查有效性 

 避免返回局部变量的地址

7.assert断言

C语言指针部分易错-CSDN博客文章浏览阅读824次,点赞22次,收藏24次。关于assert宏,它是一个断言,在写代码的过程中,如果要使用assert,就要包括头文件。否则就终止程序,这在代码的调试中很常用,我们通过定义NDEBUG宏来进行assert的禁用。sizeof(i)的值为4,而i为-1,此时如果以为选择B就万事大吉了的话,那也太小看此题了。B:int (*ptr)[10]这是一个数组指针,代码的意思是将整个的数组地址(&arr)放进这个数组指针里面,没有问题。D:前面说了&arr表示整个数组的地址,把整个数组的地址放进一个指针里面是不可行的。https://blog.csdn.net/2301_80194476/article/details/136235453?spm=1001.2014.3001.5502

 8.传值调用和传址调用

学习指针的⽬的是使⽤指针解决问题,那什么问题,⾮指针不可呢?
题目:写一个函数,交换两个整型变量的值。

#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);
    printf("交换前:a=%d b=%d\n", a, b);
    Swap1(a, b);
    printf("交换后:a=%d b=%d\n", a, b);
    return 0;
}

但是这样写的结果是错误的,其实a和b并没有交换,这里只将a,b的值传给了x,y是传值调用。

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

想要真正交换,就要进行传址调用。

#include <stdio.h>
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);
    Swap1(&a, &b);
    printf("交换后:a=%d b=%d\n", a, b);
    return 0;
}

传址调⽤,可以让函数和主调函数之间建⽴真正的联系,在函数内部可以修改主调函数中的变量;所以未来函数中只是需要主调函数中的变量值来实现计算,就可以采⽤传值调⽤。如果函数内部要修改主调函数中的变量的值,就需要传址调⽤。
本期博客到这里就结束了,如果有什么错误,欢迎指出,如果对你有帮助,请点个赞,谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/442976.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

推房子游戏c++

这段代码是一个推箱子游戏的实现。游戏中有一个地图&#xff0c;地图上有墙壁、人、箱子和目标位置。玩家通过键盘输入WASD或方向键来控制人物的移动&#xff0c;目标是将所有的箱子推到相应的目标位置上。 代码中的dt数组表示地图&#xff0c;每个位置上的字符表示对应的元素…

星闪联盟和宝尊助力平台合作战略伙伴,为老百姓接力,传递这份大爱

星闪联盟和宝尊助力平台合作战略伙伴,为老百姓接力,传递这份大爱传承下去。 星闪联盟是致力于全球化的产业联盟,目标是推动新一代无线短距通信技术SparkLink的创新和产业生态,承载智能汽车、智能家居、智能终端和智能制造等快速发展的新场景应用,满足极致性能需求。2020年9月2…

基于Java的社区买菜系统(Vue.js+SpringBoot)

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、系统设计2.1 功能模块设计2.1.1 数据中心模块2.1.2 菜品分类模块2.1.3 菜品档案模块2.1.4 菜品订单模块2.1.5 菜品收藏模块2.1.6 收货地址模块 2.2 可行性分析2.3 用例分析2.4 实体类设计2.4.1 菜品分类模块2.4.2 菜品档案模块2.4.3…

20240308-2-校招前端面试常见问题-网络及浏览器

校招前端面试常见问题【4】——网络及浏览器 1、网络相关 Q&#xff1a;请简述一下 HTTP 协议&#xff0c;以及 HTTP1.0/1.1/2.0/3.0 的区别&#xff1f; HTTP 协议&#xff1a;超文本传输协议&#xff0c;使用 TCP/IP 协议传输数据。是一个应用层的协议。 HTTP1.0&#xff…

三甲医院体检科PEIS系统全套源码,可实现健康体检、职业病体检、从业人员体检、妇女儿童体检、外检服务等全方位体检服务

大型体检中心、三甲医院体检科的PEIS系统源码&#xff0c;体检信息系统源码 一套应用于大型体检中心、三甲医院体检科的PEIS系统源码&#xff0c;C#语言开发实现全流程管理。系统从检前&#xff0c;检中&#xff0c;检后整个业务流程提供标准化以及精细化的解决方案。 实现体检…

NTFS安全权限

NTFS是新技术文件系统&#xff08;New Technology File System&#xff09;的缩写&#xff0c;是一种用于Windows操作系统的文件系统。NTFS提供了高级的功能和性能&#xff0c;包括文件和目录的权限控制、加密、压缩以及日志等。它被广泛应用于Windows NT、Windows 2000、Windo…

算法的时间复杂度和空间复杂度(数据结构)

本博客讲解算法的时间复杂度和空间复杂度的来源及定义&#xff0c;时间复杂度的表示及练习。空间复杂度的计算会在后续博客讲解 算法的复杂度 算法在编写成可执行程序后&#xff0c;运行时需要耗费时间资源和空间(内存)资源 。因此衡量一个算法的好坏&#xff0c;一般是从时…

比特币普通地址、隔离见证(兼容)、隔离见证(原生)、Taproot 地址傻傻分不清楚

我们在使用比特币钱包的时候&#xff0c;可以看到各种地址类型&#xff1a;普通地址、隔离见证&#xff08;兼容&#xff09;、隔离见证&#xff08;原生&#xff09;、Taproot 地址。 看得我们一脸懵逼&#xff0c;为什么会有这么多种类型的地址&#xff1f; 它们之间都有什么…

Jenkins集成SonarQube

文章目录 SonarQube端开启权限验证生成Jenkins登录的token Jenkins端安装SonarQube Scanner插件配置SonarQube凭证配置Jenkins的Sonar Qube信息配置SonarQube Scanner 配置项目的SonarScannerJAVA项目C#项目 效果 SonarQube端 开启权限验证 生成Jenkins登录的token 生成后记得…

MySQL通过SQL语句进行递归查询

这里主要是针对于MySQL8.0以下版本&#xff0c;因为MySQL8.0版本出来了一个WITH RECURSIVE函数专门用来进行递归查询的 先看下表格数据&#xff0c;就是很普通的树结构数据&#xff0c;通过parentId关联上下级关系 下面我们先根据上级节点id递归获取所有的下级节点数据&#x…

手机APP测试——如何进行安装、卸载、运行?

手机APP测试——主要针对的是安卓( Android )和苹果IOS两大主流操作系统,主要考虑的就是功能性、兼容性、稳定性、易用性、性能等测试&#xff0c;今天先来讲讲如何进行安装、卸载、运行的内容。 一、App安装 1、点击运行APP安装包,检测安装包是否正常; . 2、进入[安装向导]…

关于Python读取Excel表格中的内容

1、准备 首先准备好Excel表&#xff0c;并向里面填充好内容 2、相关算法 import pandas as pd# file_path rE:\data.xlsx # r对路径进行转义&#xff0c;windows需要 file_path rdata.xlsx# 这行代码括号里的head0&#xff0c;表示excel文件中第一行是表头&#xff0c;…

Independent Variable Dependent Variable

自变量&#xff08;Independent Variable&#xff09; -----------> 因变量&#xff08;Dependent Variable&#xff09; 数据 ----------------------------------------------结果&#xff0c;报告等等

python+requests+pytest+allure自动化测试框架

&#x1f345; 视频学习&#xff1a;文末有免费的配套视频可观看 &#x1f345; 关注公众号【互联网杂货铺】&#xff0c;回复 1 &#xff0c;免费获取软件测试全套资料&#xff0c;资料在手&#xff0c;涨薪更快 1、核心库 requests request请求 openpyxl excel文件操作 log…

Spring官网中查看MongoDB的API文档的详细步骤

目录 Spring官网中查看MongoDB的API文档的详细步骤1、进入 Spring 官网2、选择 Mongodb的文档介绍3、点击API文档4、进入文档查询页面 Spring官网中查看MongoDB的API文档的详细步骤 1、进入 Spring 官网 首先进入Spring的官网&#xff0c;然后点击【Spring Data】 2、选择 Mon…

Redis 架构深入:主从复制、哨兵到集群

大家好&#xff0c;我是小康&#xff0c;今天我们来聊下 Redis 的几种架构模式&#xff0c;包括主从复制、哨兵和集群模式。 前言&#xff1a; 设想一下&#xff0c;你的咖啡馆在城市中太受欢迎&#xff0c;导致每天都人满为患。为了缓解这种压力&#xff0c;你决定在其他地方…

春招秋招,网申在线测评是以什么作为通过标准?

网申是以什么作为通过标准的&#xff1f; 这个要根据测评内容来区分&#xff0c;通常来测评会包含&#xff0c;三个部分&#xff1a;认知能力测试&#xff0c;职业性格测试&#xff0c;心理健康测试。 1、认知能力测试这个是有分数的&#xff0c;高分优先&#xff0c;很明显…

云服务器实例重启后,各个微服务的接口(涉及mysql操作的)都用不了了

问题描述&#xff1a; 云服务器被黑客植入挖矿。重启云服务器实例后得到解决&#xff0c;接着把docker&#xff08;zookeeper、redis啥的&#xff09;还有后端jar包啥的都重启了&#xff0c;然后发现后端接口访问不了&#xff0c;只有不涉及数据库操作的接口正常访问&#xff…

阿里云服务器地域所在城市对照表,北京杭州深圳广州上海

2024年最新阿里云服务器地域分布表&#xff0c;地域指数据中心所在的地理区域&#xff0c;通常按照数据中心所在的城市划分&#xff0c;例如华北2&#xff08;北京&#xff09;地域表示数据中心所在的城市是北京。阿里云地域分为四部分即中国、亚太其他国家、欧洲与美洲和中东&…

js对象 静态方法和实例方法

求下面代码的输出结果&#xff1a; 首先先分析一下上面各函数&#xff1a; Person.say function(){console.log("a")} 第一个say()方法是定义在Person函数身上的&#xff0c;我们如果想使用这个方法&#xff0c;可以通过Person().say()来调用 this.say function()…