C learning_14 指针篇之破解版

这篇具有很好参考价值的文章主要介绍了C learning_14 指针篇之破解版。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

目录

1. 指针是什么

指针变量

2. 指针和指针类型

那指针类型的意义是什么?

3. 野指针

规避野指针

4. 指针运算

指针+-整数

指针-指针

指针的关系运算(比较指针的大小)

5. 指针和数组

6. 二级指针

7. 指针数组


1. 指针是什么

        指针是C语言中一种非常重要的数据类型,它存储着一个变量的内存地址。通过指针,程序可以直接访问到对应内存地址上存储的数据,从而实现一些高效的操作。指针是内存中一个最小单元的编号,也就是地址。

C learning_14 指针篇之破解版

        写c语言程序的时候,创建的变量都要在内存上开辟空间,c语言通过&操作符取出变量的地址,平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量。 

C learning_14 指针篇之破解版

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

指针变量

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

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

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

一个小的单元到底是多大?如何编址?

        一个小的单元在计算机中通常指的是1个字节(Byte),也就是8个二进制位(bit)。一个字节是计算机存储数据的基本单位,可以用来存储字符、整数、布尔值等各种数据类型。

在计算机内存中,每个字节都有一个唯一的地址,用来标识该字节在内存中的位置。内存地址通常按照字节递增的顺序进行编址,比如第一个字节的地址为0x00000000,第二个字节的地址为0x00000001,以此类推。

假设我们有一个char类型的变量,我们可以使用指针来获取它在内存中的地址,如下所示:

```
char a = 'A';
char* p = &a;
printf("a的地址为:0x%x\n", p);  // 输出:a的地址为:0x7fff5fbff8c7
```

其中,&a表示获取a变量的地址,p是一个指向char类型变量的指针变量,输出的地址就是a变量在内存中的地址。需要注意的是,输出的地址的格式通常是16进制表示。

总结:

1.一个字节对应一个地址。

2.指针变量是用来存放地址的,地址是唯一标示一个内存单元的。

3.指针的大小在32位平台是4个字节,在64位平台是8个字节。

2. 指针和指针类型

指针类型是指指针变量指向的数据类型。指针变量所指向的数据类型不同,指针变量的类型也就不同。

在C语言中,指针类型的定义格式为:数据类型 *指针变量名;

char c = 'h';
int a = 10;
double pi = 3.14;
char  *pc = &c;
int   *pi = &a;
short *ps = &pi;

char* 类型的指针是为了存放 char 类型变量的地址;

int* 类型的指针是为了存放 int 类型变量的地址;

double* 类型的指针是为了存放 double 类型变量的地址。

那指针类型的意义是什么?

C learning_14 指针篇之破解版

C learning_14 指针篇之破解版

 type* p;

        *说明p是一个指针变量。

1.p指向的对象的类型。

2.p解引用的时候访问的对象的大小是(sizeof(type))。

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

3. 野指针

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

成因:

        1. 指针未初始化

        2. 指针越界访问

        3. 指针指向的空间释放

#include <stdio.h>
int main()
{
    int* p;//局部变量指针未初始化,默认为随机值
    *p = 20;//error C4700: 使用了未初始化的局部变量“p”
    return 0;
}

        这段代码中,定义了一个指向 int 类型数据的指针变量 p,但是并没有初始化它,这意味着 p 中存储的值是随机的,可能是任意值。

        在对指针 p 解引用赋值时,需要注意指针是否已经指向了一个可用的内存地址,否则会导致未定义的行为,可能会导致程序崩溃、数据损坏等问题。

        在这个程序中,指针 p 指向的内存地址是未知的,解引用赋值 *p = 20 会尝试将值 20 写入未知的内存地址,可能会导致程序崩溃或者其他严重的后果。

        因此,这个程序是不安全的,需要避免未初始化指针的使用。在定义指针变量后应该将其初始化为一个有效的内存地址,或者在使用指针之前检查其是否为 NULL。

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

