说到指针可能很多人既熟悉又陌生。那么什么是指针呢?
1.0 定义和地址
1.1 指针定义及
指针是一个变量,它存储了指向另一个变量的内存地址。它可以用来间接地访问和修改这个变量的值。每个变量在内存中都有一个唯一的地址,指针通过存储这个地址来引用其他变量。指针可以用于在程序中传递和操作内存地址,从而使程序能够更高效地访问和操作内存中的数据。
这样说你可能,还是听不懂,那我们举个例子:
这一天有八位客人在前台登记了入住,分别是a,b,c,d,e,f,g,h。他们一起住在酒店的一个楼层
这就类似于指针,通过地址能让我们准确的找到想找的人。
我们回归到计算机中来,每次房间相当于一个字节,char刚刚好就是一个字符,但酒店也有大房间,有四个字节的int,八个字节的long.....而这些地址在C语言当中就被叫做指针。也可以理解为,
房间编号=地址=指针。
在32位下有32根总地址线,每根线只有两态,表⽰0,1【电脉冲有⽆】,那么 ⼀根线,就能表⽰2种含义,2根线就能表⽰4种含 义,依次类推。32根地址线,就能表⽰2^32种含 义,每⼀种含义都代表⼀个地址。同理在64位下,有64根总线,就能表示2^64种含义的地址。
1.2 &取地址操作符
取地址操作符用于获取一个变量的地址。
每个字符都有属于自己的地址。
代码语言:javascript复制#define _CRT_SECURE_NO_WARNINGS
#include<stdio.h>
int main()
{
int a = 2;
return 0;
}
//a的地址
//0x004FFDF0
//0x004FFDF1
//0x004FFDF2
//0x004FFDF3
这就是整形(int)a向内存申请的四个字节,那么我们应该怎么将地址存起来 ?
这就需要&(取地址操作符)&a将a的地址取出来。
代码语言:javascript复制int main()
{
int a = 2;
&a;
return 0;
}
1.3 指针变量
我们将a的地址取出来,但是要将它存到哪里呢?
这是时候就需要用到指针变量将这个地址给存起来。
代码语言:javascript复制int main()
{
int a = 2;
int* p1 = &a;
return 0;
}
指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址
那么,int*和int应该怎么理解呢?
p的左边是int*,*是在说明p是指针变量,而前面的int在p指向的对象是int类型的。
如果我们创建的变量是char a ='we',那么储存变量的类型也要是char * 。
那么指针变量的大小是多少?占多少个内存?
- 在32位的平台下,指针的大小是4个字节。
- 在64位的平台下,指针的大小是8个字节。
注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。
2.0 *解引用操作符(间接访问操作符)
我们要存放东西,拿东西就需要地址(指针),就可以通过地址(指针)找到地址(指针) 指向的对象,这⾥必须学习⼀个操作符叫解引⽤操作符(*)。
定义:解引用操作符(*)用于访问指针所指向的内存地址中存储的值。当使用解引用操作符对指针进行解引用时,实际上是在访问指针所指向的内存单元。
代码语言:javascript复制#include<stdio.h>
int main()
{
int a = 100;
int* p = &a;
*p = 10;
return 0;
}
以上代码,*p=10;*p意思就是通过p中存放的地址,找到所指向的空间,*p其实就是a变量,所以*p = 10,就是把操作符a改成0。
但为什么我们要大费周章的改,不直接对a就行更改呢?有没有意义?
那肯定是有的,对a的修改,多了一种途径,写代码就会更加灵活。
2.1 指针解引用
代码语言:javascript复制//代码1
int main()
{
int n = 0x11223344;
int* p = &n;
*p = 0;
return 0;
}
//代码2
int main()
{
int n = 0x11223344;
char* p = (char* )&n;
*p = 0;
return 0;
}
我们调试时代码1时会发现,代码1会将n的四个字节全部改为0,而代码2只有一个字节会被改为0。
这我们就能得出,指针变量初始化是根据变量的类型来决定的,类型决定了访问的字节大小。
3.0 指针中的const
将变量的地址传给指针变量,通过指针变量同样可以修改变量的值。但有时候我们不希望这个值或者地址被修改,我们用到什么呢?没错就是const。
3.1 const在*号左边
当const在*号左时,我们可以这样理解,*p指向指针的内容,内容锁死了,保证指针指向的内容不能通过指针来改变。 但是指针变量本⾝的内容可变。我们要修改它的值的时候,编译器就会报警告,无法修改。
将*p注释后则会打印5。
3.2 const在*号右边
当const在*号左时,p修饰的是指针变量本身,p里面的地址就被锁死,保证了指针变量的内容不能修改,但是指针指 向的内容,可以通过指针改变。
我们将p=&b注释掉,看看打印的值
4.0 void*指针
void*指针是无类型的指针,可以接受任何类型的指针,但void*类型的指针不能直接进⾏指针的 -整数和解引⽤的运算。
代码语言:javascript复制#include <stdio.h>
int main()
{
int a = 10;
void* pa = &a;
void* pc = &a;
*pa = 10;
*pc = 0;
return 0;
}
vs编译器的结果
又以上结果可以得知,void*可以接收任意类型的指针但是不能进行运算。
5.0 指针的运算
5.1 指针 -整数
5.1.1 普通
代码语言:javascript复制#include <stdio.h>
int main()
{
int n = 10;
char* pc = (char*)&n;
int* pi = &n;
printf("%pn", &n);
printf("%pn", pc);
printf("%pn", pc 1);
printf("%pn", pi);
printf("%pn", pi 1);
return 0;
}
打印结果: &n =00EFF8A0 pc =00EFF8A0 pc 1 =00EFF8A1 pi =00EFF8A0 pi 1 =00EFF8A4
我们可以看出来,pi和pc跳过的大小是不一样的,pi的类型是(int*)所以跳过 四个字节;而pc的类型是(char*)跳过一个字节。
总结:指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。
5.1.2 进阶
因为数组在内存中是连续存放的,所以只要知道了首元素地址,就能知道后边的元素的地址和值。
数组 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
---|---|---|---|---|---|---|---|---|---|---|
下标 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
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("%dn", *(p i));//(p i)就是指针 整数,同理-也是可行的
}
return 0;
}
5.2 指针-指针
代码语言:javascript复制 //指针-指针
#include <stdio.h>
int my_strlen(char* s)
{
char* p = s;
while (*p != '