C语言之入门指针(一)(详细教程)

C语言之入门指针

1. 内存和地址

在讲解指针之前,我们先了解一下内存和地址
用我们日常生活中的案例介绍
假设有一栋宿舍,一共7楼,一层楼有10个房间,那么一楼就有101 102 ……110的房间号,这个房间号就是地址,而这栋宿舍楼就是内存
把上述例子对应到计算机中:
在计算机中,内存被划分为一个个内存单元,其中每一个单元的大小是1个字节。其中,每个内存单元,相当于⼀个学⽣宿舍,⼀个⼈字节空间⾥⾯能放8个⽐特位,就好⽐同学们住的⼋⼈间,每个⼈是⼀个⽐特位。每个内存单元也都有⼀个编号(这个编号就相当于宿舍房间的⻔牌号),有了这个内存单元的编号,CPU就可以快速找到⼀个内存空间。
计算机中常见单位换算:

  1. bit - ⽐特位
  2. byte - 字节
  3. KB
  4. MB
  5. GB
  6. TB
  7. PB
  1. 1byte = 8bit
  2. 1KB = 1024byte
  3. 1MB = 1024KB
  4. 1GB = 1024MB
  5. 1TB = 1024GB
  6. 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时,打印数组元素

  • 6
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

4U247

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值