第七章 指针

指针的概念

系统给虚拟内存的每个存储单元分配了一个编号。从0x00 00 00 00 到0xff ff ff ff
这个编号称为地址。
指针就是地址。

在这里插入图片描述
指针变量:是个变量,用来存放一个地址。

1、无论什么类型的地址,都是存储单元的编号,在32位平台下都是4个字节。即任何类型的指针变量都是4个字节大小。
2、对应类型的指针变量,只能存放对应类型的变量的地址。

指针是一个地址,而指针变量是存放地址的变量。

指针变量的定义方法

1、简单的指针变量

数据类型 *指针变量名;

int *p;//定义了一个指针变量p

在定义指针变量的时候*时用来修饰变量的,说明变量p是个指针变量。变量名是p。

2、关于指针的运算符

&取地址、*取值

int a = 0x1234abcd;
int *p;
p = &a; //把a的地址给p赋值,&是取地址符号。
int num = *p;//*p代表取值,就相当于把a的值给了num,和num=a效果一样。

p保存了a的地址,也可以说p指向了a。

int * pointer_1=&a,*pointer_2=&b;

说明:在定义指针变量时要注意:
(1)指针变量前面的“*”表示该变量的类型为指针型变量。指针变量名是pointer_1和
pointer_2,而不是*pointer_1和 *pointer_2。这是与定义整型或实型变量的形式不同的。
上面程序不应写成:“*pointer_1=&a;"和“*pointer_2=&b;”。因为a的地址
是赋给指针变量pointer_1,而不是赋给*pointer_1(即变量a)。

指针的大小

*代表的是该变量是指针型变量。
p1是指针变量,存储的是地址,大小位32位系统4字节大小,固定不变。
前边的类型修饰符表明地址存储的数据类型。

	char* p1;
	short int* p2;
	int* p3;
	long int* p4;
	float* p5;
	double* p6;

通过指针引用数组

一个变量有地址,一个数组包含若于元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中),所谓数组元素的相针就是数组元素的地址。

引用

int a[10]={1,3,5,7,9,11,13,15,17,19};//定义a为包含10个整型数据的数组
int * pi;//定义p为指向整型变量的指针变量
p=&a[0];//把a[0]元素的地址赋给指针变量p
p=a; //p的值是数组a首元素(即a[0])的地址

在这里插入图片描述
注意:数组名不代表整个数组,只代表教组首元素的地址。上述“p=a;”的作用是“把
a数组的首元素的地址赋给指针变量p”,而不是“把数组a各元素的值赋焓p”。

取值

*p;//是a[0]的值。
*a;//是a[0]的值。
*(p+1);//是a[1]的值。
*(a+1);//是a[1]的值。

如果指针变量p已指向数组中的一个元素,则p+1指向同一数组中的下一个元
素,p-1指向同一数组中的上一个元素

指针运算

做减法的护的结果是两个指针之间有多少个元素。

	int a[10] = { 1,3,5,7,9,11,13,15,17,19 };
	int* p, * p1;
	p = a;
	p1 = &a[3];
	printf("%d", p1-p);

在这里插入图片描述

指针数组

数组中元素位相同类型的指针变量。
定义方式: int *p[5];

	int* p[5];
	int a = 100;
	int b[10] = { 1,2,3,4,5,6,7,8,9,0 };
	printf("%d\n", sizeof(p));//指针数组的大小
	p[0] = &a;//数组0位置的指针变量指向a
	printf("%p\n", p[0]);
	printf("%p\n", &a);
	printf("%d\n", *p[0]);
	p[1] = &b[2];
	printf("%d\n", *p[1]);

在这里插入图片描述

    //hello、china等并不是存放在数组中,而是文字常量区中
    //name[0]存放的是hello字符串的首地址。
	char *name[5] = {"hello", "china", "beijing", "test"};
	for (int i = 0; i < 4; i++)
	{
		printf("%s\n", name[i]);
	}

在这里插入图片描述

指针的指针

指针的指针,即指针的地址。

int a = 100;
int *p;
p = &a;
int **p1;//*p1表示是一个指针,存放int*类型。
p1 = &p;
int ***p2;//*p2表示是一个指针,存放int**类型。

字符串和指针

字符串的概念

字符串就是以‘\0’结尾的若干的字符的集合。
字符串的地址,是第一个字符的地址。

字符串的存储形式

1、数组中

char str[11] = "hello world";
str = {'o', 'o'};
strcpy(str, "what");
scanf("%s", str);

2、文字常量区

char *str = "hello world";
str = "how are you";

3、堆区

