指针

本文深入讲解了计算机编程中指针的概念及应用,包括指针的基本操作、数组与指针的关系、多级指针的使用,以及如何利用指针进行数组排序、求最大值等常见操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

计算机的内存最小单位是BYTE,每一个BYTE的内存都有一个唯一的编号,这个编号就是内存地址,编号在32位系统下是32位的整数,在64位系统下是64位的整数。

1、指针的含义与定义

指针变量指向的是一个地址,而不是一个整数(浮点)

例如:int a= 0;int b = 10;char buf[10];

printf(“%p,%p,%p\n”,&a,&b,buf);//输出的是a,b的地址和buf的首地址。

int *p = &a;//int *p;p = &a;//得到变量a的地址,将这个地址赋值给变量p

printf(“%X\n”,p);//地址虽是一个整数,但地址是一个特殊的整数,是不能直接通过整数来操作的

int* p1;//定义一个变量,名字叫p1,它可以指向一个int的地址

p1 = &b;//指针变量的值一般不能直接赋值一个整数,而是通过取变量地址的方式赋值。

//*p的意思是指针变量指向内存的内容(就是整数值)

int c = *p;

printf(“%d,”,c);//0

*p = 10;

printf(a = %d \n,a);//10该变了a之前的值

printf(%u,%u,%u,%u,buf,&buf[1],&buf[2],&buf[[3]);//取数组的前4个地址

 

char c = 0;

char* p2 = &c;//p2指向的是一个地址该地址为c的地址(&c)

printf(“%d\n”,sizeof(p));//4  指针sizeof都为4

printf(“%d\n”,sizeof(p2));//4  指针sizeof都为4

void* p3;//这个叫无类型指针,意思是这只是一个指针变量,而不是指向任何具体的数据类型

p3 = NULL;//将指针赋值NULL,值为NULL的指针我们俗称空指针

NULLC语言中的定义为(void*)0  空指针与野指针的区别,指向NULL的指针叫空指针,没有具体指向任何变量地址叫野指针。

 

int *p;

*p = 1;

printf(%d\n,*p);//p就是一个野指针,程序中要避免野指针的存在,因为野指针是导致程序崩溃的主要原因,空指针是合法的,但野指针是违法的。

指针与数组的关系

l 指针的兼容性

不能从这种类型转换成其他类型,原则上一定是相同类型的指针指向相同类型的变量地址,不能用一种类型的指针指向另一种类型的变量地址

比如:char b = 4;

int * p = &b;

printf(“%d\n”,*p);//为随机值,不是b的值,下面会弹出提示,说类型不一致

float a = 3.14;

int i = a;//自动数据类型转化,将浮点数的小数部分舍弃

int * p = &a;

printf(“%d\n”,*p);//严重的错误,因为指针类型不一致

printf(“%d\n”,i);//3

 

char buf[10] = {0,1,2,3,4};

char* p = buf;//等价与&buf[0]

char* p1 = &buf[1];

char* p2 = &buf[2];

char* p3 = &buf[3];

 

printf(“%d,%d,%d,%d”,p,p1,p2,p3);//打印出各个指针的地址

 

*p2= 7;//数组第3个元素值为7

p3 += 1;

*p3 = 100;//数组的第5个元素值100(下标为4的元素)

 

for(int i = 0;i <10;i++){//循环改变每个值

*p = i;

p++;

}

//如果中间不加什么,这两个for循环会导致崩溃

//p已经指向的数组的最后一个成员啦

p = buf ;//p的值重新指向了数组的首元素  p -= 10;也可以

 

for(int i = 0;i <10;i++){//循环改变每个值

*p = i * 10;

p++;

}

 

int a = 0x12345678;

char*p = &a;//这违反了指针的兼容性,容易造成数据的丢失

p++;

printf(“%x\n”,a);//56,如果没有p++,就是78,如果再加一个p++,就是34  如果指针不兼容的话,*p只能看到其中的一部分,不能看到所有的值

指向常量的指针与常量指针

const char*p;//定义一个指向常量的指针

char* const p;//定义一个指针常量,一旦初始化之后其内容不可改变

例如:

int a = 10;

//指向常量的指针

const int *p = &a;//p这个指针只能指向一个变量

//*p = 20;//不能通过*p的方法修改一个const指针

a = 20;

printf(“%d\n”,*p);//20

//常量指针

int b = 30;

int* const p =&a;//定义一个常量指针,可以通过常量指针修改或者读取一个变量的值

*p = 20;

//p = &b;//错误,常量指针一旦定义了,那么就不能修改其指向的变量  就是定义了a,就不能定义了b

printf(“%d”,a);//20

指针操作实现计算字符串长度以及合并字符串

char s1[100] = ”hello”;

char s2[100] = “world”;

 

//用指针来求一个字符串的长度,不可以使用数组下标的方式

//用指针来将s1s2合并为一个字符串,结果放入s1中,不可以使用数组下标的方式

char* p1 = s1;

int len = 0;

while(*p1){//*p1的内容为0的时候,表示字符串结束了

p1++

len++;

}

printf(“%d\n”,len);//5

 

//s1s2总共的字符串长度

char* p2 = s2;

while(*p2){

*p1 = *p2;//s1的最后开始,从s2的首元素开始

p2++;

p1++;

//上面三句可以用*p1++ = *p2++;

}

printf(“s1 = %s\n”,s1);//helloworld

指针的运算

指针运算不是简单的整数加减法,而是指针指向的数据类型在内存中占用字节数作为倍数的运算。

char* p;

p++;//移动了sizeof(char)这么多的字节数

int*p1;

p1++;//移动了sizeof(int)这么多字节数

 

int buf[10];

int *p1 = &buf;

int *p2 = &buf[1];

 

int*pp1 = &buf[1];

int*pp2 = &buf[3];

printf(%d\n,pp2 - pp1);//2 =4(int)/4(int)*2(下标距离)两个指针相减可以得到两个数组元素的相对距离

printf(%d\n,(int)pp2 - (int)pp1);//8

 

char*pp1 = &buf[1];

char*pp2 = &buf[3];

printf(%d\n,pp2 - pp1);//8 =4(int)/1(char)*2(下标距离)两个指针相减可以得到两个数组元素的相对距离

long long*pp1 = &buf[1];

lonog long *pp2 = &buf[3];

printf(%d\n,pp2 - pp1);//1=4(int)/8(long long)*2(下标距离)两个指针相减可以得到两个数组元素的相对距离

 

short *pp1 = &buf[1];

short *pp2 = &buf[3];

printf(%d\n,pp2 - pp1);//4=4(int)/2(short)*2(下标距离)两个指针相减可以得到两个数组元素的相对距离

 

在地址中,int类型数组相邻的地址相差一个int4)字节,short2)个字节,char(1)个字节,long long(8)个字节

 

