C指针疑难点一点通

C指针疑难点一点通

指针和内存单元

  • 指针: 地址。

  • 内存单元: 计算机中内存最小的存储单位。——内存单元。大小一个字节。 每一个内存单元都有一个唯一的编号(数)。 称这个内存单元的编号为 “地址”。

  • 指针变量:存地址的变量。

指针定义和使用:

int a = 10;

int *p = &a;			// int* p;--- windows;	int *p ---Linux       int * p ;

int a, *p, *q, b;

*p = 250;			//指针的 解引用。 间接引用。

*p : 将p变量的内容取出,当成地址看待,找到该地址对应的内存空间。

如果做左值: 存数据到空间中。

如果做右值: 取出空间中的内容。

任意“指针”类型大小:

指针的大小与类型 无关。 只与当前使用的平台架构有关。 32位:4字节。 64位: 8字节。
sizeof(int *);
sizeof(double *);

野指针:

  1. 没有一个有效的地址空间的指针。
	int *p;
	*p = 1000;
  1. p变量有一个值,但该值不是可访问的内存区域。
	//0~255系统使用内存,>255也可能是其他程序使用
	int *p = 10;
	*p = 2000;

【杜绝野指针】

空指针:

	int *p = NULL;     //#define NULL ((void *)0)。确保*p 时, p所对应的存储空间一定是一个无效的访问区域。
	...
	//进行指针判断
	if(p != NULL){
		...
	}	

万能指针/泛型指针(void *):

可以接收任意一种变量地址。但是,在使用【必须】借助“强转”具体化数据类型。基类型为void的指针变量(void * 型变量),它不指向任何类型的数据。不要把指向void类型理解为指向任何类型的数据,应理解为指向空类型不指向确定的类型的数据。再对其赋值给另一指针变量时由系统进行类型转换,使之适合于被赋值的变量的类型。

int a = 3;
int * pl = &a;
void * p3;
p3 = (void *) p1;//将p1的值转换为void * 类型,赋值给p3
char * p2 = (char * )p3;
char ch = 'R';

void *p;  // 万能指针、泛型指针

p = &ch;

// *(char *)p 等价于 *((char *)p )
//1. ((char *)p )先转换为(char *)类型
//2.*((char *)p )再间接取值
printf("%c\n", *(char *)p);

指针和数组:

  • 数组名: 数组名是地址常量,不可以被赋值。 ++ / – / += / -= / %= / /= (带有副作用的运算符)
int a[3] = {1,2,3};
int b[3];
//不允许  b = a;
// 只能
b[0] = 1;
b[1] = 2;
  • 指针(指针变量)是变量。可以用数组名给指针赋值。 ++ –

数组名作为函数参数

当数组名作为函数参数时,退化为指针,指向数组中第一个元素的位置

#include <stdio.h>

void calcArray(int arr[]) {
	printf("%d\n", sizeof(arr)); //数组中第一个元素的位置
}

int main() {
	
	int a[3] = { 10, 20, 30};
	printf("%d\n", sizeof(a));
	calcArray(a);
	return 0;
}

在这里插入图片描述

取数组元素(四种)

数组名为数组首地址。int a[3] = {1,2,3};中数组元素a等价于&a[0] 等价于* ( a + 0);

	int arr[] = {1,3, 5, 7, 8};

	int *p = arr;  

	arr[i] == *( arr + i ) == p[i] == *(p+i)

获取元素的四种方式:

arr[i] == *( arr + i ) == p[i] == *(p+i)

指针和数组区别:

  1. 指针是变量。数组名为常量。

  2. sizeof(指针):4字节(32bit) / 8字节(64bit)

  3. sizeof(数组): 数组的实际字节数。

指针++ 操作数组:

	int arr[] = { 1, 2, 4, 5, 6, 7, 8, 9, 0 };
	int *p = arr;		

	for (size_t i = 0; i < n; i++)
	{
		printf("%d ", *p);
		p++;  // p = p+1;   一次加过一个int大小。 一个元素。
	}

p的值会随着循环不断变化。打印结束后,p指向一块无效地址空间(野指针)。

指针加减运算:

数据类型对指针的作用:

  1. 间接引用:

    决定了从指针存储的地址开始,向后读取的字节数。 (与指针本身存储空间无关。)

int a = 0x12345678; //16进制
int *p = a;  //12345678
short *p = a; //00005678
char *p = a; //00000078
printf("%p\n", *p);

在这里插入图片描述

  1. 加减运算:

    决定了指针进行 +1/-1 操作向后加过的 字节数。

初始化数组并打印