这段代码存在问题,主要存在以下两个问题:

1. 数组越界

        在程序中,定义了一个大小为10的数组arr,但是在循环中使用了 <=11 的循环条件,这将导致循环执行超出了数组arr的范围,从而导致数组越界。

2. 野指针

        在循环中,每次循环时使用指针p对数组arr进行赋值,但是在循环条件为 i = 10 时,指针p所指向的位置已经超出了数组arr的最大索引值,而在第11次循环时,指针p指向的地址已经不再是arr数组的地址了,因此会导致指针p成为野指针。这将导致对指针p的访问导致不可预期的结果。

        因此,为了避免这些问题,我们需要在循环中使用正确的条件来避免数组越界,同时还应该确保指针p的范围不超出数组arr的范围。

#include<stdio.h>
int* test()
{
	int a = 10;
	return &a;
}
int main()
{
	int* p = test();
	printf("%d\n", *p); //warning C4172 : 返回局部变量或临时变量的地址: a
	return 0;
}

        这段代码存在一个问题,就是在函数test()中使用了一个局部变量a,并将其地址返回给了main()函数中的指针p。这是不安全的,因为在函数test()执行完毕后,局部变量a所占用的内存空间被释放,此时指向该内存空间的指针变成了野指针,使用该指针就会导致未定义行为。

规避野指针

1. 指针初始化,如若不知道初始化为何值,直接初始化为NULL

2. 小心指针越界

3. 指针指向空间释放,及时置NULL

4. 避免返回局部变量的地址

5. 指针使用之前检查有效性

#include <stdio.h>
int main()
{
    
    int a = 10;
    int * p = &a;

    int* ptr = NULL;//ptr是一个空指针,没有指向任何有效的空间.这个指针不能直接使用
    //int * ptr;野指针
    if (ptr != NULL)
    {
        //使用
    }
    return 0;
}

4. 指针运算

#include<stdio.h>
/*
	int arr[10]
	int* p = arr;
	arr是数组名,数组名就是首元素的地址
	p == arr == &arr[0]
	arr[i] == *(p+i) == *(arr+i)
	由于加法具有交换律
	*(arr+i) == *(i+arr) == i[arr]
*/
int main()
{
	int arr[10] = { 0 };
	//不使用下标访问数组
	int* p = &arr[0];
	int i = 0;
	int size = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < size; i++)
	{
		//p指针位置改变
		*p = i;//指针p每次向后条sizeof(int)个字节
		p++;//p += 1;

		//p指针位置不变
		//*(p + i) = i;
	}
	p = &arr[0];
	for (i = 0; i < size; i++)
	{
		//printf("%d ", *(p+i));
		printf("%d ", i[arr]);//0 1 2 3 4 5 6 7 8 9
	}
	return 0;
}

指针+-整数

C learning_14 指针篇之破解版

        这段代码定义了一个大小为N_VALUES的float类型数组values和一个指向float类型的指针vp。然后使用指针vp和数组的方式分别给数组中的所有元素赋值为0,最后指针vp指向数组中最后一个元素的后一个位置。具体解释如下:

1. 定义数组和指针:

```
        float values[N_VALUES];
        float *vp;
```

        这行代码定义了一个大小为N_VALUES的float类型数组values和一个指向float类型的指针vp,可以用指针vp访问数组中的元素。

2. 给数组中所有元素赋值为0:

```
for (vp = &values[0]; vp < &values[N_VALUES]; ) {
    *vp++ = 0;
}
```

        这段代码使用指针vp和数组的方式分别访问数组中的所有元素,将它们的值赋为0。其中,指针vp的初始值为数组的第一个元素的地址(&values[0]),每次循环使用指针解引用操作(*)访问当前指向的元素,并将其赋值为0,然后将指针vp自增一个元素大小的偏移量(float类型为4个字节),以便访问下一个元素。循环终止时,数组中的所有元素都被赋值为0。

