比较三个数的大小
1#include<stdio.h>
2int main(){
3 intnum=0,num1=0,num2=0,tmp=0;
4 int*p_num=&num,*p_num1=&num1,*p_num2=&num2;
5 printf("请输入三个数: ");
6 scanf("%d%d%d",&num,&num1,&num2);
7 if(num<num1){
8 tmp=num;
9 num=num1;
10 num1=tmp;
11 }
12 if(num<num2){
13 tmp=num;
14 num=num1;
15 num1=tmp;
16 }
17 if(num1<num2){
18 tmp=num1;
19 num1=num2;
20 num2=num1;
21 }
22 printf("%d%d %d\n",num,num1,num2);
23 return 0;
24 }
使用指针:
1 #include<stdio.h>
2 int main(){
3 int num=0,num1=0,num2=0,tmp=0;
4 int*p_num=&num,*p_num1=&num1,*p_num2=&num2,*p_tmp=&tmp;
5 printf("请输入三个数: ");
6 scanf("%d%d%d",p_num,p_num1,p_num2);
7 if(*p_num<*p_num1){
8 *p_tmp=*p_num;
9 *p_num=*p_num1;
10 *p_num1=*p_tmp;
11 }
12 if(*p_num<*p_num2){
13 *p_tmp=*p_num;
14 *p_num=*p_num1;
15 *p_num1=*p_tmp;
16 }
17 if(*p_num1<*p_num2){
18 *p_tmp=*p_num1;
19 *P_num1=*p_num2;
20 *p_num2=*p_num1;
21 }
22 printf("%d %d%d\n",*p_num,*p_num1,*p_num2);
23 return 0;
24 }
也可以修改指针指的存储区,若num<num1,存储区内容不变,用p_num指针指向存储区内容数大的,用p_num1指向存储区内容数小的
7 if(*p_num<*p_num1){
8 p_tmp=p_num;
9 p_num=p_num1;
10 p_num1=p_tmp
11 }
12 if(*p_num<*p_num2){
13 p_tmp=p_num;
14 p_num=p_num2;
15 p_num2=p_tmp
16
17 }
18 if(*p_num1<*p_num2){
19 p_tmp=p_num1;
20 p_num1=p_num2;
21 p_num2=p_tmp
22
23 }
指针和变量之间的捆绑关系会随着程序的执行不断变化,可以把指针看做变量的某种身份,利用指针可以针对身份编写程序
当把指针和数组中第一个存储区捆绑好以后,就可以通过这个指针找到数字里的每个存储区,此时可以认为指针间接捆绑了整个数组
可以在这种指针后面使用下标来表示数组里的存储区
1#include<stdio.h>
2int main(){
3 int arr[]={1,2,3,4,5},num=0;
4 int *p_arr=arr;
5 for(num=0;num<=4;num++){
6 printf("%d",arr[num]);
7 printf("%d ",p_arr[num]);
8 }
9 printf("\n");
10 return 0;
11 }
地址数据只能参与如下几个计算过程:
地址+整数 地址-整数 地址-地址
地址数据加减整数n实际加减的是n个捆绑存储区的大小
1#include<stdio.h>
2int main(){
3 int arr[]={1,2,3,4,5},num=0;
4 int *p_arr=arr;
5 printf("arr是%p\n",arr);
6 printf("arr+3是%p\n",arr+3);
7 return 0;
8}
地址数据相减结果是一个整数,这个整数表示两个地址之间包含的存储区个数
printf("&arr[3]-arr是%d\n",&arr[3]-arr); 结果是3
数组中第一个存储区的地址加下标可以得到下标对应存储区的地址
8 printf("*(arr+3)是%d\n",*(arr+3));
9 printf("*(p_arr+3)是%d",*(p_arr+3)); 执行结果一样
所有跨函数使用存储区都是用指针实现的
数组做形式参数的时候,真正的形式参数是一个指针变量
练习:编写函数生成彩票,主函数负责显示彩票内容,用指针
1#include<stdio.h>
2#include<stdlib.h>
3#include<time.h>
4void creat(int *p_arr,int size)
5 int *p_tmp=NULL;
6 for(p_tmp=p_arr;p_tmp<=p_arr+size-1;p_tmp++){
7 *p_tmp=rand()%36+1;
8 }
9 }
10int main(){
11 int arr[7]={0};
12 int *p_tmp=NULL;
13 srand(time(0));
14 creat(arr,7);
15 for(p_tmp=arr;p_tmp<=arr+6;p_tmp++){
16 printf("%d ",*p_tmp);
17 }
18 return 0;
19}
练习:编写函数把主函数里的两个变量的内容做交换
1#include<stdio.h>
2void swap(int *p_num,int *p_num1){
3 int tmp;
4 tmp=*p_num;
5 *p_num=*p_num1;
6 *p_num1=tmp;
7 }
8int main(){
9 int num=1,num1=2;
10 printf("num=%d,num1=%d\n",num,num1);
11 swap(&num,&num1);
12 printf("num=%d,num1=%d\n",num,num1);
13 }
函数可以提供指针类型存储区记录地址数据返回值,采用这种方法可以让调用函数使用被调用函数的存储区
绝不可以把局部变量的地址作为返回值使用 (因为局部变量的生命周期是被调用函数工作期间,调用结束,归还存储区。所以必须使局部变量是静态的,因为静态局部变量的生命周期是直到整个程序结束)
1#include<stdio.h>
2int *read(void){
3 static int num=0;
4 printf("请输入一个整数: ");
5 scanf("%d",&num);
6 return #
7 }
8int main(){
9 int *p_num=read();
10 printf("%d",*p_num);
11 return 0;
12 }
练习:编写函数,找出主函数中数组里最大的数,用指针
1#include<stdio.h>
2int *max(int *p_num,int size){
3 int *p_tmp=NULL,*p_max=NULL;
4 //永远和处理过的存储区中最大的数字所在的存储区捆绑
5 for(p_tmp=p_num;p_tmp<=p_num+size-1;p_tmp++){
6 if(p_max==0||*p_max<*p_tmp){
7 p_max=p_tmp;
8 }
9 }
10 return p_max;
11 }
12int main(){
13 int arr[]={1,0,3,8,12,13,5};
14 int *p_num=max(arr,5);
15 printf("%d\n",*p_num);
16 return 0;
17 }
若返回值是地址,定义函数时就需要加*表示指针(地址)类型
int*p_num=max(arr,5); 等同于 int *p_num; p_num=max(arr,5);
声明指针变量时可以使用const关键字
声明指针变量时可以把const关键字写在类型名称前。作用是不可以通过这种指针对捆绑的存储区赋值,但是可以对指针本身做赋值。 例:
4 int *p_num=#
5 *p_num=10; 可以
1#include<stdio.h>
2int main(){
3 int num=0;
4 const int *p_num=#
5 // *p_num=10; 错误
6 p_num=NULL;
7 return 0;
8}
所有用来实现跨函数使用存储区的指针形式参数都尽量采用以上方式加const关键字
(加个记号,告诉调用函数不会改)
也可以在声明变量的时候把const关键字写在指针变量名称前。作用是不可以对这种指针做赋值,但是可以通过这种指针对捆绑的存储区做赋值
1#include<stdio.h>
2int main(){
3 int num=0;
4 int * const p_num1=#
5 *p_num1=10;
6 p_num1=NULL; // 错误
7 return 0;
8 }
声明指针变量时可以使用void作为类型名称
这种指针可以和任意类型的存储区捆绑,这种指针叫无类型指针
不能通过这种指针知道捆绑存储区的类型
1#include<stdio.h>
2int main(){
3 char ch='o';
4 int num=10;
5 float fnum=4.3f;
6 void *p_v=NULL;
7 p_v=&ch;
8 p_v=#
9 p_v=&fnum;
10 return 0;
11 }
不能在这种指针前直接使用*操作符,也不能用这种指针直接进行加减整数计算
这种指针必须首先强制类型转换成有类型指针才能使用
1#include<stdio.h>
2int main(){
3 char ch='o';
4 int num=10;
5 float fnum=4.3f;
6 void *p_v=NULL;
7 p_v=&ch;
8 printf("%c\n",*(char *)p_v);
9 p_v=#
10 printf("%d\n",*(int *)p_v);
11 p_v=&fnum;
12 printf("%g\n",*(float *)p_v);
13 return 0;
14 }
这种指针通常作为函数的形式参数使用,可以通过它把任意类型的存储区传递给调用函数
C语言里规定文字信息必须记录在一组连续的字符类型存储区里
所有文字信息的最后必须使用 ‘\0 ‘ 字符结束,这个字符的ASCII码就是0
我们把符合以上两个特征的内容叫字符串,字符串就可以用来在C语言中记录文字信息
只有 ‘\0’ 前面的内容才被看做是有效字符
所有字符串都可以采用字符类型指针表示
字符串字面值是一种字符串,用双引号中间的一组字符表示,例如“abc”,“^*&”等
编译器在编译的时候会自动在字符串字面值的最后加’\0’字符
编译器在编译时会把字符串字面值替换成第一个字符所在存储区的地址
1#include<stdio.h>
2int main(){
3 printf("%p\n","abc");
4 printf("%c\n",*"abc");
5 printf("%d\n",*("abc"+3));
6 return 0;
7 }
字符串字面值里的所有字符在程序执行过程中不可以被修改
程序中多个内容一样的字符串字面值在内存里只有一个
3 printf("%p\n","abc");
4 printf("%p\n","abc");
结果:
0x8048524
0x8048524
多个并列的字符串字面值会被编译器合并成一个
字符数组也可以用来记录字符串
只有包含 ‘ \0’ 字符的字符数组才能当字符串使用
可以使用字符串字面值对字符数组进行初始化,编译器会把字符串字面值里的’\0’字符初始化到字符数组里
3 char str[]="abc";
4 printf("sizeof(str)是%d\n",sizeof(str)); 结果为4
字符数组里的字符串可以修改
使用%s做占位符把字符串里的所有字符显示在屏幕上 printf("%s\n","abcdef");
练习:编写函数把一个数组里所有存储区的内容前后颠倒,用指针编写
1 2 3 4 5
5 4 3 2 1
1#include<stdio.h>
2void reverse(int *p_num,int size){
3 int *p_head=p_num;
4 int *p_tail=p_num+size-1;
5 int tmp=0;
6 while(p_head<p_tail){
7 // 交换配对存储区的内容
8 tmp=*p_head;
9 *p_head=*p_tail;
10 *p_tail=tmp;
11 p_head++; // p_head指针向后移动一步
12 p_tail--; // p_tail指针向前移动一步
13 }
14 }
15int main(){
16 int arr[]={1,2,3,4,5};
17 int i=0;
18 reverse(arr,5);
19 for(i=0;i<=4;i++){
20 printf("%d ",arr[i]);
21 }
22 printf("\n");
23 return 0;
24 }