3、指针实现数组逆置以及求数组最大元素

//使用指针实现冒泡排序

int buf[10] = {12,45,13,45,143,19,15,30,20,13};

for(int i = 0;i < 10;i++){

for(int j= 1;j < 10-j;j++){

if(*(s+j) < *(s+j - 1)){

int temp = *(s + j);

*(s+j) = *(s+j - 1);

*(s + j -1) = temp;

}

}

}

for(int k = 0;k < 10;k++){

printf(“%d\n”,*(s + i));//从小大排序

}

 

//使用数组来求出数组中的最大值

int value = buf[0];

for(int i = 1;i < 10;i++ ){

if(value < s[i]){

value = s[i];

}

}

printf(“%d\n”,value);//143

 

//使用指针来求出数组中的最大值

int value = *s

for(int i = 1;i < 10;i++ ){

if(value < *(s+i)){

value =*(s+i);

}

}

printf(“%d\n”,value);//143

 

//数组顺序倒转(数组实现)

int low = 0;

int high= 9;

while(low < high){

int temp = buf[low];

buf[low] = buf[high];

buf[high] = temp;

low++;

high--;

}

for(int k = 0;k < 10;k++){

12,45,13,45,143,19,15,30,20,13

printf(%d\n,buf[i]);//13,20,30,15,19,143,45,13,45,12

}

