C语言指针与地址概念辨析及指针入门笔记

指针中的误区

关于指针的基础知识

int *p和float *q区别在哪里?

相同点:p和q的大小是一样的。其实任意基类型的指针,大小都没有区别。你要是够无聊强行让p=q,不会有任何精度损失。

不同点:p和q对自己存储的内容理解不同。p显然认为自己存储的是一个int变量的地址,所以会按照int方式来访问这个地址的内容。

C语言中函数名就是一个指针

#include<stdio.h>
int factor(int x);

int main()
{
	int n=1;;
	printf("%d\n",factor);
	printf("%d\n",&factor);
	printf("%d\n",(*factor)(n));
	printf("%d\n",factor(n));

	return 0;
}
int factor(int x)
{
	return x;
}

输出结果:4199845

    4199845
    1
    1

1.factor本身就为地址,值和&factor相同
2.调用函数的方式:–函数名调用 x=factor(n);

         --函数指针调用x=(*factor)(n);

关于指针的定义误区

1.int *p; 为定义一个int类型的指针变量,名字为p。*只是标记p为指针,而非一般int类型的变量

2.关于*的运算

  • int *p=1;(错误写法) 应该int *p=&a;

    p=1;p=2000;(错误写法)因为无法保证1,2000是合法地址

    p只能赋一个确定的地址;

  • p=&a;

    *p=a;

  • 指针运算中:p+1;为增加一个基本型

可以直接把地址当做指针使用

#include <stdio.h>
int main()
 {void swap(int *p1,int *p2);
  int a,b;
  scanf("%d,%d",&a,&b);
  if (a<b)  swap(&a,&b); 
  printf("max=%d,min=%d\n",a,b);
  return 0;
 }
void swap(int *p1,int *p2);
{

}

1.可以直接把地址和指针互换使用(只要代表地址都可以,eg.数组名,函数名,一般变量地址)

2.任何指针作形参的函数,实参可以用地址/指针

指针变量做函数参数运用中的误区

正确写法

int a,b;
void swap(int *p1,int *p2);
if(a>b)
swap(&a,&b);//将a,b中更大的排在前面输出
printf("%d%d",a,b);
return 0;
}
void swap(int *p1,int *p2)
{
    int temp;
    temp=*p1;
    *p1=*p2;
    *p2=temp;
}

错误案例1

void swap(int *p1,int *p2)
{	int *temp;
	*temp=*p1;
 	*p1=*p2;
	*p2=*temp;
}

分析:*p1=a *temp=*p1=a;

​ *temp为指针变量temp指向的变量。

​ 由于未对temp赋初值,所以并不确定temp指向的内存单元。

​ 万一temp指向的内存单元储存着一个重要数据,就会破坏系统的正常运行

错误案例2

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

分析:虽然将x和y的值互换了,但是形参值变化并不改变实参的值

不能通过改变形参指针的值(形参所指向的地址)来改变实参指针的地址,实现改变实参指针所指向地址变量的值

#include <stdio.h>
int main()
{	void swap(int *p1,int *p2);
	int a,b;
	int *pointer_1,*pointer_2;	//pointer_1,pointer_2是int *型变量
	printf("please enter two integer numbers:");
	scanf("%d,%d",&a,&b);
	pointer_1=&a;
	pointer_2=&b;
	if(a<b) swap(pointer_1,pointer_2);
	//调用swap函数,用指针变量作实参
	printf("max=%d,min=%d\n",*pointer_1,*pointer_2);
	return 0;
}
void swap(int *p1,int *p2) 	//形参是指针变量
{	int *p;
	p=p1;				//下面3行交换p1和p2的指向
	p1=p2;
	p2=p;
}
  • p1 p2指向的地址改变,但是实参指指针指向的地址未改变,&a &b未改变。也就是a,b的值并没有发生变化
    • 改变p1 p2的值,并不会影响&a &b和a,b。
    • 但是改变*p1*p2的值,会改变a,b的值,不会改变&a,&b的值

指针在一维数组和二维数组中应用的差别

