C语言指针及指针进阶

本章为指针入门,指针进阶地址:http://t.csdn.cn/Bj6mr

目录

1、指针是什么

2、一个单元是多少个字节、如何编址(这里是截了比特的ppt

3、指针的类型有什么用

4、野指针

1、指针未初始化

2、指针的越界访问

 3、已经被销毁的地址还是再用

 4、如何规避野指针

5、指针运算

1、指针+-整数

2、指针-指针 

 3、指针的关系运算

 6、数组名是什么

1、&arr 

 2、sizeof(arr) 

 7、二级指针

8、指针数组

1、指针是什么

我的理解就是存放了一个变量的地址

指针是个变量,存放内存单元的地址(编号),如图所示。

C语言指针及指针进阶_第1张图片编写一个代码测试一下

首先先创建一个整形变量,在里面存放一个10,之后创建一个整形指针存放a的地址。

之后分别读出a的地址,与p存储的数据,可以看见读出的两个地址是一样的。

之后取指针p的地址,可以看出p也是有地址的。

#define _CRT_SECURE_NO_WARNINGS
#include 
int main()
{
	int a=10;
	int *p=&a;
	printf("a=%d\r\n",*p);
	printf("a地址=%p\r\n",&a);
	printf("p存储的地址=%p\r\n",p);
	if(&a==p)
	{
		printf("是同一个地址\r\n");
	}
	printf("p的地址=%p\r\n",&p);
	getchar();
	return 0;
}

运行结果C语言指针及指针进阶_第2张图片

总结:指针也是一个变量,但是是用来存放地址的变量。(存放在指针中的值都被当成地址处理)。


2、一个单元是多少个字节、如何编址(这里是截了比特的ppt

总结一下就是32位的机器有32根地址线,就有32位地址,这32位地址需要用4个字节来存储,所以指针的大小就是4个字节=2的32次方,有4GB的K线进行编写。

64位机器指针就是8字节

C语言指针及指针进阶_第3张图片

C语言指针及指针进阶_第4张图片

3、指针的类型有什么用

我们知道变量的类型有整形、浮点型、字符型等,那么指针有没有类型,如果有类型的话指针的类型有什么用呢,他存放的不是地址吗。那他的类型有什么意义。

写个代码测一下

首先我们创建一个int变量、一个char指针、一个int指针

用两个指针分别保存int变量的地址并且打印出来地址与存储值

#define _CRT_SECURE_NO_WARNINGS
#include 
int main()
{
	int n = 65538;
	char *pc = (char*)&n;
	int *pi = &n;
	printf("n的地址=	  %p\n", &n);
	printf("拿char类型存储a地址为:  %p\n",pc);
	printf("拿char类型存储a地址+1为:%p\n", pc+1);
	printf("拿int类型存储a地址为:	 %p\n", pi);
	printf("拿int类型存储a地址+1为: %p\n", pi+1);
	printf("拿char类型指针解应用n: %d\n", *pc);
	printf("拿int类型指针解应用n: %d\n", *pi);
	printf("pi的地址=%p\n", &pc);
	printf("pc的地址=%p\n", &pi);
	getchar();
	return 0;
}

C语言指针及指针进阶_第5张图片

 这里我先打印了n的地址,之后分别拿int指针和char指针存储了n的地址,可以看见打印出来的地址是一样的,这也就说明了指针类型并不会影响地址的存储。

但是对pc+1和pi+1后得到的地址不一样,这就说明了指针类型会影响指针的步距。

 之后对pc和pi进行一个解引用,可以看出int类的可以打印出65538但是char类型的只能打印出2,这就证明了指针类型还会影响,指针能操作的权限。char类型就只能控制4个字节。65536在弟五位。

直接看地址

int指针

int main()
{
	int a=0x11223344;
	int* pi=&a;
	char* pc=&a;
	*pi=0;
	getchar();
	return 0;
}

C语言指针及指针进阶_第6张图片取a的地址,里面数据为11 22 33 44

用int指针将其改为0这里四个字节全变为了0。

 char指针:只有低字节变为了00,说明char指针只影响了一个字节。

C语言指针及指针进阶_第7张图片

4、野指针

概念: 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

野指针的成因:1.指针未初始化   2.指针越界访问  3.指针指向的空间已经被释放了

1、指针未初始化

//局部变量指针未初始化,默认为随机值

声明指针变量后没有初始化为NULL,导致了地址为随机值。

int main()
{
	int  *pi;//局部变量指针未初始化,默认为随机值
	char *pc=NULL;
	getchar();
	return 0;
}

2、指针的越界访问

int main()
{
	int arr[10] = {0};
	int *p = arr;
	int i = 0;
	for(i=0; i<=11; i++)
	{
		//当指针指向的范围超出数组arr的范围时,p就是野指针
		*(p++) = i;
	}
	return 0;
}

10位的数组,每次p++跳过一个字节,这里循环了12次,会直接超出数组的范围,占用其他内存空间。

C语言指针及指针进阶_第8张图片

 3、已经被销毁的地址还是再用

int* test()
{
	int a=0;
	return &a;
}
int main()
{
	int* p=test();
	*p=30;
	return 0;
}

虽然a的地址被返回了,但是这个局部变量出了函数之后就被销毁了,地址也被系统回收了,但是这个地址还是在我们手中,并且我们给他写了值,这个时候虽然你能用但是是违法的,就已经算是野指针了。

C语言指针及指针进阶_第9张图片

 4、如何规避野指针

通过上面三点总结一下怎么规避

1. 指针初始化(1、指针没有初始化指向了未知地址
2. 小心指针越界(2、数组多写越界了
3. 指针指向空间释放即使置NULL
4. 指针使用之前检查有效性(3、被回收了还在用

#include 
int main()
{
int *p = NULL;
//....
int a = 10;
p = &a;
if(p != NULL)
{
*p = 20;
}
return 0;
}

5、指针运算

1、指针+-整数 2、指针-指针  3、指针的关系运算

1、指针+-整数

通过指针遍历整个数组

P++ int类型指针 ++之后直接让整个指针跳了4个字节

int main()
{
	int arr[10]={0,1,2,3,4,5,6,7,8,9};
	int i=0;
	int sz=sizeof(arr)/sizeof(arr[0]);
	int* p=arr; //arr直接出现为首元素地址
	for(i=0;i

2、指针-指针 

两个整形指针一个存储arr首元素地址,一个存储末尾元素地址,两个相减。

int main()
{
	int arr[10]={0,1,2,3,4,5,6,7,8,9};
    int* pi=arr;
	int* pi1=&arr[9];
	printf("pi1-pi=%d\n",pi1-pi);
	getchar();
	return 0;
}

结果为9,两个地址相减直接为元素个数了

 用指针-指针的方式模拟实现一个strlen函数

在函数内创建两个指针,都指向头,之后一个指针解引用判断是否当前位置符号为\0,如果一直不为\0就一直循环++位置,为\0之后就退出循环,之后用尾指针减去头指针,就能直接得到数组长度。

int My_Strlen(char* str)
{
	char* start=str;
	char* end=str;
	while((*end) != '\0')
	{
		end++;
	}
	return end-start;
}

int main()
{
	int len=0;
	char arr[]="My Strlen";
	len=My_Strlen(arr);
	printf("arr len=%d",len);
	getchar();
	return 0;
}

 3、指针的关系运算

指针比较大小:通过比较地址大小吧一整个数组值写为0;

/*1、创建一个整形指针,让他指向数组首地址,让其与尾地址比较大小*/

/*2、如果i指向的地址小于arr[5]的地址,则解引用将其内容写为0*/

                                    C语言指针及指针进阶_第10张图片

int main()
{
	int* i=NULL;
	int a=0;
	int arr[]={1,1,2,3,4,5,6};
	int sz=sizeof(arr)/sizeof(arr[0]);
	for(i=arr;i<&arr[sz];i++)
	{
		*i=0;

	}
	for(a=0;a

输出结果:

 c语言标准规定了

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

C语言指针及指针进阶_第11张图片

 如图所示:p1允许与p2比较不能与p3比较,可以向后越界一个,不可以向前越界。这样写大部分编译器可以运行,但是不符合c语言语法。

 6、数组名是什么

在大部分的情况下数组名单独出现就是首元素的地址但是有两种情况下不是。

1、&arr:代表的是整个数组的地址

2、sizeof(arr):这个代表数组大小,求数组长度的时候会用到

1、&arr 

可以看出来直接取地址虽然和首元素地址一样,但是+1的时候一个跳过的是一个元素一个跳过的是整个数组。

int main()
{
	int arr[5]={0,1,2,3,4};
	printf("%p\n",arr);
	printf("%p\n",&arr[0]);
	printf("%p\n",&arr);
	printf("%p\n",arr+1);
	printf("%p\n",&arr+1);
	getchar();
	return 0;
}

C语言指针及指针进阶_第12张图片

 2、sizeof(arr) 

很明显计算的是整个数组的字节数

int main()
{
	int arr[5]={0,1,2,3,4};
	int	len=sizeof(arr);
	int sz=sizeof(arr)/sizeof(arr[0]);
	printf("%d %d\n",len,sz);
	getchar();
	return 0;
}

 7、二级指针

C语言指针及指针进阶_第13张图片

 2级指针就是存储了1级指针的地址。

/*1、将一级指针pi的地址存入二级指针ppi*/

/*2、将变量b的地址通过二级指针解引用赋值进去*/

将pi内存储的地址改为了int b的地址,所以解引用之后打印为20。

其中有一个**ppi的语句:等于就是2次解引用,先通过解引用找到了pi再对pi解引用这样就可以直接找到a我们可以直接写入一个整数。

int main()
{
	int a=10;
	int b=20;
	int* pi=&a;
	int** ppi=π
	*ppi=&b;
    //**ppi=15;//直接找到a改为15
	printf("%d ",*pi);
    //printf("%d ",a); //测试一下是否和*pi找到的地方一样
	getchar();
	return 0;
}

 

8、指针数组

存放地址的一个数组,是一个数组

 这就是一个指针数组;int* arr3[5];

arr3是一个数组,有五个元素,每个元素是一个整形指针。

C语言指针及指针进阶_第14张图片

通过一个指针数组打印其数组值

/*1、写三个整形变量*/

/*2、创建一个指针数组将三个整形的地址放入*/

/*3、将元素(a,b,c的地址)解引用得到数据*/

int main()
{
	int a=10;
	int b=20;
	int c=30;
	int i=0;
	int* arr[3]={&a,&b,&c};
	for(i=0;i<3;i++)
	{
		printf("%d ",*(arr[i]));
	}
	getchar();
	return 0;
}

你可能感兴趣的:(c语言笔记,c语言,c++,数据结构)