int main(int argc, char* argv[])
{
	int arr[10];
	int length = sizeof(arr) / sizeof(arr[0]);
	int* p = arr;
	//初始化数组
	for (int i = length - 1; i >= 0; i--)
	{
		*(p + i) = 10 + i; // *(p + i) == arr[i]
	}
	//最终p还指向数组的首地址。

	//输出,使用指针输出数组中所有元素
	for (int i = length - 1; i >= 0; i--)
	{
		printf("%d", *p);
		p++; //等价于p = p + 1;
	}
   //最终p指向一块无效的内存区域, p为野指针。p++会改变p的指向
   
	printf("p - a = %d \n", p - a ); // 输出10为数组元素的个数
	printf("\n");

	return 0;

}
  1. 指针 * / % : 不允许!!!

  2. 指针 ± 整数:

    1. 普通指针变量±整数

      char *p; 打印 p 、 p+1  偏过 1 字节。
      
      short*p; 打印 p 、 p+1  偏过 2 字节。
      
      int  *p; 打印 p 、 p+1  偏过 4 字节。		
      
    2. 在数组中± 整数

      short arr[] = {1, 3, 5, 8};
      
      int *p = arr; //等价 *p = &arr[0];   arr == &arr[0]数组元素首元素的地址
      //p指向数组第4个元素
      int *p = &arr[3];
      
      p+3;			// 向右(后)偏过 3 个元素
      
      
      p-2;			// 向前(左)偏过 2 个元素
      
    3. &数组名 + 1
      加一个数组的大小(数组元素个数 乘以 sizeof(数组元素类型))

int main() {
	int a[] = { 1,2,3,4,5,6,7,8,9,0 };
	
	printf("a      = %p\n", a);
	printf("&a[0]  = %p\n", &a[0]);

	printf("a + 1  = %p\n", a + 1);
    int (*p)[10]=&a;//p指向的是长度为10的整形指针
	printf("&a     = %p\n", &a);
	printf("&a + 1 = %p\n", &a + 1);

	return 0;
}
a      = 008FFEB0
&a[0]  = 008FFEB0
a + 1  = 008FFEB4
&a     = 008FFEB0 
&a + 1 = 008FFED8
  • &a + 1&a 相关 40即一个数组的长度
  • a代表数组第一个元素的地址,&a代表的是数组的首地址,虽然&aa的地址相同但含义不同,&a为数组的首地址
  1. 指针 ± 指针:

     指针 + 指针: error!!!
    
     指针 - 指针:
    
     	1) 普通类型(int、double等)的指针变量来说, 语法允许。无实际意义。【了解】
    
     	2) 数组来说:偏移过的元素个数。
    

指针实现 strlen 函数:

//	char str[] = "hello";
int strlen(char str[]) {
	char* p = str;
	while (*p != '\0')
	{
		p++;
	}
	//	p-str; 即为 数组有效元素的个数。
	return p - str;
}

指针比较运算:

1) 普通变量来说, 语法允许。无实际意义。

2) 数组来说: 地址之间可以进行比较大小。可以得到,元素存储的先后顺序。

3)空指针

int *p;
p = NULL;		// 这两行等价于: int *p = NULL;
if (p != NULL){
	printf(" p is not NULL");
}else {
	printf(" p is NULL");
}

指针数组:

一个存储地址的数组。数组内部所有元素都是地址。
int main() {
	int a = 10;
	int b = 20;
	int c = 30;

	int* p1 = &a;
	int* p2 = &b;
	int* p3 = &c;

	//整型指针数组,数组元素为 整型变量 地址
	//类型为int* [], 类型判断方式,去掉变量名(数组名arr)后剩余的为类型,(C语言变量名:字母、数字、下划线开头)
	int* arr[] = { p1,p2,p3 };
	// 数组元素为 整型变量 地址
	int* arr1[] = { &a,&b,&c };

	printf("*(arr[0]) = %d \n", *(arr[0])); // arr[0] == *(arr + 0)
	//指针数组本质,是一个二级指针
	printf("*(arr[0]) = %d \n", **arr);   //*(*(arr + 0)) *(*arr) == **arr
	return 0;
}

在这里插入图片描述

指针数组本质,是一个二级指针。

int main() {
	int a[] = { 10 };
	int b[] = { 20 };
	int c[] = { 30 };	
	// 数组元素为 数组 地址。	
	int* arr[] = { a,b,c };
	// arr[0][0] == *(arr[0]) = * (*(arr + 0) + 0)  = **arr;
	printf("*(arr[0]) = %d \n", *(arr[0])); // arr[0] == *(arr + 0)
	printf("*(arr[0]) = %d \n", **arr);   //*(*(arr + 0)) *(*arr) == **arr
	return 0;
}

二维数组, 也是一个二级指针。

多级指针:

int a = 0;

int *p = &a;  				一级指针是 变量的地址。

int **pp = &p;				二级指针是 一级指针的地址。

int ***ppp = &pp;			三级指针是 二级指针的地址。	

多级指针,不能  跳跃定义!

对应关系:(解引用)

ppp == &pp;			//&pp;三级指针(等价关系)等价 二级指针的地址

//三级指针的解引用 == 二级指针 == 一级指针的地址
*ppp == pp == &p; 			二级指针(等价关系)

**ppp == *pp == p == &a				一级指针(等价关系)

***ppp == **pp == *p == a				普通整型变量(等价关系)
int main() {
	int a = 10;
	int* p = &a;
	int** pp = &p;
	int*** ppp = &pp;
	printf("***ppp = %d\n", ***ppp);
	printf("**pp = %d\n", **pp);
	printf("*p = %d\n", *p);
	printf("a = %d\n", a);
	return 0;
}
***ppp = 10
**pp = 10
*p = 10
a = 10

*p将p变量的内容取出,当成地址看待,找到该地址对应的内存空间。

如果做左值: 存数据到空间中。

如果做右值: 取出空间中的内容。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值