指针(C语言)

目录

指针变量:

指针变量的性质:


指针变量:

说到指针,大家都不陌生。就表面意思来说,指针就是地址,地址就是存储变量的位置。在了解这一逻辑顺序后,有个问题:什么是指针变量呢?指针变量来存放指针的变量。我们通过下面的部分了解一下它的种类。

我们已经了解到下图中的数据类型:

我们可以用相应的数据类型来声明一个变量,C语言中定义声明一个变量的区别可以理解为:

变量定义:用于为变量分配存储空间,还可为变量指定初始值。程序中,变量有且仅有一个定义。

变量声明:用于向程序表明变量的类型和名字。                                                ---------链接

声明好一个变量后,那它就具有了特定的类型,例如:

int iNum;//声明了一个基本整型变量

char cChar;//声明了一个字符型变量

float fFloat;//声明了一个单精度型变量

double dDouble;//声明了一个双精度型变量

接下来介绍指针运算符,一个是&运算符(&运算符用于得到存取变量的地址),而另一个则是*运算符(*运算符用于得到在地址中存取的值),在C/C++中只有这两个关于指针的运算符,将上面的改一下:

int *iNum;//声明了一个基本整型指针变量

char *cChar;//声明了一个字符型指针变量

float *fFloat;//声明了一个单精度型指针变量

double *dDouble;//声明了一个双精度型指针变量

或是:

int* iNum;//声明了一个基本整型指针变量

char* cChar;//声明了一个字符型指针变量

float* fFloat;//声明了一个单精度型指针变量

double* dDouble;//声明了一个双精度型指针变量

不论*号是与类型名还是变量名连在一起,都是可以的,都声明了同样的指针变量。

指针变量声明完了,那它有什么用呢?简单点说就是存取值。这个是什么意思呢?我们先看几个例子:

#include<stdio.h>
int main()
{
	int iNum;
	int *iNum_position;

	iNum = 10;//第一个例子
	iNum_position = &iNum;
	printf("第一次执行:\niNum的值:%d\n存取在iNum_position的值:%d\n", iNum, *iNum_position);
	
    iNum = 11;//第二个例子
	*iNum_position = iNum;
	printf("第二次执行:\niNum的值:%d\n存取在iNum_position的值:%d\n", iNum, *iNum_position);
	return 0;
}

指针变量应该被赋什么类型的值?整型,字符型,还是其它的?不。指针变量当然只能被赋指针类型的变量,文章开头提到:指针就是地址。那么,指针变量应该被赋地址

所以,我们可以看到,在第一个例子中,我们先用&运算符得到iNum的地址,即&iNum,然后把它赋给iNum_position。在打印值的时候,用*运算符得到存储在iNum_position中的地址的值

而在第二个例子中,我们先用*运算符得到存储在iNum_position中的地址的值,这个值即*iNum_position可以理解为一个容器,我们将iNum装进这个容器

看看执行结果:

 我们想一下第一、二个例子间的区别,第一个是将变量的地址赋给iNum_position,第二个则是将变量直接赋给*iNum_position。对于第一个例子,相信大家没问题,可对于第二个,不知各位有没有想过,为什么能够将值直接赋给*iNum_position呢?我们是用int*声明了iNum_position,也就是说,我们用int声明了*iNum_position,*iNum_position是可以被赋基本整型的值的。

需要注意的是,&iNum_position是个固定的值,而&iNum也是,要问原因的话你得去问PC内存了。

例如:

#include<stdio.h>
int main()
{
	int iNum;
	int *iNum_position;
	printf("赋值前:&iNum_position:%p &iNum:%p\n", &iNum_position,&iNum);
	
	iNum = 10;
	iNum_position = &iNum;
	printf("第一次赋值:&iNum_position:%p &iNum:%p\n", &iNum_position, &iNum);
	
	iNum = 11;
	*iNum_position = iNum;
	printf("第二次赋值:&iNum_position:%p &iNum:%p\n", &iNum_position, &iNum);
	
	return 0;
}