//数组顺序倒转(指针实现)

int *start = &buf[0];

int *end =&buf[9];//或者int* end = start + 9;

while(start > end){

int temp = * start;

*start = *end;

*end = temp;

start++;

end--;

}

for(int k = 0;k < 10;k++){

12,45,13,45,143,19,15,30,20,13

printf(%d\n,*(s+i));//13,20,30,15,19,143,45,13,45,12

}

指针实现计算数组第二大元素

 第一步定义两个变量,分别为最大max和第二大smax,第二步先默认数组中第一个和第二个元素分别为最大和第二大。当遇到大于max的元素smax = max,max= 大于max的元素,如果遇到小于max,但大于smax的元素,那么就让smax等于该元素。

int buf[10] = {12,16,17,18,13,15,14,20,11,22};

//先用数组方式实现

//先比较第一个和第二个

int max = 0;

int s_max = 0;

if(s[0] > s[i]){

max = s[0];

s_max = s[1];

}else{

max = s[1];

s_max = s[0];

}//max等于s[0]s[1]中大的那个元素的值

int i;

for(i =2;i < 10;i++){//从第3个元素开始遍历数组

if(max < s[i]){//如果遇到大于max的元素,那么让s_max等于max,max等于这个元素

s_max = max;

max = s[i];

}else if(max > s[i] && s[i] > s_max){//如果这个元素是介于maxs_max之间,那么就让这个元素等于s_max

s_max = s[i];

}

}

printf(“%d”,s_max);//20

 

//用指针方式实现

//先比较第一个和第二个

int max = 0;

int s_max = 0;

if(*s > *(s + 1)){

max = *s

s_max = *(s + 1);

}else{

max = *(s + 1);

s_max = *s;

}//max等于s[0]s[1]中大的那个元素的值

int i;

for(i =2;i < 10;i++){//从第3个元素开始遍历数组

if(max < *(s + i)){//如果遇到大于max的元素,那么让s_max等于max,max等于这个元素

s_max = max;

max = *(s + i);

}else if(max > *(s + i) && *(s + i) > s_max){//如果这个元素是介于maxs_max之间,那么就让这个元素等于s_max

s_max = *(s + i);

}

}

printf(“%d”,s_max);//20

指针实现汉字字符串逆置

实现字母逆置

char str[100] = “you”;

char* str_start = &str[0];

char+ str_end = &str[strlen(str ) - 1];

while (str_start < str_end){

char* temp = *str_start;

*str_start = *str_end;

*str_end = temp;

str_start++;

str_end--;

}

printf(“%s\n”,str);

实现汉字逆置

//对于VS的汉字是GBK的编码,一个汉字2个字节,对于QT汉字是UTF8编码,一个汉字是3个字节

char str[100] = “你好我也好”;

short* str_start = &str[0];

short* str_end = &str[strlen(str ) - 2];

while (str_start < str_end){

short* temp = *str_start;

*str_start = *str_end;

*str_end = temp;

str_start++;

str_end--;

}

 

printf(“%s\n”,str);

指针的复习1

1、屏蔽错误

在头文件加入#pragma warning(disable::错误编码)//错误编码如4470

2、野指针不能用来判断真假,否则会报错

3、计算数组和指针的sizeof

int buf[10] = {1,2,3,4,5,6,7,8,9,10};

int* p = buf;

printf(“%d,%d\n”,sizeof(p),sizeof(buf));//4(指针类型)  40=4*10(数组类型)

for(int i = 0;i< 10;i++){

printf(“%d”,buf[i]);//p[i]或者*(p+i)等价于buf[i]  C语言允许指针通过数组下标的方法访问数组成员

}

 

指针数组以及多级指针

1int *a[10];//定义了一个指针数组,一共10个成员,其中每个成员都是int*类型。

printf(“%d,%d\n”,sizeof(a),sizeof(a[0]));//40 4

