如果想更详细的了解指针的知识点可以观看我的另外一篇博客。
指针高级:https://blog.csdn.net/qq_46485161/article/details/115037692
内容目录
地址和指针的概念
为了说清楚什么是指针,必须弄清楚数据在内存中是如何存储的,又是如何读取的。
内存区的每一个字节有一个编号,这就是“地址” 。如果在程序中定义了一个变量,在对程序进行编译时,系统就会给这个变量分配内存单元。
1、按变量地址存取变量值的方式称为“直接访问”方式 ,例如:
printf(″%d″,i);
scanf(″%d″,&i);
k=i+j;
2、另一种存取变量值的方式称为“间接访问”的方式。即,将变量i的地址存放在另一个变量中。
在C语言中,指针变量是一种特殊的变量,它是存放地址的。假设我们定义了一个指针变量i_pointer用来存放整型变量的地址,它被分配地址为(3010)、(3011)的两个字节。可以通过语句:i_pointer =&i;
将i的地址(2000)存放到i_pointer中。这时, i_pointer的值就是(2000) ,即变量i所占用单元的起始地址。要存取变量i的值,可以采用间接方式:先找到存放“i的地址”的变量i_pointer ,从中取出i的地址(2000),然后到2000 、 2001字节取出i的值
指针和指针变量
指针和指针变量的定义:
一个变量的地址称为该变量的“指针”。
例如,地址2000是变量i的指针。
如果有一个变量专门用来存放另一变量的地址(即指针),则它称为“指针变量”。
上述的i_pointer就是一个指针变量。
指针变量的值(即指针变量中存放的值)是地址(即指针)。
请区分“指针”和“指针变量”这两个概念。
变量的指针和指向变量的指针变量
1、定义一个指针变量
定义指针变量的一般形式为
基类型 *指针变量名;
int *ptr;
说明
指针变量的类型:指明了该指针指向的内存空间所存储的数据类型。
定义中的“”表示所定义的变量是指针变量。变量名是ptr,而非ptr。
注意:指针变量中只能存放地址(指针),不要将一个整数(或任何其他非地址类型的数据)赋给一个指针变量
给指针赋值
1、通过 & 运算符为指针赋值,例如:
ptr_var = &var;
2、通过另一个指向相同类型数据项的指针变量对指针进行赋值,例如:
ptr_var2 = ptr_var;
3、给指针变量赋值为符号常量NULL
例如:float *ptr_var3=NULL;
说明:NULL是一个空指针,表示该指针变量的值没有意义。作用是为了避免对没有被初始化的指针变量的非法引用。NULL 的定义在“stdio.h”中。
4、通过指针为变量赋值
例如:
int *ptr_var = NULL;
int var = 100;
ptr_var = &var;
*ptr_var = 10; /*等价于var = 10;*/
/*如果ptr_var指向var,则把10赋给var*/
指针运算符
&是一元运算符,它返回操作数的内存地址
例如:
int var=12,*ptr;
ptr = &var;
*是 &的反运算符,它也是一元运算符,返回指针指向的内存位置中的值
例如:
int var=12,*ptr=&var;
int temp = *ptr; //等价于temp=var;
对“&”和“”运算符说明:
(“&”和“”两个运算符的优先级别相同,但按自右而左方向结合)
如果已执行了语句 pointer_1=&a; pointer_2=&a ;
(1) pointer_2 =&* pointer_1 ?
先进行* pointer_1的运算,它就是变量a,再执行&运算。&* pointer_1与&a相同,即变量a的地址,就是将&a(a的地址)赋给pointer_2
(2) &a? &a?
先进行&a运算,得a的地址,再进行运算。即&a所指向的变量,也就是变量a。&a和pointer_1的作用是一样的,它们都等价于变量a。即&a与a等价。
(3) (pointer_1)++ 和 pointer_1++
pointer_1++:++和为同一优先级别,而结合方向为自右而左,因此它相当于(pointer_1++)。由于++在pointer_1的右侧,是“后加”,因此先对pointer_1的原值进行运算,得到a的值,然后使pointer_1的值改变,这样pointer_1不再指向a了。
(*pointer_1)++: *pointer_1相当a,所以整个表达式相当于a++
指针的算术运算
两种形式:指针±整数 或者 指针 - 指针
注:①指针与整型值加减的结果是指针,表示使该指针指向该指针下移或上移存储单元个数(整型值)之后的内存地址。存储单元的大小就是该指针的数据类型所需的内存大小。
②指针与指针的减运算要求相减的两个指针属于同一类型,其结果是整数,表示两个指针之间的数据的个数。
例如:
//假定var存储在地址1000中,因为整数的长度是 4 个字,ptr_var节的值将是1004
int var, *ptr_var;
ptr_var = &var;
ptr_var ++;
指针递增时,将指向其类型的下一个元素的内存位置,反之亦然
指针比较
前提:两个指针都指向相同类型的变量
假设ptr_a和ptr_b分别指向a和b
指针按地址传递
指针可以作为参数
把实参的地址传给形参
允许函数访问内存位置
被调函数能够修改主调程序的参数的值
示例:
#include <stdio.h>
void swap(int *pi1, int *pi2)
{
int temp;
temp = *pi1;
*pi1 = *pi2;
*pi2 = temp;
}
void main()
{
int iNum1, iNum2;
iNum1 = 10;
iNum2 = 20;
swap(&iNum1, &iNum2);
printf(“iNum1=%d,iNum2=%d” ,iNum1,iNum2);
}
指针与一维数组
一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。指针变量既然可以指向变量,当然也可以指向数组和数组元素。
所谓数组的指针(数组的名称)是指数组的起始地址,也就是第一个元素的地址。数组的指针是个常量指针
数组元素的地址可以用两种方式表示:
①在数组元素前面加“&”符号, 例如:&ary[2]
②数组名 + 下标 ,例如:ary + 2
示例:
#include <stdio.h>
int main()
{
static int ary[10] = {
1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int i;
int *p=NULL;
for (i = 0; i < 10; i ++)
{
printf("\ni=%d,ary[i]=%d,*(ary+i)=%d ",i,ary[i],*(ary + i));
printf(" &ary[i]= %X,ary+i=%X",&ary[i],ary+i);
}
for (p = ary; p < ary+10 ; p++)
{
/*能否使用ary++?*/
printf("\naddress:%x,value:%d",p,*p);
}
return 0;
}
数组作参数传递
一维数组元素作参数传递时,按值传递
整个数组(数组名)当参数传递时,按地址传递,有以下三种等价的写法:
void TestArray(char ary[ ]);
void TestArray(char ary[LEN]);
void TestArray(char *ary);
整型数组作为返回值
归纳起来,如果有一个实参数组,想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况:
① 形参和实参都用数组名,如:
void f(int x[],int n)
{
····
}
void main()
{
int a[10];
·····
f(a,10)
}
②实参用数组名,形参用指针变量。如:
void f(int *x,int n)
{
····
}
void main()
{
int a[10];
·····
f(a,10)
}
③实参形参都用指针变量。例如:
void f(int *x,int n)
{
····
}
void main()
{
int a[10],*p=a;
·····
f(a,10)
}
④实参为指针变量,形参为数组名。如:
void f(int x[],int n)
{
····
}
void main()
{
int a[10],*p=a;
·····
f(a,10)
}
指向字符串常量的字符指针
可以声明字符指针直接指向字符串常量,例如char* pStr=“Welcome”;字符指针pStr将指向字符常量“Welcome”。“Welcome”为字符串常量,因此其值不能修改。
可以使用字符指针来存储和访问字符串
声明字符指针语法:char* pStr;
声明字符串:char str[10]=“hello”;
使用字符指针指向字符串:pStr=str;
可以使用字符指针访问字符串,例如pStr[0]=‘a’; 该代码将第一个字符修改为’a’
void main()
{
char* pStr;
char str[10]="hello";
pStr=str;
pStr[0]='a';
printf("pStr = %s\n",pStr);
} ==>pStr = aello
字符数组与字符指针的区别
字符数组作为返回值
#include <stdio.h>
char *print()
{
char acStr[]="hello world";
return acStr; —>返回整个数组(数组首地址)
}
void main()
{
char *pcStr;
pcStr = print();
puts(pcStr);
return;
}
结果:
不能打印hello world原因:
在函数中的数组是局部变量,函数结束系统自动清除。如果此时返回一一个地址,可能会访问到系统下次分配到的数据,如果
修改该位置还可能导致巨大的错误。
这里可以把char acStr[]="hello world"; 改为char *acStr="hello world";