#include <stdio.h>
int main()
{	int a[3][4]={1,2,3,4,5,6,7,8,9,10,11,12};
    int b[3]={1,2,3};
    printf("%d\n",a);//第0行的首地址---&a[0][0]---a[0]
    printf("%d\n",b);//数组b的首地址---&b[0]
    printf("%d\n",a+1);//第1行的首地址。推广:a+i,第i行的首地址---&a[i][0]
    printf("%d\n",b+1);//&b[1]
    printf("%d\n",*a[1]);//第1行首地址所存储的元素
    //printf("%d\n",*b[1]); 不合法的写法
    printf("%d\n",a[0]);//第0行首地址---&a[0][0]
    printf("%d\n",b[0]);//元素b[0]
    printf("%d\n",*a);//相当于*(a+0),第0行首地址
    printf("%d\n",*b);//元素b[0]
    printf("%d\n",*(a+1));//第1行首地址
    printf("%d\n",*(b+1));//元素b[1]
    printf("%d\n",*(*(a+1)+1));//元素a[1][1]
    printf("%d\n",(*(a+1)+1));//a[1][1]的地址。*(a+1)为地址
    printf("%d\n",(*(b+1)+1));//(b[1]+1)
    return 0;
}
/*
6487520
6487568
6487536
6487572
5
6487520
1
6487520
1
6487536
2
6
6487540
3
*/

易混淆:*a[i]和*(a+i)

  • 在一维数组中 a[i]代表元素 a[i]=*(a+i)
  • 在二维数组中,a[i]代表第i行首地址,*a[i]代表第i行首元素, *(a+i) =a[i]代表第i行首地址

数组指针

优先级顺序:()>[]>*

(int *)p,int *p,int (*p)有什么区别

要讨论p的类型,若p

int p[4],int (*p)[4],int *p[4]有什么区别

int p[4]

  1. 常规数组定义
  2. p为数组首地址,一般不去动
  3. 若一定要动,p++,加的是一个基类型(int)的长度

int (*p)[4]

  1. 数组指针定义
  2. 类型为int (*)[]
  3. p为指向一维数组的指针变量
  4. 常与二维数组连用
  5. p++,为加一个数组的长度

int *p[4]–指针数组定义–类型为int *[]–p为一个一维数组,其中元素为指向整型变量的指针

#include<stdio.h> 
int main()
{
	//一维数组
	int a[5] = { 1, 2, 3, 4, 5 };
	//步长为5的数组指针,即数组里有5个元素
	int (*p)[5];
	//把数组a的地址赋给p,则p为数组a的地址,则*p表示数组a本身
	p = &a;
 
	//%p输出地址, %d输出十进制
	//\n回车
	//在C中,在几乎所有使用数组的表达式中,数组名的值是个指针常量,也就是数组第一个元素的地址,它的类型取决于数组元素的类型。
	printf("%d\n", a); //输出数组名,一般用数组的首元素地址来标识一个数组,则输出数组首元素地址
	printf("%d\n", p); //根据上面,p为数组a的地址,输出数组a的地址
	printf("%d\n", *p); //*p表示数组a本身,一般用数组的首元素地址来标识一个数组
    printf("%d\n",*(p+2));//不合法
    printf("%d\n",p+2);//*(p+2)和p+2的值相同,但*(p+2)+3和p+2+3的值不同
	printf("%d\n", &a[0]); //a[0]的地址
	printf("%d\n", &a[1]); //a[1]的地址
	printf("%d\n", p[0]); //数组首元素的地址
	printf("%d\n", **p); //表示a[0]
	printf("%d\n", (*p)[0]); //表示a[0]
	printf("%d\n", (*p)[1]);//表示a[1]

	//(*p)[i]=*(*p+i);
 
	//将二维数组赋给指针
    int b[3][4]={1,2,3,4,5,6,7,8,9,11,12};
	int(*pp)[4]; 
	pp = b; 
	printf("%d\n",**(pp+1));//b[1][0] 
	printf("%d\n",*(*pp+1));//b[0][1]元素的值
	printf("%d\n",*(*(pp+1)+1));//b[1][1]
	printf("%d\n",b[1][1]);//b[1][1]
	
    return 0;
}

也就是说:

a=p=*p(数值上)–地址

p+2=*(p+2)(数值上)–地址

**p=*a=a[0]

但是**p!=*§//有可能会误解,因为*p和p数值上相等

*p[0]=a[0]–元素

基本形式:(*p)[4]

