完全自学C(干货) —— 指针初识

目录

一,内存

二,指针

三,指针类型

四,野指针

五,指针的运算

六,指针和数组

七,二级指针

八,指针数组


一,内存

  • 内存是电脑重要存储器,计算机中所有程序的运行都是在内存中进行的;
  • 为了有效利用内存,把内存划分为一个个小的内存单元,每个内存单元大小为1字节(byte);
  • 为了能够有效的访问到内存的每个单元,就给内存单元进行了编号,称为内存单元的地址;
  • 32位机器,即32根地址线/数据线,最大可用内存大小4GB(2^32byte);
  • 64位机器,即64根地址线/数据线,最大可用内存大小2^34GB(2^64byte,非常大);

二,指针

  • 指针即内存地址(编号),指针变量即存放内存地址(编号)的变量;
  • 变量可使用 & 操作符来访问其地址;
  • 指针变量可使用*(解引用)操作符来获取地址指向的变量;
    • NULL空指针不可解引用;
  • 32位,指针大小为4byte;
  • 64位,指针大小为8byte;

空指针,是特殊指针,不指向任何实际的对象或函数(即不指向任何有效地址),一般指向0地址(取决于系统实现);访问空指针会报错,是因为其指向的逻辑地址没有对应的物理地址;

int main()
{
	char s = 'a'; //字符'a'对应ASCII码为97
	char* p = &s; //*表示是指针,char*表示存放char类型的指针
	printf("%d\n", sizeof(p)); //测试指针变量的大小
}
//结果:4
int main()
{
	char s = 'a'; //字符'a'对应ASCII码为97
	char* p = &s;
	printf("%p\n", p); //结果:0x006FFDEB
	printf("%c\n", *p); //结果:a
	return 0;
}

注:

  • 查看内存地址,按F10进入调试,在菜单内存选择内存1(调试-窗口-内存-内存1),在地址栏内输入&s,即可查看s变量的内存地址;
  • 0x006FFDEB,0x表示十六进制形式,006FFDEB — 每个数表示4位,每两个数表示8位一个字节,这里有8个数32位4个字节,即地址有4个字节(32位机器下);

三,指针类型

type + *

  • type,表示指针指向内存地址处的数据类型,如char*,就表示指向内存地址处的数据类型为字符型;
  • *,表示其是指针;
int main()
{
	char s = 'a'; 
	char* p = &s; //表示指针变量p,指向的数据为char类型

	return 0;
}

指针类型的意义

  • 指针 +/- 整数,决定了指针向前或者向跳过多大字节数,如int就跳过4个字节;
  • 指针的解引用,决定了解引用时,访问的字节个数,如int就访问4个字节;
int main()
{
	char a = 'a';
	int b = 10;
	char* pa = &a; 
	int* pb = &b;
	printf("%p %p\n", pa, pa + 1); //结果:010FFA1B 010FFA1C,相差1个字节
	printf("%p %p\n", pb, pb + 1); //结果:010FFA0C 010FFA10,相差4个字节
	return 0;
}

注:

//不可连续创建多个指针类型变量
//b类型为int
int* a, b;

//应改为
int*a, *b;

四,野指针

野指针,即指针指向的位置是不可知的(如随机的、不正确的、没有明确限制的);

野指针的成因:

  • 创建指针时未初始化;
    • 应赋值NULL或指向合法内存;
  • 指针越界访问;
    • 避免越界;
  • 指针指向的空间释放(动态内存空间会讲解到);
    • 给释放的指针空间,赋值NULL;

规避野指针,最重要的是使用前检查指针的有效性;

int main()
{
	//未初始化指针,为随机值
	char* a; 
	int* b;
    *a = 'a'; //对未初始化指针,直接使用(解引用)会报错
	return 0;
}
int main()
{
	//指针越界访问
	char arr[] = "abcd"; //有五个元素,末尾有隐藏的字符'\0'
	char* p = arr + 5; //数组名arr,即表示指针(且是首元素的地址)
	printf("%c", *(p));
	return 0;
}
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

