【C语言】善于利用指针(一)

个人主页

⭐个人专栏——C语言初步学习⭐

点击关注一起学习C语言


目录

导读:

1. 什么是指针

1.1 概念

1.2 图解

 1.3 示例

2. 指针和指针类型

2.1 指针的定义

2.2 指针的解引用

3. 野指针

3.1 野指针成因

3.2 如何规避野指针

4. 指针运算

4.1 指针+-整数

 ​编辑

 4.2 指针-指针

5. 指针和数组

5.1 数组指针

5.2 数组元素的访问

6. 二级指针

博主有话说:


导读:

经过前面的学习,我们已经了解了分支结构、循环结构、数组和函数的使用

还包括动态内存分配和柔性数组

 今天我们来学习指针的第一部分,主要学习指针的类型和基本用法

1. 什么是指针

指针是C语言中的一个重要概念,也是C语言的一个重要特色。正确而灵活地运用它,可以使程序简洁、紧凑、高效。每一个学习和使用C语言的人,都应该深入的学习和掌握指针。可以说,不掌握指针就是没有掌握C语言的精华。

1.1 概念

指针理解的2个要点:

  1. 指针是内存中一个最小单元的编号,也就是地址
  2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量

 总结:指针就是地址,口语中说的指针通常指的是指针变量。

1.2 图解

可以理解为: 

指针变量
我们可以通过&(取地址操作符)取出变量的内存其实地址,把地址可以存放到一个变量中,这个变量就是指针变量
 

【C语言】善于利用指针(一)_第1张图片

 1.3 示例

int main()
{
	int a = 10;//在内存中开辟一块空间
	int* p = &a;//这里我们对变量a,取出它的地址,可以使用&操作符。
	//a变量占用4个字节的空间,
	//这里是将a的4个字节的第一个字节的地址存放在p变量中,
	//p就是一个之指针变量。
	return 0;
}

我们来进入调试看一看

【C语言】善于利用指针(一)_第2张图片

我们发现,p存的就是a的地址,对p解引用得到的就是该地址所指向的内存空间的内容

总结:

  • 指针是用来存放地址的,地址是唯一标示一块地址空间的。
  • 指针的大小在32位平台是4个字节,在64位平台是8个字节。

2. 指针和指针类型

2.1 指针的定义

我们都知道,变量有不同的类型,整形,浮点型等。那指针有没有类型呢?

准确的说:有的。

int num = 10;
 p = #

要将&num(num的地址)保存到p中,我们知道p就是一个指针变量,那它的类型是怎样的呢? 我们给指针变量相应的类型。

char  *pc = NULL;

int   *pi = NULL;

short *ps = NULL;

long  *pl = NULL;

float *pf = NULL;

double *pd = NULL; ‘

指针的定义方式是:

type + *

  • int 表示指向整型变量的指针
  • double* 表示指向双精度浮点型变量的指针
  • char* 表示指向字符型变量的指针

 需要注意的是,指针类型并不决定指针变量所指向内存地址的类型,而是决定了指针变量对指向的内存地址所存储的值的解释方式。

例如:

一个int类型的指针变量可以指向一个double类型的变量,但这样会导致解释的错误。

因此,指针类型的指定应该与指针变量所指向的变量类型相同,以确保正确的解释和使用。

2.2 指针的解引用

指针的解引用是指通过指针变量访问其所指向的变量的值。

在C++中,可以使用星号(*)来解引用指针变量,例如:

int main()
{
	int x = 10;
	int* ptr = &x;   // ptr指向x的地址
	printf("%d\n", *ptr); //输出10
	*ptr = 20;       // 将ptr指向的值改为20
	printf("%d\n", *ptr); // 输出20
	return 0;
}

【C语言】善于利用指针(一)_第3张图片 

在上述代码中,ptr指向变量x的地址,可以使用星号()来解引用ptr,访问x的值。

在第一次输出时,输出了x的初始值10,然后使用星号()将ptr解引用,输出了x的值。

在第二次输出时,输出的是x被修改后的值20。

3. 野指针

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

3.1 野指针成因

1. 指针未初始化

对未初始化的指针进行解引用是一种未定义的行为,会导致程序崩溃或产生随机结果,因此在使用指针前一定要确保其被正确初始化。

int main()
{
	int* p;//局部变量指针未初始化,默认为随机值
	*p = 20;
	printf("%d\n", *p);
	return 0;
}

 【C语言】善于利用指针(一)_第4张图片