要注意指针变量的类型,从“int (*p)[4];”可以看到,p的类型不是int *型,而是int (*)[4]型,p被定义为指向一维整型数组的指针变量,一维数组有4个元素,因此p的基类型是一维数组,其长度是16字节。“*(p+2)+3”括号中的2是以p的基类型(一维整型数组)的长度为单位的,即p每加1,地址就增加16个字节(4个元素,每个元素4个字节),而“*(p+2)+3”括号外的数字3,不是以p的基类型的长度为单位的。由于经过*(p+2)的运算,得到a[2],即&a[2][0],它已经转化为指向列元素的指针了,因此加3是以元素的长度为单位的,加3就是加(3×4)个字节。虽然p+2和*(p+2)具有相同的值,但由于它们所指向的对象的长度不同,因此(p+2)+3和*(p+2)+3的值就不相同了。

在一维数组中的应用

#include <stdio.h>
int main()
{	int a[4]={1,3,5,7};		//定义一维数组a,包含4个元素
	int (*p)[4];			//定义指向包含4个元素的一维数组的指针变量中
	p=&a;				//使p指向一维数组
	printf("%d\n",(*p)[3]);	//输出a[3],输出整数7
	return 0;
}

在二维数组中的应用

#include <stdio.h>
int main()
{	int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};		//定义二维数组a并初始化
	int (*p)[4],i,j;			//指针变量p指向包含4个整型元素的一维数组
	p=a;					//p指向二维数组的0行
	printf("please enter row and colum:");
	scanf("%d,%d",&i,&j);	//输入要求输出的元素的行列号
	printf("a[%d,%d]=%d\n",i,j,*(*(p+i)+j));			//输出a[i][j]的值
	return 0;
}

在二维数组中*(a+1)=a[1]—第一行的首地址

用指针将字符串a复制为b,易错点

#include <stdio.h>
int main()
{	char a[]="I am a boy.",b[20],*p1,*p2;
	p1=a;p2=b;
	//p1,p2分别指向a数组和b数组中的第一个元素
	for(;*p1!='\0';p1++,p2++)//p1,p2每次自加1
		*p2=*p1;
		//将p1所指向的元素的值赋给p2所指向的元素
	*p2='\0';	//在复制完全部有效字符后加'\0'
	printf("string a is:%s\n",a);	//输出a数组
	printf("string b is:%s\n",b);	//输出b数组
	return 0;
}

因为将*p1!=‘\0’,作为判断条件,待循环结束,字符串a的结尾不一定有’\0’,所以需要补充一个

如果想要"改变"字符串,用指针或者字符数组,如果只是想要访问,用字符串常量.。
但是用指针,实际上没有改变原字符串存储地址所存储的元素,只是另取一片地址存储新字符串。
*string =" l love China."
string =“i am boby.”
l love China 和 i am boby两个字符串同时存在,和strcpy实现的功能不一样

有关指针的小结

  1. 首先要准确理解指针的含义。“指针”是C语言中一个形象化的名词,形象地表示“指向”的关系,其在物理上的实现是通过地址来完成的。

    &a是变量a的地址,也可称为变量a的指针。

    指针变量是存放地址的变量,也可以说,指针变量是存放指针的变量。

    指针变量的值是一个地址,也可以说,指针变量的值是一个指针。

    指针变量也可称为地址变量,它的值是地址。

    &是取地址运算符,&a是a的地址,也可以说,&是取指针运算符。

    &a是变量a的指针(即指向变量a的指针)。

    数组名是一个地址,是数组首元素的地址,也可以说,数组名是一个指针,是数组首元素的指针。

    函数名是一个指针(指向函数代码区的首字节),也可以说函数名是一个地址(函数代码区首字节的地址)。

    函数的实参如果是数组名,传递给形参的是一个地址,也可以说,传递给形参的是一个指针。

  2. 要区别指针和指针变量。指针就是地址 ,而指针变量是用来存放地址的变量。

  3. 什么叫“指向”?地址就意味着指向,因为通过地址能找到具有该地址的对象。对于指针变量来说,把谁的地址存放在指针变量中,就说此指针变量指向谁。

  4. 并不是任何类型数据的地址都可以存放在同一个指针变量中的,只有与指针变量的基类型相同的数据的地址才能存放在相应的指针变量中。

    int a,*p;		//p是int*型的指针变量,基类型是int型 
    float b;
    p=&a;		//a是int型,合法 
    p=&b;		//b是float型,类型不匹配
    
  5. void *指针是一种特殊的指针,不指向任何类型的数据。如果需要用此地址指向某类型的数据,应先对地址进行类型转换。

  6. 变量定义

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

快苏排序OAO

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

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

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

打赏作者

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

抵扣说明:

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

余额充值