一、什么是指针

指针表示内存地址(如酒店的房间号表示房间的地址)

我们平时说的指针,是保存地址的指针变量

二、定义指针变量

  2.1 格式


#include <stdio.h>
int main(int argc, const char *argv[])
{
    int a,b,c;
    printf("%p\t%p\t%p\n",&a,&b,&c);

    //使用指针变量保存a的地址
    int *p = &a;
    printf("p=%p\n",p); 
    //因为指针变量p的值就是内存地址,所以使用%p格式符打印
    return 0;
}

  2.2 指针的初始化和赋值

指针和变量的关系

指针指向一个变量实际上是指向该变量的首地址

#include <stdio.h>
int main(int argc, const char *argv[])
{
    int *p;     //定义了一个能够保存一个int类型变量地址的指针p
    //上面的p现在是一个野指针(不知道指向的指针)
    
    int a=90;
    //用int类型变量a的地址给指针p赋值
    //让指针p指向变量a
    p = &a;
    /*******************通过指针访问变量的值***********************/
    //拿到变量的地址后,需要对地址解引用访问到地址中的内容
    //可以使用(*地址)解引用
    printf("%d\n",*p);
    *p = 12;   //<==>a=12
    printf("a=%d\n",a);
    //定义指针p1并初始化
    int *p1 = NULL;    //NULL表示空地址,当指针没有明确指向时可以指向NULL
    return 0;
}

  2.3 通过指针的间接访问

直接通过变量名访问变量是直接访问

通过变量的地址(指针)访问变量是间接访问

如何通过指针访问变量:对地址进行解引用(对地址取*)

总结 * 的用法:

1、定义时,表示定义了指针变量

2、解引用时,表示取地址中的内容

PS:如果*前面有数据类型一定是定义指针变量, 如果*前面没有数据类型一定是对地址的解引用

#include <stdio.h>
int main(int argc, const char *argv[])
{
    int *p;     //定义了一个能够保存一个int类型变量地址的指针p
    //上面的p现在是一个野指针(不知道指向的指针)
    
    int a=90;
    //用int类型变量a的地址给指针p赋值
    //让指针p指向变量a
 p = &a;
 //定义指针p1并初始化
    int *p1 = NULL;    //NULL表示空地址,当指针没有明确指向时可以指向NULL
 
    /*******************通过指针访问变量的值***********************/
    //拿到变量的地址后,需要对地址解引用访问到地址中的内容
    //可以使用(*地址)解引用
    printf("%d\n",*p);
    *p = 12;   //<==>a=12
    printf("a=%d\n",a);

    return 0;
}

  2.4 指针的运算

因为指针是一个内存地址,对于指针的大部分运算没有实际意义。

指针常用的操作是加法和减法

#include <stdio.h>
int main(int argc, const char *argv[])
{
    int *p;     //定义了一个能够保存一个int类型变量地址的指针p
    //上面的p现在是一个野指针(不知道指向的指针)
    
    int a=90;
    //用int类型变量a的地址给指针p赋值
    //让指针p指向变量a
 p = &a;
    printf("p=%p\n",p);  //0x3c
    printf("p+1=%p\n",p+1); //0x40,相差四个字节 p+1表示p所指向空间的下一个位置
    printf("++p=%p\n",++p); //++p表示指针变量p本身向后偏移一个字节


    char c;
    char *p1=&c;
    printf("p1=%p\n",p1);  //0x73
    printf("p1+1=%p\n",p1+1); //0x74,相差四个字节

    return 0;
}

PS:

指针的加减运算,表示向后或者向前偏移n个字节。

n由指针的数据类型决定。

  2.5 指针的大小

指针的大小和数据类型无关,指针的数据类型只决定指针的偏移量

指针的大小由操作系统决定,32位操作系统占4Byte,64位操作系统占8Byte

  2.6 指针和一维整形数组

数组的特点:数组名表示数组的首地址

所以指针指向一维数组,可以直接将数组名赋值给指针

#include <stdio.h>
int main(int argc, const char *argv[])
{
    int arr[4]={1,9,34,2};
    /****定义指针p指向一维整形数组****/
    int *p = arr;  //数组名本身就是数组的首地址
    //p = &arr[0];
    printf("%d\n",*p); //*p<==>arr[0]

    //p<==>arr
    //arr[i]<==>*(p+i)
    //p[i]<==>arr[i]
    //*(p+i)<==>*(arr+i)
    //p+i<==>arr+i

    //[i]<==>对地址先偏移i个字节,再解引用(取*)
    printf("p+1=%p\t*(p+1)=%d\n",p+1,*(p+1)); //<==>arr[1]
    printf("arr+2=%p\t*(arr+2)=%d\n",arr+2,*(arr+2)); //<==>arr[2]
    printf("p+3=%p\t*(p+3)=%d\n",p+3,*(p+3)); //<==>arr[3]
    return 0;
}

 
  