char *str = (char*)malloc(10);//动态申请10个字节的存储空间。
//首地址给str赋值。
strcpy(str, "hello world");//将字符串拷贝到存储空间中。
scanf("%s", str);

字符串的可修改性

1、数组中

char str[100] = "hello world";
str[0] = 'y';

2、文字常量区
不可修改

3、堆区

#include<stdio.h>
#include<stdlib.h>
int main()
{
	char* str = (char*)malloc(20);//动态申请20个字节的存储空间。
	char* str1 = "hello world";
	//首地址给str赋值。
	strcpy(str, str1);//将字符串拷贝到存储空间中。
	str[0] = 'o';
	*(str + 1) = 'y';
}

数组指针

二维数组

int a[3][5];
printf("%p\n", a);//a是数组起始的地址
printf("%p\n", a+1);//a+1是第二行起始地址

数组指针的定义

指向的数组的类型 (*指针变量名) [指向的数组元素的个数]

	int a[3][5];
	int(*p)[5];
	p = a; //把a的地址赋值给P
	printf("%x\n", a);
	printf("%x\n", a+1);
	printf("%x\n", p);
	printf("%x\n", p+1);

数组名字和指针变量的区别

int a[5];
int *p;
p =a;

相同点:
a是数组的名字,是a[0]的地址,p=a即p保存了a[0]的地址,即a和p都指向a[0],所以在引用数组元素的时候(a[2],*(a+2)),a和p等价。
不同点:
1、a是常量,p是变量。
可以用等号给p赋值,但不能用等号给a赋值。
2、对a取地址,和对p取地址结果不同。
因为a是数组的名字,所以对a取地址结果为数组指针。
p是个指针变量,所以对p取地址为指针的指针。

数组指针取*

数组指针取*,并不是取值的意思,而是指针类型发生变化。
一维数组取*,结果为它指向的一维数组第0个元素的地址。他们还是指向同一个地方。
二维数组取*,结果为一维数组指针。他们还是指向同一个地方。
三维数组取*,结果为二维数组指针。他们还是指向同一个地方。
。。。

	int a[3][5];
	int(*p)[5];
	p = a;
	printf("%p\n", a);//a是数组的名字,是数组中第0个一位数组的首地址。
	printf("%p\n", a + 1);//a+1跳一行
	printf("%p\n", *a + 1);//*a+1是&a[0][1]
	printf("%p\n", &a + 1);//&a+1跳过整个数组
	
	printf("%p\n", p); //数组第一行的首地址。
	printf("%p\n", p+1);//第二行的地址
	printf("%p\n", *p);//一维数组第0个元素的地址。
	printf("%p\n", *p+1);//是&a[0][1]
	

指针和函数的关系

1、指针作为函数的参数

1、值传递

将实参的值复制给形参,不会改变实参的值。

void swap(int x, int y)
{
	int temp = x;
	x = y;
	y = temp;
}

int main()
{
	int a = 10, b = 20;
	swap(a, b);
	printf("a=%d,b=%d", a, b);
}

2、地址传递

将地址传递为形参,会修改实参的值。

void swap(int *x, int *y)
{
	int temp = *x;
	*x = *y;
	*y = temp;
}

int main()
{
	int a = 10, b = 20;
	swap(&a, &b);
	printf("a=%d,b=%d", a, b);
}

3、给函数传数组

void func(int a[])
{
	a[0] = 3;
	printf("%d\n", a[0]);//3
}
void func1(int* p)
{
	*p = 10;
	printf("%d\n", *p);//10
}
int main()
{
	int a[2] = { 1,2 };
	func(a);
	func1(a);
}

4、指针作为函数的返回值

char* func()
{
	static char str[100] = "hello world";//func方法执行完后,防止内存被释放。
	char *str = "hello world";//文字常量区也不被释放
	return str;
}

int main()
{
	char* p;
	p = func();
	printf("%s\n", p);
	return 0;
}

指针保存函数的地址

1、函数指针

函数也有起始地址,函数的名字就是函数的首地址,即函数的入口地址。

2、函数指针的用处

必须创建线程的时候,传递函数作为执行任务。

3、函数指针的定义

返回值类型 (*函数指针变量名)(形参列表)

int max(int x, int y)
{
	if (x > y) {
		return x;
	}
	else {
		return y;
	}
}
int main()
{
	int (*p)(int x, int y);
	p = max;
	printf("%d\n", max(10, 20));
	printf("%d\n", (*p)(10, 20));
}

特殊指针

1、空类型指针 void*

void*可以保存任何类型的地址。

void * memset(void *s, int c, size_t n);
这个函数是将s指向的内存的前n个子字节,全部赋值为c。