并不能成功运行。

2. 指针越界访问 

指针越界访问是指程序试图访问超出指针所指向的内存空间范围的地址,这也是一种常见的编程错误。

指针越界访问可能会导致程序崩溃、数据被破坏、安全漏洞等问题。这种错误很难检测和调试,因为错误不一定会立即引起程序崩溃,而且错误的结果也不一定与越界访问的代码行直接相关。

 

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

【C语言】善于利用指针(一)_第5张图片

虽然运行成功,但是依旧是错误的

3. 指针指向的空间释放

int main() 
{
    int* p = (int*)malloc(sizeof(int)); // 分配一个 int 类型的内存块,返回指向该内存块的指针
    if (p == NULL) 
    {
        printf("分配内存失败!\n");
        return -1;
    }
    *p = 10; // 给指针所指向的内存块赋值
    printf("p 指向的值为:%d\n", *p);
    free(p); // 释放指针所指向的内存块
    printf("p 指向的值为:%d\n", *p); 
    // 这里会出现错误,因为指针所指向的内存块已被释放
    return 0;
}

【C语言】善于利用指针(一)_第6张图片

3.2 如何规避野指针

  1. 指针初始化
  2. 小心指针越界
  3. 指针指向空间释放即使置NULL
  4. 避免返回局部变量的地址
  5. 指针使用之前检查有效性

例如:

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

4. 指针运算

指针+- 整数

指针-指针

指针的关系运算

4.1 指针+-整数

int main()
{
	int n = 10;
	char* pc = (char*)&n;//强转char*
	int* pi = &n;
	printf("%p\n", &n);
	printf("%p\n", pc);
	printf("%p\n", pc + 1);
	printf("%p\n", pi);
	printf("%p\n", pi + 1);
	return  0;
}

 【C语言】善于利用指针(一)_第7张图片

 我们可以看到,都是加1,但是结果却不一样,而这就是与指针的类型有关了

一个int类型占4个字节,int + 1自然就是加四个字节

【C语言】善于利用指针(一)_第8张图片

 一个char类型占一个字节,char + 1也是加一个字节

【C语言】善于利用指针(一)_第9张图片

总结:

指针的类型决定了指针向前或者向后走一步有多大(距离)。

 4.2 指针-指针

int main()
{
	int arr[5] = { 1, 2, 3, 4, 5 };
	int* p1 = &arr[1]; // 指向 arr[1] 的指针
	int* p2 = &arr[4]; // 指向 arr[4] 的指针
	printf("p2 - p1 = %ld\n", p2 - p1); 
	// 输出 p1 和 p2 之间的距离
	return 0;
}

【C语言】善于利用指针(一)_第10张图片

5. 指针和数组

5.1 数组指针

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

【C语言】善于利用指针(一)_第11张图片 

我们发现打印出的地址是一样的,数组名表示的是数组首元素的地址

当然也有例外,感兴趣的小伙伴可以看sizeof与数组

所以我们可以这样定义一个数组指针

int arr[10] = {1,2,3,4,5,6,7,8,9,0};
int *p = arr;//p存放的是数组首元素的地址

5.2 数组元素的访问

引用数组元素可以用下标法,也可以用指针法

int main()
{
    int arr[] = { 1,2,3,4,5,6,7,8,9,0 };
    int* p = arr; //指针存放数组首元素的地址
    int sz = sizeof(arr) / sizeof(arr[0]);
    int i = 0;
    for (i = 0; i < sz; i++)
    {
        printf("&arr[%d] = %p   <====> p+%d = %p\n", i, &arr[i], i, p + i);
    }
    return 0;
}

【C语言】善于利用指针(一)_第12张图片

所以我们就可以如下进行访问:

int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	int* p = arr; //指针存放数组首元素的地址
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

【C语言】善于利用指针(一)_第13张图片 

【C语言】善于利用指针(一)_第14张图片

6. 二级指针

指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里?

这就是二级指针

int main() 
{
    int num = 10;
    int* p = #
    int** pp = &p;

    return 0;
}

【C语言】善于利用指针(一)_第15张图片

对于二级指针的运算有:

  • *pp 通过对pp中的地址进行解引用,这样找到的是p,*pp其实访问的就是 p

博主有话说:

今天有关指针的初步学习就到这里,更多关于指针的学习关注博主,掌握最新消息

你可能感兴趣的:(c语言初步学习,c语言,开发语言,指针,数组)