c语言有这个就够了五.指针

author author     2022-12-05     441

关键词:

(一)指针的定义

1. 指针是内存中一个最小单元的编号,也就是地址

2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量

int main()

int a=10;
int* p=&a; //指针变量
return 0;

这样,我们就可以理解为:

通过&取出变量的内存地址,把地址放在一个变量中,这个变量就是指针变量。

总结:

指针就是变量,用来存放地址的单位变量。(存放在指针中的值,都被当做地址处理)

1.一个单元大小为一个字节

2.编址

对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电

平(低电压)就是(1或者0);那么32根地址线产生的地址就会是:

00000000 00000000 00000000 00000000
00000000 00000000 00000000 00000001
...
11111111 11111111 11111111 11111111

这里就有2^32个地址;每个地址标识一个字节,那我们就可以给 (2^32Byte == 2^32/1024KB ==

232/1024/1024MB==232/1024/1024/1024GB == 4GB) 4G的空闲进行编址。


32位机器上,地址用4个字节空间来存储,一个指针大小为4字节

64为机器上,地址用8个字节空间来存储,一个指针大小为8字节


总结:

1.指针是用来存放地址的,地址是唯一标示一块地址空间的;

2.指针的大小在32位平台是4个字节,在64位平台是8个字节。

(二)指针和指针类型

#include<stdio.h>
int main()

printf("%d\\n", sizeof(char*));
printf("%d\\n", sizeof(short*));
printf("%d\\n", sizeof(int*));
printf("%d\\n", sizeof(double*));
return 0;

【C语言有这个就够了】五.指针_i++

可以看到,笔者的电脑是64位,所以无论什么类型,大小都为8

那既然如此,我们为什么还要有不同类型的指针?

举个例子:

#include<stdio.h>
int main()

int a = 0x1122334455667788;//两个16进制位是八个2进制位正好占一个字节
int* pa = &a;
char* pc = &a;
printf("%p\\n", pa);
printf("%p\\n", pc);
return 0;

这段代码,我们发现两个输出结果相同

【C语言有这个就够了】五.指针_i++_02

但细心观察会发现:

【C语言有这个就够了】五.指针_数组_03

深入研究:我们发现不同类型有其独特的作用


1.决定指针进行解引用时可以访问空间的大小

  • int:
#include<stdio.h>
int main()

int a = 0x1122334455667788;//两个16进制位是八个2进制位正好占一个字节
int* pa = &a;
*pa=0;
return 0;

【C语言有这个就够了】五.指针_i++_04

*pa=0后

【C语言有这个就够了】五.指针_数组_05

  • char:

int main()

int a = 0x1122334455667788;//两个16进制位是八个2进制位正好占一个字节
char* pa = &a;
*pa = 0;
return 0;

【C语言有这个就够了】五.指针_#include_06

*pa = 0后

【C语言有这个就够了】五.指针_数组_07

我们发现int类型改变了所有字节,而char类型只改变了第一个字节

所以指针类型有意义:决定指针进行解引用时可以访问空间的大小

int*p   *p可以访问4个字节
char*p *p可以访问1个字节
doubl*p *p可以访问8个字节

2.指针类型决定了指针的步长(指针+-整数)

【C语言有这个就够了】五.指针_#include_08

我们发现int类型加1后,向后跳了4个字节(跳过一个整型值的大小);

char加1后,向后跳了1个字节(跳过了一个字符的大小)。

(double 8 个字节)

例如:

int:

#include<stdio.h>
int main()

int arr[10] = 0 ;
int*p=arr;
//char* p = arr;
int i = 0;
for (i = 0; i < 10; i++)

*(p + i) = 1;

return 0;

【C语言有这个就够了】五.指针_数组_09

char:

#include<stdio.h>
int main()

int arr[10] = 0 ;
//int*p=arr;
char* p = arr;
int i = 0;
for (i = 0; i < 10; i++)

*(p + i) = 1;

return 0;

【C语言有这个就够了】五.指针_i++_10

(三)野指针

1.概念

野指针就是指指针的位置是不可知的

2.成因

2.1指针未初始化

int main()

int a;//局部变量不初始化,默认是随机值;
int *p; //局部的指针变量,未初始化,默认为随机值
*p=20;//内存中随便找一个地址放20
return 0;

2.2指针越界访问

#include <stdio.h>
int main()

int arr[10] = 0;
int *p = arr;
int i = 0;
for(i=0; i<=12; i++)

//当指针指向的范围超出数组arr的范围时,p就是野指针
*p= i;
p++;

return 0;

2.3指针指向的内存空间释放

后面会细讲,这里只做简介:

int* test()

int a=10;
return &a;返回地址后,a被销毁,其地址释放


int main()

int *p=test();
*p=20;
return 0;

3.如何避免

1.指针初始化

2.小心指针越界

3.指针指向空间释放即使置NULL(NULL用来初始化指针,给指针赋值)

4. 避免返回局部变量的地址

5. 指针使用之前检查有效性

(四)指针运算

1.指针+-整数

#include<stdio.h>
int main()