3. 指针vp指向数组最后一个元素的后一个位置:

```
vp = &values[N_VALUES];
```

        这行代码将指针vp指向数组中最后一个元素的后一个位置。由于数组下标从0开始,而数组中共有N_VALUES个元素,所以最后一个元素的下标为N_VALUES-1。

指针-指针

C learning_14 指针篇之破解版

        这段代码创建了一个大小为10的int类型数组arr,并将其所有元素初始化为0。然后使用“指针-指针”的方式计算数组中最后一个元素地址与第一个元素地址之间的元素个数,结果为9(前提:两个指针指向了同一块空间)。具体解释如下:

1. 创建数组并初始化为0:

```
int arr[10] = { 0 };
```

        这行代码创建了一个大小为10的int类型数组arr,并将其所有元素初始化为0。

2. 使用“指针-指针”的方式计算数组中元素的个数:

```
printf("%d\n", &arr[9] - &arr[0]);
```

        这行代码使用“指针-指针”的方式计算数组中最后一个元素地址与第一个元素地址之间的元素个数。其中,&arr[9]表示数组中最后一个元素的地址,&arr[0]表示数组中第一个元素的地址,二者相减即为这两个地址之间的偏移量,即数组中元素的个数的绝对值。由于数组中共有10个元素,最后一个元素的下标为9,所以输出结果为9。

#include<stdio.h>
//计数器count
int my_strlen1(char* s)
{
	int count = 0;
	while (*s != '\0')
	{
		count++;
		s++;
	}
	return count;
}
//递归
int my_strlen2(char* s)
{
	if (*s == '\0')
		return 0;
	else
		return 1 + my_strlen2(s + 1);
}
//指针 - 指针
int my_strlen3(char* s)
{
	char* start = s;
	while (*s)
	{
		s++;
	}
	return s - start;
}
//指针 - 指针
int my_strlen4(char* s)
{
	char* start = s;
	while (*s++);
	return s - start - 1;
}
int main()
{
	char arr[] = "abcdef";
	//不算'\0'
	printf("%d\n", my_strlen1(arr));
	printf("%d\n", my_strlen2(arr));
	printf("%d\n", my_strlen3(arr));
	printf("%d\n", my_strlen4(arr));
	return 0;
}

指针的关系运算(比较指针的大小)

C learning_14 指针篇之破解版

5. 指针和数组

        指针指的是指针变量,不是数组,指针变量的大小是4/8个字节,是专门来存放地址的。

        数组也不是指针,数组是一块连续的空间,存放一组相同类型的数据的。

        联系:       

        1. 数组名本身就是指向数组首元素地址的指针。例如,对于一个int类型的数组a,a就是指向a[0]的指针。

        2. 数组元素可以使用指针来进行访问。例如,a[i]可以写成*(a+i),其中a+i表示a的首地址加上i个元素的偏移量,也就是第i个元素的地址。

        3. 数组名也可以用来表示指向数组的指针,可以在某些情况下将数组名用作指针使用。例如,在函数参数中使用数组名时,实际上是传递了数组首元素的指针。

        4. 指针也可以用来模拟数组的行为。例如,可以用指针来动态分配内存来模拟变长数组。

总结:数组中,数组名其实就是数组首元素的地址,数组 == 地址 == 指针

        当我们知道数组首元素的地址的时候,因为数组又是连续存放的,所以通过指针就可以遍历访问数组,数组可以通过指针来访问。

#include <stdio.h>
int main()
{
    int arr[10] = { 0,1,2,3,4,5,6,7,8,9 };
    int i = 0;
    //数组名就是首元素地址
    //arr == &arr[0]
    int* p = arr;
    for (i = 0; i < 10; i++)
    {
        //&arr[i] == p+i
        //通过指针访问
        printf("&arr[%d] = %p <=====>  %p\n",*(p+i), &arr[i],p+i);
        //所以 p + i 其实计算的是数组 arr 下标为i的地址。
    }
    return 0;
}

