C语言之入门指针
文章目录
1. 内存和地址
在讲解指针之前,我们先了解一下内存和地址
用我们日常生活中的案例介绍
假设有一栋宿舍,一共7楼,一层楼有10个房间,那么一楼就有101 102 ……110的房间号,这个房间号就是地址,而这栋宿舍楼就是内存
把上述例子对应到计算机中:
在计算机中,内存被划分为一个个内存单元,其中每一个单元的大小是1个字节。其中,每个内存单元,相当于⼀个学⽣宿舍,⼀个⼈字节空间⾥⾯能放8个⽐特位,就好⽐同学们住的⼋⼈间,每个⼈是⼀个⽐特位。每个内存单元也都有⼀个编号(这个编号就相当于宿舍房间的⻔牌号),有了这个内存单元的编号,CPU就可以快速找到⼀个内存空间。
计算机中常见单位换算:
- bit - ⽐特位
- byte - 字节
- KB
- MB
- GB
- TB
- PB
- 1byte = 8bit
- 1KB = 1024byte
- 1MB = 1024KB
- 1GB = 1024MB
- 1TB = 1024GB
- 1PB = 1024TB
⽣活中我们把⻔牌号也叫地址,在计算机中我们把内存单元的编号也称为地址。C语⾔中给地址起了新的名字叫:指针。
内存中的编号 就是 地址 就是 内存
2. 指针变量和内存
2.1 取地址操作符(&)
在C语言中变量的创建其实就是向内存中申请了一个空间,例如:
#include <stdio.h>
int main()
{
int a = 10;
return 0;
}
由于int是4个字节,所以向内存中申请了4个字节的空间给a,用于存放整数10,每个字节都有对应的地址,每次运行代码时,a的地址都不同,取决于编译器分配的地址
我们也可以使用取地址操作符 & 取出a的地址
%p是用来打印地址的,当有一个操作数的时候,&是取地址操作符,当有两个操作数的时候,&是按位与
2.2 指针变量和解引用操作符(*)
2.2.1 指针变量
当我们将一个变量的地址取出来之后,为了方便之后的使用,我们可以使用
解引用操作符来将地址存在指针变量中
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;
return 0;
}
指针变量也是一种变量,这种变量就是专门用来存放地址的
这⾥pa左边写的是 int* , * 是在说明pa是指针变量,⽽前⾯的 int 是在说明pa指向的是整型(int)类型的对象
2.2.2 解引用操作符
当我们将地址存放在一个指针变量时,我们要想访问这个地址中的元素,我们可以使用解引用操作符 * ,就比如我们拿到了地址(指针),我们就可以通过地址(指针)来找到里面的对象。
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;
*pa = 100;
printf("a = %d\n", *pa);
return 0;
}
在上述代码中,我们使用*pa来存放了a的地址,然后我们通过 *解引用操作符来访问pa中的对象,将a改为了100
代码运行结合 a = 100
2.3 指针变量的大小
指针变量的大小取决于地址的大小
在32位平台下的地址是32个bit位
在64位平台下的地址是64个bit位
#include <stdio.h>
int main()
{
printf("%zd\n", sizeof(char*));
printf("%zd\n", sizeof(short*));
printf("%zd\n", sizeof(int*));
printf("%zd\n", sizeof(double*));
return 0;
}
32位平台下:
64位平台下:
• 32位平台下地址是32个bit位,指针变量⼤⼩是4个字节
• 64位平台下地址是64个bit位,指针变量⼤⼩是8个字节
• 注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。
3. 指针变量的类型意义
在指针变量大小的相同的情况下,那么指针变量的类型有什么意义呢
3.1 指针的解引用
//代码一
#include <stdio.h>
int main()
{
int n = 0x11223344;
int *pi = &n;
*pi = 0;
return 0;
}
//代码二
#include <stdio.h>
int main()
{
int n = 0x11223344;
char *pc = (char *)&n;
*pc = 0;
return 0;
}
0x11223344是十六进制,其中一个数字代表4个二级制
调试我们可以看到,代码1会将n的4个字节全部改为0,但是代码2只是将n的第⼀个字节改为0
结论:指针的类型决定了,对指针解引⽤的时候有多⼤的权限(⼀次能操作⼏个字节)
3.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("pc = %p\n", pc + 1);
printf("%p\n", pi);
printf("pi = %p\n", pi + 1);
return 0;
}
运行结果:
在上述代码中,在pc和pi中存放的地址是一样的,但是+1之后的结果不一样,这个取决于指针变量的类型,int类型是4个字节,所以地址一次上上了4个字节,而char是一个字节,所以只加上了1
结论:指针的类型决定了指针向前或者向后⾛⼀步有多⼤(距离)。
3.3 void* 指针
在指针类型中,有一个特殊的类型void* 指针,可以理解为无具体类型的指针(泛指型指针),这种指针可以存放任意类型的地址,但是不能进行指针的加减和解引用操作符
如果我们用char *来存放int类型的地址,系统会警告
但是使用void *来存放任意类型的地址,系统不会报错,但是无法使用加减和解引用操作符
4. const修饰指针
4.1 const修饰变量
#include <stdio.h>
int main()
{
const int n = 10;
n = 100; //不能修改n的值
int m = 10;
m = 100; //m的值可以修改
return 0;
}
在上述代码中,n的值不能被修改,n本质上是变量,但是被const修饰后变成了常变量(可以理解为无法改变)
4.2 const修饰指针变量
#include <stdio.h>
void test1()
{
int a = 10;
int b = 10;
const int* pa = &a;
*pa = 100;
pa = &b;
}
void test2()
{
int b = 10;
int a = 10;
int* const pa = &a;
*pa = 100;
pa = &b;
}
void test3()
{
int b = 10;
int a = 10;
const int* const pa = &a;
*pa = 100;
pa = &b;
}
int main()
{
test1();
test2();
test3();
}
在test1()中: const放在左边,修饰的是指针指向的内容,所以当我们用pa来改变a的值是不行的,但是我们仍然可以将b的地址存入其中
在test2()中: const放在右边,修饰的是指针变量,所以当我们使用pa来改变a的值是可以的,但是将b的地址存入其中是不行的
在test3()中:const即修饰了指针指向的内容又修饰了指针变量,这时无论是修改a的值还是将地址存入其中都是不行的
• const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变,但是指针变量本⾝的内容可变。 • const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变
5. 指针的运算
指针的运算有三种:
• 指针± 整数
• 指针-指针
• 指针的关系运算
5.1 指针±整数
打印出数组中的元素
//代码一
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
for (i = 0; i < 10; i++)
{
printf("%d ", arr[i]);
}
return 0;
}
如何用指针实现:
//代码二
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int i = 0;
int* p = arr;
for (i = 0; i < 10; i++)
{
printf("%d ", *p);
p++;
}
return 0;
}
在上述代码中,我们将arr的首地址存入*p中,(数组名就是数组的首地址)然后我们通过解引用操作符来打印数组中的元素,打印完一次之后我们p++,将它的地址加4,由于数组在内存中是连续存放的,所以p++之后会访问第二个元素,以此类推
5.2 指针-指针
int my_strlen(char* str)
{
char* start = str;
while (*str != '\0')
str++;
return str - start;
}
int main()
{
char arr[] = "abcdef";
int len = my_strlen(arr);
printf("%d\n", len);
return 0;
}
上述代码中,我们模拟了strlen函数,传进去一个数组,当数组中的元素不为 ‘\0’ 时,str++,然后使用*start存放了arr的首地址,最后当元素为 ’\0‘ 时返回最后一个元素的地址 - 首元素地址,计算出数组的长度
•结论:指针-指针 就是计算中间有多少个元素
5.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;
}
在上述代码中,sz计算了数组的长度,然后当p存放的地址小于最后一个地址元素+1时,打印数组元素