c指针

指针是用来保存内存地址的变量。
定义

type * pvalue;

一、“&”是用来取地址的。

因为pvalue是个指针,他存的是地址,可以把地址赋值给指针,比如,scanf()中的“&”符号,就是用来取地址的,如:

int a=8;
int * pvalue=&a;

习惯上说,pvalue指向了a。

二、“*”是用来解引用的。

也就是获得指针所指向的地址处的数据
对上面的例子

printf("%p",&a);//打印a的地址
printf("%p",pvalue);//打印a的地址
printf("%p",*pvalue);//打印8(pvalue所指向的地址的值)

也可以通过“*”符号对地址所在的值进行赋值
如:

*pvalue=0x1122;//修改pIntValue所指向地址处的内容

三、指针可以用来交换两个数的数据

我们知道,通过形参来进行数据的交换,对实参是不会有影响的。原因是创建一个函数,在内存的栈中,会重新分配空间来保存变量,也就是说,实参和形参在内存中的位置是不同的,他们的内存地址是不一样的。
可以使用指针来交换数据

#include<stdio.h>
void exchange(int * v1,int * v2){
	int temp=*v1;
	*v1=*v2;
	*v2=temp;
}

int main(int argc, char* argv[]){
	int value1=8;
	int value2=5;
	exchange(&value1,&value2);
	printf("%d,%d",value1,value2);
	//纯手打,自己验证
	return 0;
}

四、指针的强制类型转换

C语言中,不同类型的指针之间是不能直接赋值的,否则会报错滴。

(转换的类型)变量;
如:

int * ivalue=NULL;
short a=1;
ivalue=(int *)&a;

五、指针的具体内涵

  1. 指向何处
  2. 解释方式(数据长度、编码方式等)
    也就是说多个指针可以同时指向一个地址,因为他们的解释方式不同,所以解引用得到的值也会不同。
    void*类型的指针
    我们还可以定义void*类型的指针,void指针其实就是没有解释方式
    其他有解释方式的指针可以直接赋值给void指针,
    反过来就不行了。

六、指针的加减

指针的加减和普通的加减有点区别
公式:

指针+数字 =指针地址+ 数字*sizeof(type)

也是好理解的,可以这样想:因为指针是有类型的,他是指向一个同类型的变量的地址,因此,如果他加减,实际上就是指针的移动,而移动一个所变化的值正好是一个这种类型的大小

七、数组作为参数传递时的退化

数组作为参数传递时,其实传递的是数组的首地址,实参数组变为形参时,会退化为指针,也就是说可能会丢失信息。

八、变参函数

变参函数可以通过指针来实现
它的核心技术是:
通过第一个参数的地址,定位到其他所有参数
也就是说我们如果找到了他的第一个参数的地址,其他地址也就找到了
看个例子:
目的:创建一个实现可以任意个数相累加的变参函数
在这里插入图片描述通过调试,可以发现变参函数的数据存储是有规律的,只要找到他第一个参数的地址,那其他的数组也就找到了,因此我们可以用下面的方法来实现我们的需求

#include<stdio.h>
//count为要输入的数据个数
int add(int count, ...){
	int * pvalue = &count;
	int sum = 0;
	for (int i = 0; i<count; i++){
		sum += *(pvalue + 1 + i);
	}
	return sum;
}
int main(int argc, char*argv[]){
	int sum1=add(4, 1, 2, 3, 4);//
	int sum2=add(5, 1, 2, 3, 4, 8);//
	printf("%d,%d",sum1,sum2);
	return 0;
}

九、字符串的两种定义方式的区别

C语言中,对于字符串,实际是一种约定。
C语言中字符串是约定以0x00为结尾的一系列ASCII码码值,在实际的调用中,往往只传递字符串的首地址。

  1. char sayHello[] =“hello world”;
  2. char * sayHello=“hello world”;

第一种:

  • 用char类型的数组来存储hello world,数组是存放在栈空间的

第二种:

  • 用指针的方式来声明字符串,他内部的存储方式 ,“hello
    world”是存放在全局区的而指针的地址还是在栈区,也就是说先将全局区的空间开辟出来并把“hello
    world”赋值到这块空间,然后在返回其首地址给指针。

全局区用来存放字符串的内存属性,默认是只能读,不能写的。
因此以上的两种方式的不同:
指针方式的不可以对其中的值进行重新赋值。

十、与字符串有关的库函数

  • strlen:返回字符串的长度
  • strcat:字符串的拼接
  • strcpy:字符串的复制

十一、指针数组与数组指针

指针数组:其本质是个数组,只不过数组中存放的是指针,举个例子

int * parr[3];

#include<stdio.h>
main(int  argc, char*argv[]){
	int * parr[3];
	int a,b,c;
	parr[0]=&a;
	parr[1]=&b;
	parr[2]=&c;
	return 0;
}

数组指针:本质是个指针,指向一个数组,这个就有点难受了。
int (*pvalue)[3] ,因为“[]”的优先级要高于解引用的符号,因此不加()代表的是数组,加()代表先做指针运算,也就是定义一个int [3]类型的数组指针。
如下:

#include<stdio.h>
int main(int argc, char* argv[])
{
	int iValueAry[3];
	int(*pValueAry)[3] = &iValueAry;//定义一个int[3]类型的指针,指向iValueAry[3]
	printf("%p\r\n", pValueAry);//数组返回的是其首地址
	printf("%p\r\n", iValueAry);//数组返回的是其首地址,与上面的相等
	printf("%p\r\n", iValueAry + 1);//首地址的址+4,因为一个int是4个字节
	printf("%p\r\n", pValueAry + 1);//首地址的址+3*4,因为一个int[3]类型的数组有3个int类型的数,所以是3*4
	return 0;
}