结果为:

这还只是整型指针,如果更复杂一些呢?比如结构体指针,我们怎么去用呢?

道理是一样的,可以转化为基本数据类型之间的值交换

有一串代码:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct student
{
	int id;
	char name[10];
}stu;

int main()
{
	stu *stu1=NULL, stu2;
	stu1 = (stu*)malloc(sizeof(stu*));
	
    stu2.id = 7;
    strcpy(stu2.name, "Luoge");
	stu1->id = stu2.id;
	strcpy(stu1->name, stu2.name);
	
    printf("stu1-id:%d\nstu1-name:%s", stu1->id, stu1->name);
	return 0;
}

 也可以这么写:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
typedef struct student
{
	int id;
	char name[10];
}stu;
int main()
{
	stu *stu1=NULL, stu2;
	stu1 = (stu*)malloc(sizeof(stu*));
	
    stu2.id = 7;    
    strcpy(stu2.name, "Luoge");
	(*stu1).id = stu2.id;
	
    for (int i = 0; i < strlen(stu2.name); i++)
		stu1->name[i] = stu2.name[i];
	stu1->name[strlen(stu2.name)] = '\0';
	
    printf("stu1-id:%d\nstu1-name:%s", stu1->id, stu1->name);
	return 0;
}

我们首先定义一个结构体(没学过结构体的朋友看这里),用typedef将它重命名为stu。用stu声明两个变量分别为*stu1和stu2。

我们用malloc()函数为结构体指针stu1分配内存空间(需要这么做),然后为stu2.id和stu2.name分别赋值,接着就有两种方法为stu1的id赋值。

1.stu1->id = stu2.id;

//->运算符指向结构体成员运算符,它的作用是搭配结构体指针变量获取结构体指针变量的成员变量。

2.(*stu1).id = stu2.id;

//.运算符叫结构体成员运算符,它的作用是搭配结构体变量获取结构体变量的成员变量。

可以看出,stu1->id等价于(*stu1).id,在这里能够将stu2.id成功赋给这两个的原因是他们都是int类型的变量,而下面的stu1->name[i]等价于(*stu1).name[i]。

我们着重看一下第二种方法中为stu1->name赋值的方法:

for (int i = 0; i < strlen(stu2.name); i++)
        stu1->name[i] = stu2.name[i];
stu1->name[strlen(stu2.name)] = '\0';

我们将stu2.name中的字符一个个赋给stu1->name,然后再将stu1->name目标字符串的最后一个非空字符下一个字符设置为'\0',让后面的字符串断掉,以防打印出乱码。

结果为:

指针变量的性质:

1.不同类型的指针变量所占用的存储单元长度是相同的。

这是什么意思呢?这是说整型、字符型、结构体类型等指针变量占用着相同的内存空间。

例如,有下面这样的代码:

#include<stdio.h>
#include<string.h>
typedef struct student
{
	int id;
	char name[10];
}stu;
int main()
{
	stu *stu1;
	int *iNum;
	char *cChar;
	float *fFloat;
	double *dDouble;
	
	printf("sizeof(stu1):%d\n", sizeof(stu1));
	printf("sizeof(iNum):%d\n", sizeof(iNum));
	printf("sizeof(cChar):%d\n", sizeof(cChar));
	printf("sizeof(fFloat):%d\n", sizeof(fFloat));
	printf("sizeof(dDouble):%d\n", sizeof(dDouble));
	return 0;
}

结果是什么呢?

那为什么是这样呢?我们将这些指针的值打印出来试试,代码为:

#include<stdio.h>
#include<string.h>
typedef struct student
{
	int id;
	char name[10];
}stu;
int main()
{
	stu *stu1 = (stu*)malloc(sizeof(stu*));
	int *iNum = (int*)malloc(sizeof(int*));
	char *cChar = (char*)malloc(sizeof(char*));
	float *fFloat = (float*)malloc(sizeof(float*));
	double *dDouble = (double*)malloc(sizeof(double*));
	
	printf("stu1:%p\n", stu1);
	printf("iNum:%p\n", iNum);
	printf("cChar:%p\n", cChar);
	printf("fFloat:%p\n", fFloat);
	printf("dDouble:%p\n", dDouble);
	return 0;
}

 结果为:

可以看出,这些指针变量的值都是十六进制的,算下来有32位,刚好是一个int类型的大小,故均占据了四个字节的大小。

2.通过指针变量可以改变自身所存储的指针所表示的变量的值。

不理解不要紧,我们讲个简单的例子:

#include<stdio.h>
#include<string.h>
int main()
{
	int iNum1, *iNum2;
	iNum1 = 10;
	iNum2 = &iNum1;
	*iNum2 = 11;
	printf("%d", iNum1);
	return 0;
}

iNum1的值是多少呢?结果为:

 已知iNum1的初值为10,将iNum的地址赋给iNum2后,我们通过iNum2来更改iNum2所存储的地址的存储的值。可以说,*iNum2等效于iNum1,等效于*&iNum1。

由于*和&为同一级运算符(这里的*不是指乘号运算符,&也不是指按位与运算符),并且都具有右结合性,说白了就是从右往左结合,先计算&iNum1,再计算*&iNum1。

3.自增或自减操作对于字符数组元素地址不可使用,但对指针变量可以使用。

这个又是怎么一回事呢?这里要提到左值和右值的概念。

按字面意思,通俗地说。以赋值符号 = 为界,= 左边的就是左值,= 右边就是右值。 比如:(1) int b = 3;

(2) int a = b;

第(2)行代码,a为左值,b为右值。

更深一层,可以将 L-value 的 L, 理解成 Location,表示定位、地址。将 R-value 的 R 理解成 Read,表示读取数据。现在的计算机数据放在内存。内存有两个很基本的属性:内存地址和内存里面放的数据。想象完全一样的箱子。每个箱子有个编号,用来区分到底是哪个箱子,箱子里面可以放东西。内存地址相当于箱子的编号,内存的数据,相当于箱子里面放的东西。

变量名编译之后,会映射成内存地址。看看a = b的含义。其实就是 将 "b地址内存里面的数据",放到"a地址内存"中。

那么我们再来看这样一行代码:

#include<stdio.h>
#include<ctype.h>
int main()
{
	int *iInt;
	int cInt[10] = {9,8,7,6,5,4,3,2,1,0};
	iInt = cInt;
	for (int i = 0; i < 10; i++)
	{
		printf("cInt[%d] = %d\n", i, *iInt);
		iInt++;
	}
	return 0;
}

我们在将整型数组 cInt[] 的首地址赋给指针变量 iInt 后,接着我们利用 iInt 的自增操作输出各个数组元素,结果为:

 那我们直接利用数组的首地址呢?同理得:

#include<stdio.h>
#include<ctype.h>
int main()
{
	int *iInt;
	int cInt[10] = {9,8,7,6,5,4,3,2,1,0};
	iInt = cInt;
	for (int i = 0; i < 10; i++)
	{
		printf("cInt[%d] = %d\n", i, *cInt);
		cInt++;
	}
	return 0;
}

显示错误:

 在这里,cInt 是一个不可更改的值,换句话说,这个地址已经是确定的了,是一个左值,是不能对其更改的,而我们之所以能够成功地利用 iInt ,在于 iInt 是一个指针变量,当然是可以改变所存储地址的值的。不过,我们仍可以对这个左值操作来输出各个数组元素。只需:

将printf("cInt[%d] = %d\n", i, *cInt);
        cInt++;

改成printf("cInt[%d] = %d\n", i, *(cInt+i));


欢迎指正我的上一篇博客:​ 一题多解×4(反转数字+完数)  ​

我的下一篇博客:函数(C语言)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

奔走的月光

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

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

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

打赏作者

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

抵扣说明:

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

余额充值