C learning_14 指针篇之破解版

数组名表示的是数组首元素的地址。(2种情况除外)

        1.sizeof(数组名) 2.&数组名   -----这两个代表整个数组

6. 二级指针

        指针是一种特殊的变量,它的值为内存中另一个变量的地址。在 C 中,指针变量存储的是另一个变量的地址。如果我们要通过指针修改这个变量的值,我们可以使用一级指针。

但有时候我们需要通过指针修改指针所指向的变量的值,这时候就需要使用二级指针。简单来说,二级指针是指一个指针变量的地址,也就是指向指针的指针。

例如:
int a = 10;
int * p = &a;
int ** pp = &p;

这里,变量 `p` 是一个指向 `int` 类型的指针,它存储变量 `a` 的地址。而变量 `pp` 是一个指向 `int*` 类型的指针,它存储变量 `p` 的地址。这样我们就可以通过 `pp` 来修改 `a` 的值了。

例如:

**pp = 20;

这里,可以理解为 *(*pp)= 20,首先*pp通过解引用获取指针变量p的地址,然后再*p解引用修改a的值。

        使用二级指针时需要注意,要先分配好一级指针指向的内存空间,然后再通过二级指针来修改它所指向的变量的值。

 7. 指针数组

        指针数组是数组,不是指针。它是由多个指针变量组成的数组,数组中的每个元素都是指针变量,它们的值是一个地址。我们可以通过指针数组来保存多个变量的地址,并且可以通过下标来访问这些变量。

指针数组的定义方式如下:
int *ptrArr[10]; //定义一个包含10个指向int类型的指针变量的数组

        在这个示例中,`ptrArr` 是一个包含10个指向 `int` 类型的指针变量的数组。我们可以使用下标来访问数组中的每个元素,例如 `ptrArr[0]`、`ptrArr[1]` 等,它们都是指向 `int` 类型的指针变量。

        指针数组可以用于保存多个变量的地址。

#include<stdio.h>
int main()
{
	char arr1[] = "abc";
	char arr2[] = "def";
	char arr3[] = "ghi";
	//指针数组,每一元素都是一个一维数组的地址
	char* ptr[] = { arr1,arr2,arr3 };
	int i = 0;
	for (i = 0; i < 3; i++)
	{
		printf("%s\n", ptr[i]);
	}
	return 0;
}

C learning_14 指针篇之破解版

#include<stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	//指针数组,每一元素都是一个一维数组的地址
	int* ptr[] = { arr1,arr2,arr3 };
	int i = 0;
	//打印数组的内容
	for (i = 0; i < 3; i++)
	{
		int j = 0;
		for(j=0;j<5;j++)
		{
			printf("%d ", ptr[i][j]);
		}
		printf("\n");
	}
	return 0;
}

C learning_14 指针篇之破解版文章来源地址https://www.toymoban.com/news/detail-453473.html