2、NULL

空指针。
char *p = NULL;
可以任务p哪里都不指向,也可以认为p指向内存编号为0的地址。
通常用NULL来指针初始化。

该数据集通过合成方式模拟了多种发动机在运行过程中的传感器监测数据,旨在构建一个用于机械系统故障检测的基准资源,特别适用于汽车领域的诊断分析。数据按固定时间间隔采集,涵盖了发动机性能指标、异常状态以及工作模式等多维度信息。 时间戳:数据类型为日期时间,记录了每个数据点的采集时刻。序列起始于2024年12月24日10:00,并以5分钟为间隔持续生成,体现了对发动机运行状态的连续监测。 温度(摄氏度):以浮点数形式记录发动机的温度读数。其数值范围通常处于60至120摄氏度之间,反映了发动机在常规工况下的典型温度区间。 转速(转/分钟):以浮点数表示发动机曲轴的旋转速度。该参数在1000至4000转/分钟的范围内随机生成,符合多数发动机在正常运转时的转速特征。 燃油效率(公里/升):浮点型变量,用于衡量发动机的燃料利用效能,即每升燃料所能支持的行驶里程。其取值范围设定在15至30公里/升之间。 振动_X、振动_Y、振动_Z:这三个浮点数列分别记录了发动机在三维空间坐标系中各轴向的振动强度。测量值标准化至0到1的标度,较高的数值通常暗示存在异常振动,可能与潜在的机械故障相关。 扭矩(牛·米):以浮点数表征发动机输出的旋转力矩,数值区间为50至200牛·米,体现了发动机的负载能力。 功率输出(千瓦):浮点型变量,描述发动机单位时间内做功的速率,取值范围为20至100千瓦。 故障状态:整型分类变量,用于标识发动机的异常程度,共分为四个等级:0代表正常状态,1表示轻微故障,2对应中等故障,3指示严重故障。该列作为分类任务的目标变量,支持基于传感器数据预测故障等级。 运行模式:字符串类型变量,描述发动机当前的工作状态,主要包括:怠速(发动机运转但无负载)、巡航(发动机在常规负载下平稳运行)、重载(发动机承受高负荷或高压工况)。 数据集整体包含1000条记录,每条记录对应特定时刻的发动机性能快照。其中故障状态涵盖从正常到严重故障的四级分类,有助于训练模型实现故障预测与诊断。所有数据均为合成生成,旨在模拟真实的发动机性能变化与典型故障场景,所包含的温度、转速、燃油效率、振动、扭矩及功率输出等关键传感指标,均为影响发动机故障判定的重要因素。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
### 关于CTGU教材第7章指针进阶的内容 #### 字符串处理函数实现 在C语言编程中,指针用于高效操作内存地址。对于字符串处理而言,通过指针对字符数组的操作可以简化代码逻辑并提高效率。 下面展示了一个例子,即如何编写一个自定义`strlen()`函数来计算给定字符串的长度: ```c #include <stdio.h> int custom_strlen(const char *str) { int length = 0; while (*str != '\0') { // 当遇到终止符'\0'时停止计数 ++length; ++str; } return length; } void main() { char string[30]; fgets(string, sizeof(string), stdin); printf("The length of the input is %d.\n", custom_strlen(string)); } ``` 此程序首先读取用户输入的一行文本,并调用`custom_strlen()`方法返回其不含结尾换行符的实际长度[^4]。 #### 移动特定字符至末尾的功能实现 另一个典型应用是指针用来重新排列字符串内的某些特殊字符的位置。例如,将所有的星号(`*`)移至原字符串末端的同时保持其他部分不变: ```c #include <stdio.h> #include <string.h> void move_asterisks_to_end(char *input_string) { size_t asterisk_count = 0; char *current_position; current_position = input_string; /* 统计开头连续'*'的数量 */ while (*current_position == '*') { ++asterisk_count; ++current_position; } /* 将非'*'字符向前挪动 */ memmove(input_string, current_position, strlen(current_position)); /* 补充剩余位置为'*' */ memset(&input_string[strlen(current_position)], '*', asterisk_count); /* 添加字符串结束标志 */ input_string[strlen(current_position)+asterisk_count]='\0'; } int main(){ char test_str[]="****hello"; puts(test_str); // 输出原始字符串 move_asterisks_to_end(test_str); puts(test_str); // 输出调整后的字符串 return 0; } ``` 上述代码展示了如何利用指针遍历整个字符串,并巧妙运用标准库函数完成所需功能[^2].
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

七号公园的忧伤

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值