int main()
{
	int* p = (int*)malloc(10 * sizeof(int)); //开辟动态内存,即40个字节的内存
	if (NULL == p)
		printf("%s\n", strerror(errno));
	else
	{
		int i = 0;
		for (i; i < 10; i++)
			*(p + i) = i;
	}
	//打印
	int i = 0;
	for (i; i < 10; i++)
		printf("%d ", *(p + i));
	//释放空间 
	free(p); //此时p空间已释放,即为野指针
	p = NULL; //为避免野指针,可给p赋予null

	return 0;
}

五,指针的运算

  • 指针加减整数
  • 指针减指针(表示指针间的元素个数,且指向的是同一块空间)
  • 指针的关系运算

标准规定:

  • 允许指向数组元素的指针,与指向数组最后一个元素后面的内存位置的指针比较,但是不允许与指向第一个元素之前的内存位置的指针进行比较。
//指针加减整数
int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	for (i; i < 10; i++)
	{
		*(p + i) = i; //p+i表示指针跳过i个字节,*(p+i)与arr[i]相同,即数组的每个元素
	}
    return 0;
}
//指针减指针
int main()
{
	char arr[5] = "abcd";
	char* p = arr;
	char* pend = arr;
	while (*pend != '\0') //pend指向'\0'时,即循环结束
	{
		pend++;
	}
	printf("%d\n", pend - p);
    return 0;
}
//结果:4
int main()
{
	char arr[5] = { 'a','b','c','d','e' };
	char* p = arr + 5;

	for (p; p > arr;)
	{
		*--p = 0;
	}
	return 0;
}

//要避免以下写法,因为最后一次判断是arr-1 > arr
int main()
{
	char arr[5] = { 'a','b','c','d','e' };
	char* p = arr + 4;
	for (p; p >= arr; p--)
	{
		*p = 0;
	}
	return 0;
}

六,指针和数组

  • 数组名,表示数组首元素的地址;
  • &数组名,表示整个数组的地址;
  • arr[i] = i[arr] = *(p+i) = p[i] = i[p]

注:除以下两种情况外,其余情况arr均表示首元素地址

  • &arr,此时数组名arr表示整个数组,是整个数组地址;
  • sizeof arr,此时数组名arr表示整个数组,计算整个数组大小;
int main()
{
	int arr[5] = { 1,2,3,4,5 };
	printf("%p %p\n", arr, &arr[0]); //结果:00AFFDD8 00AFFDD8 
	printf("%p %p\n", arr, arr + 1); //结果:00AFFDD8 00AFFDDC 相差4,即一个int元素
	printf("%p %p\n", &arr, &arr + 1); //结果:00AFFDD8 00AFFDEC 相差20,即5个int元素正好是整个数组
	return 0;
}
int main()
{
	int arr[5] = { 1,2,3,4,5 };
	int* p = arr;
	printf("%d %d\n", *(p + 2), arr[2]); //结果:3 3
	return 0;
}
int main()
{
	int arr[] = { 1,2,3,4,5 }; //整型数组
	int* p = arr;
	printf("%d ", arr[2]);
	printf("%d ", 2[arr]);
	printf("%d ", p[2]);
	printf("%d ", 2[p]);
	printf("%d ", *(p+2));
	return 0;
}
//结果:3 3 3 3 3
//其实[]是一个操作符,arr,2是操作数
//最后都会转换为 *(p+2)

七,二级指针

二级指针

  • 即其指向的地址也是一个指针,但此指针指向的地址不是指针;

三级指针

  • 即其指向的地址也是一个指针,且此指针指向的地址还是指针,但这个指针指向的地址不是指针;

其他,依次类推;

int main()
{
	int a = 1;
	int* p = &a; //指针,即一级指针
	int** pp = &p; //二级指针
	printf("%d\n", **pp); //结果:1
	return 0;
}

八,指针数组

指针数组,即存放指针的数组;

int main()
{
	int arr[] = { 1,2,3,4,5 }; //整型数组
	int* parr[] = { arr,arr + 1,arr + 2,arr + 3,arr + 4 }; //指针数组

	int i = 0;
	for (i; i < 5; i++)
	{
		printf("%d ", *parr[i]);
	}

	return 0;
}
//结果:1 2 3 4 5

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值