到了这里,关于C learning_14 指针篇之破解版的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 目录打开显示提示文件或目录损坏且无法读取、文件或目录损坏且无法读取的破解之道

        咱们在平日工作时,通常都会将资料放进不同的目录中,方便咱们找到,随着时间的推移就会产生有越来越多目录。最近有位用户了这样一个问题,就是目录无论怎么都无法打开,这样就无法浏览、使用里面的资料了,影响到了咱们的正常使用,那目录文件或目录损坏且

    2024年02月02日
    浏览(90)
  • 使用dirhunt无需暴力破解即可扫描Web目录

    Dirhunt 是一个针对搜索和分析目录进行优化的网络爬虫。如果服务器启用了 “index of” 模式,则该工具可以找到有趣的东西。如果未启用目录列表,Dirhunt 也很有用。它可以检测带有虚假 404 错误的目录、创建空索引文件以隐藏内容的目录等等。 Dirhunt 不使用暴力。但它也不仅

    2024年02月07日
    浏览(52)
  • C++类循环依赖破解:前向声明与智能指针的妙用

      概述: 在C++中,通过前向声明和指针或智能指针,可以解决类之间的循环依赖问题。这种技术确保了代码结构清晰,有效规避了生成错误。通过示例演示了如何使用这些方法处理类之间相互引用的情况,提升了代码的可维护性和可读性。 在C++中,类之间的循环依赖关系可

    2024年03月23日
    浏览(24)
  • CleanMyMac X 4.14.6 for Mac 最新中文破解版 系统优化垃圾清理工具

    CleanMyMac X for Mac 是一款功能更加强大的系统优化清理工具,相比于CleanMyMac 3来说,功能增加了不少,此版本为4.7.4官方最新中英文正式破解版本,永久使用,解决了打开软件崩溃问题,最新版4.3.0版本中新增了一个比较有趣的工具,用于搜索Mac上的垃圾空间,称之为“空间镜

    2024年01月22日
    浏览(46)
  • learn_C_deep_14 (条件编译的基本使用与理解)

    目录 条件编译 1.条件编译如何使用? 2.为何要有条件编译? 3. 条件编译都在哪些地方用? 1.条件编译如何使用?         C语言的条件编译是一种在程序编译时根据条件选择不同代码段进行编译的技术。条件编译可以用于实现代码跨平台,开启或关闭某些功能,以及调试等情

    2024年02月06日
    浏览(39)
  • 论文阅读【14】HDLTex: Hierarchical Deep Learning for Text Classification

    论文十问十答: Q1论文试图解决什么问题? 多标签文本分类问题 Q2这是否是一个新的问题? 不是 Q3这篇文章要验证一个什么科学假设? 因为文本标签越多,分类就越难,所以就将文本类型进行分层分类,这样就可以加大文本分类的准确度。 Q4有哪些相关研究?如何归类?谁

    2023年04月09日
    浏览(39)
  • 安全小课堂丨什么是暴力破解?如何防止暴力破解

    什么是暴力破解? 暴力破解也可称为穷举法、枚举法,是一种比较流行的密码破译方法,也就是将密码进行一一推算直到找出正确的密码为止。比如一个6位并且全部由数字组成的密码,可能有100万种组合,也就是说最多需要尝试100万次才能找到正确的密码,但也有可能尝试

    2024年04月28日
    浏览(46)
  • 1. ELK Stack 理论篇之什么是ELK Stack?

    ELK Stack指的就是 Elastic Stack。 Elastic Stack 是 ELK Stack 的更新换代产品。 那么, ELK 到底是什么呢? “ELK”是三个开源项目的首字母缩写,这三个项目分别是: Elasticsearch 、 Logstash 和 Kibana 。 Elasticsearch 是一个搜索和分析引擎。 Logstash 是服务器端数据处理管道,能够同时从多个来

    2023年04月09日
    浏览(33)
  • Android 5.0 ~ 14访问Android/data(obb)目录的方法

    众所周知,安卓每次出新版本的时候都会收紧权限,存储权限也不例外。虽说官方的意思是为了保护隐私安全,但这些改动着实令开发者和用户感到头疼,尤其是Android/data、Android/obb目录的访问。毕竟用户更难操作,开发者也要费力适配。那么今天就来探索下怎么适配这些变

    2024年03月24日
    浏览(49)
  • 前端React篇之哪些方法会触发 React 重新渲染?重新渲染 render 会做些什么?

    在React中,以下方法会触发重新渲染: setState() :当调用组件的setState方法并传入新的状态值时,React会触发重新渲染。 forceUpdate() :可以强制组件重新渲染,不管组件的状态是否发生变化。 props改变 :当组件接收到新的props时,它会进行重新渲染。 context改变 :如果使用了

    2024年04月10日
    浏览(41)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包