十二、数组指针与二维数组

先上代码

#include<stdio.h>
int main(int argc, char* argv[])
{
	int iValueAry[2][3]={1,2,3,4,5,6};
	int(*pValue)[3] = iValueAry;
	printf("%p\r\n", pValue);
	printf("%p\r\n", pValue + 1);
	return 0;
}

所谓二维数组,其实他的名字就是一个数组指针,可直接将其赋值给上面的pValue,因为是个int[3]数组类型的指针,所以这个指针是指向一个数组(iValueAry),因此若是pValue+1,那就是移动到二维数组的第二行了,所以数组指针也叫做行指针。

#include<stdio.h>
int main(int argc, char* argv[])
{
	int iValueAry[2][3]={1,2,3,4,5,6};
	int(*pValue)[3] = iValueAry;
	printf("%p\r\n", pValue);//打印iValueAry的首地址,因为我们知道,数组名就是数组首地址
	printf("%p\r\n", pValue + 1);//打印首地址+1*3*4,因为pValue的类型是int[3],所以一下是加了3个int类型的字节数,而一个int是4字节,所以是1*3*4
	printf("%p\r\n", pValue + 2);//打印首地址+2*3*4,道理同上
	printf("%p\r\n", *pValue);//因为pValue是一个指针,指向iValueAry,指针解引用就是获取指针所指向地址的数据值,因此对pValue解引用理论上会得到一个int [3]类型的数组,也就是{1,2,3},但是数组是通过首地址来传送的,因此,打印的结果也是其首地址
	printf("%p\r\n", **pValue);//对pValue的第一次解引用得到一个int[3]类型的数组,不过是以首地址的方式返回的,想一下,如果在对其int[3]类型解引用,就会得到数组中的真实数据了
	return 0;
}

==printf("%p\r\n", **pValue); ==只有这个不好理解。
其实可以这样想,pValue是指向一个二维数组的有三个元素的数组指针,第一次对其解引用,得到的是二维数组中的一个3个元素的数组,在进行解引用才是对其元素的真实数据的显示。

十三、用指针来返回多个值

一般来说,函数的返回值只有一个,但运用指针,我们可以在一个函数中带出不止一个参数。
比如,可以指针做为函数的形参,

#include<stdio.h>
void back(int * a,int * b){
	//带出两个值
	*a = 6;
	*b = 1;
}


int main(int argc, char* argv[])
{
	int a;
	int b;
	back(&a,&b);
	printf("%d,%d",a,b);
	return 0;
}

用来带出数据的参数,称为传出参数

十四、通过传出参数带出字符串(二级指针)

错误的实例:

#include<stdio.h>
void FunOutString(char* pOutValue)
{
	char* pszContent  = "Hello, world\r\n";
	pOutValue  = pszContent;
}
int main(int argc, char* argv[])
{
	char* pszValue  = NULL;
	FunOutString(pszValue);
	printf("带出后的结果:%s\r\n", pszValue);
	return 0;
}

输出结果还是NULL。
指针本身是个变量,是存在于栈区的,当程序运行到FunOutString函数时,会在内存中重新开辟pOutValue 参数的空间,这和我们想改变那个值不是同一个,因此是没法改变的。
正确的实例:

#include<stdio.h>
void FunOutString(char** pOutValue)
{
	char* pszContent  = "Hello, world\r\n";
	*pOutValue  = pszContent;
}
int main(int argc, char* argv[])
{
	char* pszValue = NULL;
	FunOutString(&pszValue);
	printf("带出后的结果:%s\r\n", pszValue);
	return 0;
}

pszValue是一个指针,那&pszValue就是取这个指针的地址,其实对于这个东西整体,就是一个二级指针的类型,因为指针本来就是地址,那对地址在取地址当然就是个二级指针了,用二级指针就可以改变pszValue 的地址对应的值了,这个和上面说的通过指针交换两个数的值(第三大模块)有点累似,不好理解可以回去看看。因为要传入一个指针的地址(也就是二级指针),所以形参就是char 双星类型,然后对传入的这个char双星类型的进行一次解引用,就会得到其地址处的值,然后进行赋值就可以了
所谓的二级指针,他的本质是一个指针,只不过,对于其解引用后,得到的还是一个指针。

十五、函数指针

所谓的函数,其实是内存中的一段机器码。 既然函数是内存中的一段机器码,那么函数也就有所谓的首地址的概念。
可以自己试试,函数名打印的结果,是一个(全局区)的地址。
函数名是首地址,那么就可以使用指针变量存放它。
来存放函数地址的指针,就称为函数指针。我们使用函数指针,其实就是用来调用对应的函数。
如:

#include<stdio.h>
void function()
{
	printf("hello");
}
int function2(int x, int y)
{
    return x + y;
}
int main(int argc, char* argv[])
{
	void(*p)();//定义一个函数指针
	p = function;//将指针指向function函数
	(*p)();//对p进行解引用来调用函数
    int(*p2)(int x, int y) = NULL;//指向int返回值,int,int参数的函数
    p2= function2;
	return 0;
}

定义:
函数类型+(*+指针名)+(形参参数类型)

  • 6
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值