int arr[10] = 1,2,3,4,5,6,7,8,9,10;
int i = 0;
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
for (i = 0; i < sz; i++)

printf("%d ", *p);
p = p + 1; //p++

return 0;

此处用上了指针加整数


#define N_VALUES 5
float values[N_VALUES];
float *vp;
for (vp = &values[0]; vp < &values[N_VALUES];)

*vp++ = 0;

先将vp地址对应的值改为0,再后置++发生作用,将vp地址想后移动1。

2.指针-指针

#include<stdio.h>
int main()

int arr[10] = 1,2,3,4,5,6,7,8,9,10 ;
printf("%d", &arr[9] - &arr[0]);
return 0;

【C语言有这个就够了】五.指针_数组_11


我们可以看到指针-指针结果是两个指针之间的距离(两个指针之间元素个数加1)同时,当小地址-大地址时,结果为负:


【C语言有这个就够了】五.指针_数组_12

一般情况,两个相减的指针要位于同一个数组空间;


  • 模拟实现strlen

int my_strlen(char *s) 

char *p = s;
while(*p != \\0 )
p++;
return p-s;

3.指针的关系运算

两种代码在绝大多数编译器中都可以运行,但第一种更优

for(vp = &values[N_VALUES]; vp > &values[0];)

*--vp = 0;
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)

*vp = 0;

允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与 指向第一个元素之前的那个内存位置的指针进行比较。

(五)指针和数组

关于数组名,我们看个例子:

#include <stdio.h>
int main()

int arr[10] = 1,2,3,4,5,6,7,8,9,0 ;
printf("%p\\n", arr);
printf("%p\\n", &arr[0]);
printf("%p\\n", &arr);
return 0;

【C语言有这个就够了】五.指针_数组_13

我们发现:

  • arr相当于arr[0],是首元素的地址;
  • &arr看似和arr,arr[0]相同,其实取出的是整个数组的地址。

对代码做出变形,可以看到&arr型在加1后

【C语言有这个就够了】五.指针_i++_14

&arr在加1后,地址加了40(图中是16进制表达式),相当于跳过了跳过了一个数组(一个int 4字节)

  • sizeof(数组名)计算的是整个数组的大小。

再有:

#include<stdio.h>
int main()

int arr[10] = 0 ;
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)

printf("%p========%p\\n", p + i, &arr[i]);

return 0;

【C语言有这个就够了】五.指针_数组_15

我们可以看到p+i是数组arr下标为i的地址

如此,我们可以直接通过指针访问数组

#include<stdio.h>
int main()

int arr[10] = 0;
int* p = arr;
int i = 0;
for (i = 0; i < 10; i++)

*(p + i) = i; //对数组内容赋值

for (i = 0; i < 10; i++)

printf("%d ", arr[i]);
//printf("%d ", *(p + i));

return 0;

(六)二级指针

#include<stdio.h>
int main()

int a = 10;
int* pa = &a;
int** ppa = &pa;//ppa就是二级指针变量
int*** pppa = &ppa;//int**说明ppa的类型,*pppa说明pppa是指针

**ppa = 20;
printf("%d\\n", **ppa);
printf("%d\\n", a);
return 0;

【C语言有这个就够了】五.指针_数组_16

**ppa 先通过 *ppa 找到 pa ,然后对 pa 进行解引用操作: *pa ,那找到的是 a .

(七)指针数组

指针数组————数组

数组指针————指针

实例:

#include<stdio.h>
int main()

int a = 10;
int b = 20;
int c = 30;
int i = 0;
//int* pa = a;
//int* pb = b;
//int* pc = c;
//整形数组存放整型
//字符数组存放数组
//指针数组存放指针
int* arr2[3]=&a,&b,&c;//将地址放入创建好的指针数组中
for (i = 0; i < 3; i++)

printf("%d ", *(arr2[i]));//arr2中的数据为指针,通过*解引用

return 0;

【C语言有这个就够了】五.指针_数组_17

与以下代码对比

#include<stdio.h>
int main()

int a = 10;
int b = 20;
int c = 30;
int i = 0;
//int* pa = a;
//int* pb = b;
//int* pc = c;
//整形数组存放整型
//字符数组存放数组
//指针数组存放指针
int arr2[3]=a,b,c;
for (i = 0; i < 3; i++)

printf("%d ", arr2[i]);


return 0;


【C语言有这个就够了】五.指针_数组_17









c语言有这个就够了四.操作符详解

(七)关系操作符​​< <= > >= != ==​​都挺简单,唯一注意=和==(八)逻辑操作符逻辑与 &&逻辑或 ||#include<stdio.h>intmain()inta=9;intb=1;intc=0;printf("%d\\n",a&b);printf(" 查看详情

c语言有这个就够了七.实用调试技巧

(一)什么是BUG历史上第一个bug导致程序运行错误的对象(二)调试是什么调试就是破案的过程,因为有人写代码是这样的:1.调试又称除错,是发现和减少计算机程序或电子仪器设备中程序错误的一个过程。2.调试的基本步骤... 查看详情

c语言有这个就够了四.操作符详解