#include <stdio.h>
int main(int argc, const char *argv[])
{
    int arr[5]={12,8,59,61,70};
    int *p=arr;
    printf("%p\n",arr);
    printf("%p\n",arr+3);
    //printf("%p\n",p+5);   越界访问,因为数组中只有5个元素
    printf("%d\n",*(p+1)); //8
    printf("%d\n",*p+2);   //14
    printf("%d\n",p[3]);   //<==>*(p+3) 61 
    return 0;
}       

    2.6.1 练习

      2.6.1.1 用指针的形式,完成对数组中元素的输入和输出

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
	int arr[5];
	int *p=arr;
	int i;
	for(i=0;i<5;i++)
	{
		scanf("%d",p+i);
	}
	for(i=0;i<5;i++)
	{
		printf("%d ",*(p+i));
	}
	printf("\n");
	return 0;
}
      2.6.1.2 通过指针,求数组中元素的最大值

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
	int arr[6]={231,195,632,456,245,654};
	int *p=arr;
	int max=*p;
	int i;
	for(i=1;i<6;i++)
	{
		if(*(p+i)>max)
		{
			max=*(p+i);
		}
	}
	printf("max=%d\n",max);
	return 0;
}
      2.6.1.3 定义一个数组,用指针指向该数组并用指针的形式完成冒泡排序。

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
	int arr[6]={156,324,126,489,456,354};
	int *p=arr;
	int i,j,temp;
	for(i=0;i<6;i++)
	{
		for(j=0;j<5;j++)
		{
			if(*(p+j)>*(p+j+1))
			{
				temp=*(p+j);
				*(p+j)=*(p+j+1);
				*(p+j+1)=temp;
			}
		}
	}
	for(i=0;i<6;i++)
	{
		printf("%d ",*(p+i));
	}
	printf("\n");
	return 0;
}
      2.6.1.4 使用指针,实现数组的逆置

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
	int arr[]={1,2,3,4,5,6};
	int *p=arr;
	int len=sizeof(arr)/sizeof(arr[0]);
	int i,j,temp;
	i=0;
	while(i<len/2)
	{
		temp=*(p+i);
		*(p+i)=*(p+len-1-i);
		*(p+len-1-i)=temp;
		i++;
	}
	for(i=0;i<len;i++)
	{
		printf("%d ",*(p+i));
	}
	printf("\n");
	return 0;
}

  2.7 指针和一维字符数组

本质上与指针指向以为整型数组一致,只有数据类型不同

char str[100]="hello";
char *p = str;

str[i]<==>p[i]
*(str+i)<==>*(p+i)
str+i<==>p+i

#include <stdio.h>
int main(int argc, const char *argv[])
{
    char str[100]="hello";
    char *p = str;
    printf("str=%p\n",str);
    printf("str+1=%p\n",str+1);
    printf("p=%p\n",p);
    printf("p+1=%p\n",p+1);

    return 0;
}

    2.7.1 练习

      2.7.1.1 终端输入字符串,如果有大写字母转为小写字母,如果是小写字母转为大写字母,其他字符转为#

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
	char str[100];
	gets(str);
	printf("str=");
	puts(str);
	char *p=str;
	while(*p!='\0')
	{
		if(*p>'A'&&*p<'Z')
		{
			*p+=32;
		}
		else if(*p>'a'&&*p<'z')
		{
			*p-=32;
		}
		else
		{
			*p='#';
		}
		p++;
	}
	printf("str=");
	puts(str);
	return 0;
}
      2.7.1.2 使用指针实现字符串函数族的函数:strlen、strcpy、strcat、strcmp

strlen:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
	char str[]="hello";
	char *p=str;
	int count=0;
	while(*p!='\0')
	{
		count++;
		p++;
	}
	printf("len=%d\n",count);
	return 0;
}

strcpy:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
	/*******strcpy*********/
	char str1[100]="hello";
	char str2[100]="hi";
	printf("str1=");
	puts(str1);
	char *p1,*p2;
	p1=str1;
	p2=str2;
	while(*p1!='\0'||*p2!='\0')
	{
		*p1=*p2;
		p1++;
		p2++;
	}
	*p1='\0';
	printf("str1=");
	puts(str1);

	return 0;
}

strcat:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
	/*******strcat********/
	char str1[100]="hello";
	char str2[100]=" wu yi fan";
	printf("str1=");
	puts(str1);
	printf("str2=");
	puts(str2);
	char *p1,*p2;
	p1=str1;
	p2=str2;
	while(*p1!='\0')
	{
		p1++;
	}
	while(*p2!='\0')
	{
		*p1=*p2;
		p1++;
		p2++;
	}
	*p1='\0';
	printf("str1=");
	puts(str1);
	return 0;
}

strcmp:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
	/*******strcmp*********/
	char str1[100]="hello";
	char str2[100]="hi";
	char *p1,*p2;
	p1=str1;
	p2=str2;
	while(*p1==*p2&&*p1!='\0')
	{
		p1++;
		p2++;
	}
	printf("str1与str2从第一位开始首个不同元素的ASCII码差值为:\n%d\n",*p1-*p2);
	return 0;
}

三、内存分区

栈区:定义变量时,自动申请自动释放

堆区:程序手动申请,手动释放

静态区:用static修饰的关键字都在静态区

PS:指针指向字符串常量区的内容,不能通过指针修改

#include <stdio.h>
int main(int argc, const char *argv[])
{
    char *p="hello world";
    //定义了一个char类型的指针变量p,指向字符串常量hello world
    //根据内存分区,字符串常量被分配在.ro段
    //该分区中的内容不能更改,为只读内容
    
    //*p = 'a';
    printf("1\n");
    p=NULL;  //让指针p指向NULL
    printf("%c\n",*p);  //段错误
    return 0;
}

四、段错误

本质:非法访问内存

1.修改常量区的内容

2.野指针的间接访问(错误不能预知)

3.数组越界(错误不能预知)

4.空指针的间接访问

五、练习

  5.1输入带空格的字符串,删除字符串中的空格

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc, const char *argv[])
{
	char str[100];
	gets(str);
	char *p=str;
	while(*p!='\0')
	{
		if(*p==' ')
		{
			while(*p!='\0')
			{
				*p=*(p+1);
				p++;
			}
			p=str;
		}
		p++;
	}
	puts(str);
	return 0;
}

Logo

开源鸿蒙跨平台开发社区汇聚开发者与厂商,共建“一次开发,多端部署”的开源生态,致力于降低跨端开发门槛,推动万物智联创新。

更多推荐