short*b[10];//定义了一个指针数组,一共10个成员,其中每个成员都是short*类型。

printf(“%d,%d\n”,sizeof(b),sizeof(b[0]));//40 4

2、int a = 10;

int *p = &a;

int **p;

pp = &p;//定义了一个二级指针,指向了一个一级指针的地址

**pp = 100;//通过二级指针修改内存的值  通过**pp访问a,通过*pp访问p

//*pp = 10;//相当于将p指向了编号为10的这块内存,pp还是正常的指针,但p被修改成了野指针//通过*p访问a

printf(“a = %d\n”,a);//100

int ***ppp = &pp;

***ppp = a;

printf(“a = %d\n”,a);//0

指向多维数组的指针

 

1、int buf[2][3] = {{1,2,3},{4,5,6}};

//int* p[3];//指针数组

int(*p)[3];//定义了一个指针,指向int[3]这种数据类型,指向二位数组的指针

p =buf;//p指向了二维数组的第一行

printf(“%d\n”,sizeof(p));//4

printf(“%d,%d\n”,p,p+1);//位移了1 * sizeof(int * 3) = 12

 

//打印二维数组

for(int i = 0;i < 2;i++)

{

for(int j = 0;j < 3;j++){

printf(%d\n,buf[i][j]);//123456可以用*(*(p + i) + j)或者p[i][j]代替

}

}

二维数组:

int buf[3][5]

二维数组名称,buf表示数组首地址

int(*a)[5]

定义一个指向int[5]类型的指针变量a

a[0],*(a + 0),*a

0行,0列元素地址

a + 1

第一行首地址

a[1],*(a + 1)

第一行,0列元素地址

a[1] + 2,*(a + 1) + 2,&a[1][2]

第一行第二列元素地址

*(a[1] + 2),*(*(a + 1) + 2),a[1][2]

第一行第二列元素的值

const保护函数参数以及返回值为指针的函数

 

 1、函数的参数可以是指针类型。它的作用是将一个变量的地址传送给另一个函数。通过函数的指针参数可以间接的实现形参修改实参的值。

 

经常用指针访问数组

int main(){

int a = 10;

test(&a);//test变量n指向的是一个地址

//scanf(“%d”,&i);//所有需要在函数内部修改实参的值,都需要通过将指针作为函数参数调用实现

 

int b = 10;

int c = 20;

swap(&b,&c);//swap(b,c)是不可行的

printf(“%d%d”,b,c);//20 10

 

int buf[10] = {1,2,3,4,5,6,7,8,9,10};

printf(“buf = %d”,sizeof(buf));//

set_array(buf);//放的是数组的首地址

print_array(buf,sizeof(buf)/sizeof(int));

 

int *p = &a;

*p = 10;

printf(“%d”,*p);//10

//很少直接用指针指向一个变量,然后通过*方式访问变量

return 0;

}

void test(int *n)

{

(*n)++;

}

//交换

void swap(int* a,int * b){

int temp = *a;

*a = *b;

*b = temp;

}

void set_array(int buf[])//数组名代表数组的首地址 int* buf等价于int buf[]当数组名作为函数的参数时,数组名其实就是一个指针变量一维数组名作为函数参数时,C语言将数组名解释为指针

{

printf(“buf = %d”,sizeof(buf));

buf[0] = 100;

buf[1] = 200;

//*buf = 100

//buf++;

//*buf = 200;

}

void print_array(int * buf,int n){

int i ;

for(i = 0;i < n;i++){

printf(“buf[%d] = %d”,i,buf[i]);

}

}

返回数组指针

因为数组不能被拷贝,所以函数不能返回数组。不过,函数可以返回数组的指针或者引用。要想定义一个返回数组的指针或引用比较繁琐,所以最直接的方法是使用类型别名。

tydedef int arrT[10];//arrT是一个类型别名,它表示的类型是含有10个整数的数组

using arrT = int[10];//arrT的等价声明

arrT* func(int i);//func返回一个指向含有10个整数的数组的指针。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值