(一)操作符分类算术操作符;移位操作符;位操作符;赋值操作符;单目操作符;关系操作符;逻辑操作符;条件操作符;逗号表达式;下标引用,函数调用和结构成员。(二)算术操作符+-*/%1./操作符的两个操作数都是整数... 查看详情

c++this指针----看这篇就够了(代码片段)

介绍在C++中,成员变量和成员函数是分开存储的,每一个非静态成员函数只会诞生一份函数实力,也就是说多个同类型的对象会共用这一个函数,那么当代码需要调用对象自己的时候,要怎么去区分那个... 查看详情

c语言这一张图就够了!!!

思维导图,原文件更详细,需要的私信我哦!!! 查看详情

c语言入门到精通,这一篇就够了(13万字笔记)(代码片段)

...情提示:先关注收藏,再查看,13万字保姆级C语言从入门到精通教程。文章目录计算机常识什么是计算机程序?什么是计算机语言?常见的计算机语言类型有哪些?什么是C语言?C语言历史C语言标准C语言现状为什么要学习C... 查看详情

了解kotlin,看这个就够了

在这本书中,我会使用Kotlin作为主要的语言来开发一个android应用。方式是通过开发一个应用来学习这门语言,而不是根据传统的结构来学习。我会在感兴趣的点停下来通过与Java1.7对比的方式讲讲Kotlin的一些概念和特性。用这种... 查看详情

想要了解kotlin,看这个就够了

在这本书中,我会使用Kotlin作为主要的语言来开发一个android应用。方式是通过开发一个应用来学习这门语言,而不是根据传统的结构来学习。我会在感兴趣的点停下来通过与Java1.7对比的方式讲讲Kotlin的一些概念和特性。用这种... 查看详情

c语言基础知识入门这一篇就够了(代码片段)

一、c语言基础知识入门hello,world,你好编程!#include<stdio.h> intmain()  /*在双引号中间输入HelloWorld*/   printf("HelloWorld");  return0; 注:在最新的C语言的标准中,main主函数前的类型为int而不是void!&#x... 查看详情

c语言_7运算符!看这篇就够了

一、算术运算符1.加法运算符+#include<stdio.h>intmain()inta=2;intb=3;printf("a+b=%d\\n",a+b);return0;运行结果:2.减法运算符-#include<stdio.h>intmain()inta=4;intb=3;printf("a-b=%d\\n",a-b);return0;运行结果:3.乘法运算符*#in 查看详情

学习go语言一篇就够了(持续更新)(代码片段)

前言:写博客也写了差不多一年了,我更多的时候是记录自己学习的情况,有时也有自己工作上遇到的bug,自己有时候也比较迷茫,不知道怎么去写博文,我也很想别人跟我提提建议,但是有时候觉得写写博客还是很有成就感的,我有时候... 查看详情

c++this指针----看这篇就够了(代码片段)

介绍在C++中,成员变量和成员函数是分开存储的,每一个非静态成员函数只会诞生一份函数实力,也就是说多个同类型的对象会共用这一个函数,那么当代码需要调用对象自己的时候,要怎么去区分那个... 查看详情

图片压缩,用这个就够了(代码片段)

现在的智能手机分辨率都很高,拍的高清照片动辄5M甚至7M。上传到系统的图片太大了,导致页面加载缓慢。为此,让组里一小伙做一个压缩工具。发版后,发现图片虽然是压缩了,不过有个别图片严重失真。  然后,在... 查看详情

真的,java并发编程入门看这个就够了(代码片段)

(真的,Java并发编程入门看这个就够了)1.Java天生多线程importjava.lang.management.ManagementFactory;importjava.lang.management.ThreadInfo;importjava.lang.management.ThreadMXBean;publicclassJavaThreadpublicstaticvoidmain(Str 查看详情

c++编程书籍推荐:零基础入门书籍,学c++看它们就够了!

...个没有编程经验的C++零基础小白,或者有其它语言经验的C++初学者,那么强烈推荐下面的十本零基础小白入门C++书籍。1.《C++Primer》作者:StanleyLippman,JoséeLajoie,andBarbaraE.Moo(更新到C++11)推... 查看详情

就够了

...之前,让我们来看一个在缓存中最经典的案例场景,理解这个运用场景之后,我们再去理解一致性hash算法就更容易了,在这个过程中我们还能体会一致性hash算法的优势之处,好了,让我们来描述一下这个经典的分布式缓存案例... 查看详情

redis这一篇就够了

概述什么是RedisRedis(RemoteDictionaryServer)是一个使用C语言编写的,开源的(BSD许可)高性能非关系型(NoSQL)的键值对数据库。Redis可以存储键和五种不同类型的值之间的映射。键的类型只能为字符串,值支持... 查看详情

promise看这篇就够了(代码片段)

...是也有一个不好的地方,当我们有很多回调的时候,比如这个回调执行完需要去执行下个回调,然后接着再执行下个回调,这样就会造成层层嵌套,代码不清晰,很容易进入“回调监狱”,就容易造成下边的例子:async(1,fu... 查看详情