作业:
- 整理思维导图
- 使用数组指针完成二维数组中元素的输入和输出
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, const char *argv[]) { int a[3][3] = {0}; int (*p)[3] = a; int i = 0; int j = 0; for(i = 0;i < 3;i++){ for(j = 0;j < 3;j++){ scanf("%d",*(p+i)+j); } } for(i = 0;i < 3;i++){ for(j = 0;j < 3;j++){ printf("%-2d",*(*(p+i)+j)); } printf("\n"); } return 0; }
- 复习从指针开始的内容
【5】指针的运算
指针保存内存地址,所以指针的某些运算没有意义
指针常见的运算,算术运算:+、-,赋值/混合赋值运算,自增自减,关系运算:判断是否是相同的地址,
指针的偏移量:指的是指针,进行加减运算和自增自减运算时,偏移的字节个数(只和数据类型有关)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, const char *argv[])
{
int a=100;
int *p = &a;
printf("p=%p\tp+1=%p\n",p,p+1); //dc e0,因为指针是int类型,偏移4个字节
printf("--p=%p\n",--p); //让指针p指向前四个字节的空间,向前偏移四个字节
//关系运算==,!=,>,<
int b = 79;
int *p1 = &b;
int *temp; //野指针
if(p==p1)
{
printf("p和p1指向同一片空间\n");
}
else if(p!=p1)
{
printf("p和p1指向不同的空间\n");
}
temp = p; //让temp和p指向相同的空间
p = p1;
p1 = temp;
if(p1==temp)
{
printf("p1和temp指向同一片空间\n");
}
if(p>p1)
{
printf("p1指向的空间在p的前面\n");
}
return 0;
}
【6】大小端存储问题
小端存储:数据低位存放在地址低位,生活中大部分的计算机都是小端存储
大端存储:数据高位存放在地址低位,网络通信
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, const char *argv[])
{
int a = 0x12345678;
char *p = &a; //隐式强转,偏移量是1Byte
//int *p1 = &a; //p1的算术运算,偏移量是4Byte
if(*p==0x78)
{
printf("小端存储\n");
}
else if(*p==0x12)
{
printf("大端存储\n");
}
return 0;
}
【7】一维数组和指针结合
数组名是数组的首地址
[i]先偏移i个偏移量,再解引用*
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, const char *argv[])
{
int arr[5]={19,89,100,8,9};
int *p = arr; //arr本身就是数组的首地址
//指针指向的是数据的首地址
p = &arr[0];
//p+i<==>arr+i
/*for(int i=0;i<5;i++)
{
printf("%d\n",p[i]);
}*/
printf("arr=%p\tp=%p\n",arr,p);
printf("arr+1=%p\tp+1=%p\n",arr+1,p+1);
printf("arr+1=%p\tp+1=%p\n",arr+2,p+2);
printf("&arr[3]=%p\tarr+3=%p\n",&arr[3],arr+3);
//printf("arr++=%p\tp++=%p\n",arr++,p++); //error,arr是一个地址常量不能自增,p虽然只想arr数组,但是p是变量,p++只是让p向后偏移了
printf("%d\n",*(&arr[3]));
printf("%p\n",&(*(p+3))); //*和&,可以互相抵消
printf("%p\n",&arr[3]+1); //&arr[4]
//值:*(p+i),p[i],arr[i],*(arr+i) p[i]只出现在指针指向数组时,通过下标的访问
//地址:p+i,arr+i
//*(p+i)<==>*(arr+i)
//arr[i]<==>p[i] [i]<==>先偏移i个偏移量,再解引用*
return 0;
}
练习:
1、使用指针,完成冒泡排序
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, const char *argv[])
{
int arr[5];
int *p = arr; //定义了一个指针p指向数组arr
//指针变量保存的是内存地址,使用数组中元素的首地址赋值给指针变量p
int i,j;
for(i=0;i<5;i++)
{
scanf("%d",p+i); //p+i表示数组中下标为i的元素的地址
}
int temp;
//冒泡排序
for(i=1;i<5;i++)
{
for(j=0;j<5-i;j++)
{
if(*(p+j)>*(p+j+1))
{
temp = *(p+j);
*(p+j) = *(p+j+1);
p[j+1] = temp;
}
}
}
for(i=0;i<5;i++)
{
printf("arr[%d]=%d\n",i,*(p+i));
}
return 0;
}
2、终端输入字符串,使用指针实现,字符串的逆置
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, const char *argv[])
{
char str[100];
gets(str);
char *p = str;
int len=strlen(str),i;
char temp; //逆置私服穿,需要char类型的temp中间变量
/*for(i=0;i<len/2;i++)
{
//实现字符串的逆置
temp = *(p+i);
*(p+i) = *(p+len-1-i);
*(p+len-1-i) = temp;
}*/
char *end = p+len-1; //尾指针,指向字符串中最后一个字符
while(end>p)
{
temp = *p;
*p = *end;
*end = temp;
p++;
end--;
}
p = str; //重置p,让p重新指向str
puts(p);
return 0;
}
3、终端输入字符串,使用指针,完成删除字符串中的空格
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, const char *argv[])
{
char str[100];
gets(str);
int len = strlen(str);
printf("%ld\n",len);
char *p = str;
int i=0,j=0;
//循环遍历字符串中的每一个字符
while(*(p+i))
{
if(*(p+i)!=' ')
{
*(p+j) = *(p+i);
j++;
}
i++;
}
//拿结尾的'\0'
*(p+j) = *(p+i); //此时*(p+i)=='\0'
//可以直接puts(p)的原因:p没有移动过
puts(p);
return 0;
}
【8】指针和一维字符数组
本质上和指针指向一维整形数组一样,只是数据类型从int变成char
char str[100];
char *p = str;
指针直接指向字符串常量:
char *p1 = "hello world";
char *p2 = "hello world";
//p1和p2指向相同的地址,因为同一个字符串常量,只会在.ro段中存在一份
*(p1+3)='9'; //段错误,修改.ro段的只读内容
内存分区:
练习:
1、使用指针实现,strlen、strcpy、strcat和strcmp。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, const char *argv[])
{
//strcpy功能
char str[50]="";
char str1[]="world";
//定义两个指针指向字符串
char *p = str;
char *p1 = str1;
gets(str);
puts(str);
//strcpy是源字符串中的每一位都拷贝给目标字符串
while(*p1)
{
*p = *p1;
p++;
p1++;
}
*p = *p1;
puts(str);
return 0;
}
//strcmp
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, const char *argv[])
{
//实现strcmp的功能,不需要考虑长度
char str[]="hello";
char str1[]="hqllo";
char *p = str;
char *p1 = str1;
while(*p==*p1&&*p!='\0')
{
p++;
p1++;
}
int ret = *p-*p1;
if(ret>0)
{
printf("str>str1\n");
}
else if(ret<0)
{
printf("str<str1\n");
}
else
{
printf("str==str1\n");
}
return 0;
}
【9】指针和二维数组
一级指针不能指向二维数组的
因为偏移量不一样,一级指针的偏移量:一个数据类型的大小
二维数组的偏移量:一整行元素
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, const char *argv[])
{
int arr[][3]={1,2,3,4};
int *p = arr;
printf("p=%p\n",p); //0x20
printf("p+1=%p\n",p+1); //0x24
printf("arr=%p\n",arr); //0x20
printf("arr+1=%p\n",arr+1); //0x2C
//二维数组的数组名是一个行指针,算数运算时,操作一行元素
//arr[i][j]
return 0;
}
【10】数组指针
本质是一个指针,指向一个数组
数组指针常用于指向二维数组
存储类型 (*数据类型)[数组指针指向的数组的长度] 指针名;
//加()为了表示作为指针的优先级是最高的
二维数组的数组名,是一个行指针,行指针降级一次会变成列指针,列指针是指向单个元素的
数组指针,指向一个数组的空间,也是一个行指针,降级一次变成列指针,列指针是指向单个元素的
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, const char *argv[])
{
int arr[2][3];
int (*p)[3]=arr;
//arr[i][j]
int i=1,j=0;
printf("%p\n",arr[i]);
printf("%d\n",*(p[i]+j)); //arr[i][j]
printf("%p\n",*(p+i));
printf("%p\n",arr[i]+j);
printf("%d\n",*(*(arr+i)+j)); //<==>*(p[i]+j)
printf("%p\n",*p);
printf("%d\n",**p); //arr[0][0]
//[i] <==> 先偏移i个偏移量,再解引用
return 0;
}
【11】指针数组
本质是一个数组,保存的是指针
用于主函数的外部传参
int (*指针名)[二维数组的列数]; //数组指针
存储类型 数据类型 *数组名[长度]; //指针数组
char str[3][9]={"zhangsan","lisi","wangwu"};
//能存下,造成空间的浪费
char *p1="zhangsan";
char *p2="lisi";
char *p3="wangwu";
char *arr[3]={p1,p2,p3};
arr数组名,
char *[3], //能存3个char*的数组
argc和argv[]接收的是,主函数从命令行接收的外部传参,./a.out后面跟的数据
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, const char *argv[])
{
char *p1="zhangsan";
char *p2="lisi";
char *arr[2]={p1,p2}; //定了一个能存2个char*类型的指针数组,并使用p1和p2初始化数组
/*printf("%d\n",argc);
printf("%s\n",argv[0]); //访问argv数组中的第一个元素
*/
int i;
//argc记录外部传参的个数
//argv保存指向外部参数的指针
for(i=0;i<argc;i++)
{
printf("%s\n",argv[i]);
}
return 0;
}
练习:
1、使用命令行的外部传参,实现个位数的加减、乘除运算。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, const char *argv[])
{
//通过命令行传参实现计算器
/*switch(*argv[2])
{
case '+':
printf("%d\n",(*argv[1]-'0')+(*argv[3]-'0'));
break;
case '-':
printf("%d\n",(*argv[1]-'0')-(*argv[3]-'0'));
break;
case '/':
printf("%d\n",(*argv[1]-'0')/(*argv[3]-'0'));
break;
case '*':
printf("%d\n",(*argv[1]-'0')*(*argv[3]-'0'));
break;
}*/
int a = *argv[1]-'0';
int b = *argv[3]-48;
if(0==strcmp(argv[2],"+"))
{
printf("%d\n",a+b);
}
else if(0==strcmp(argv[2],"-"))
{
printf("%d\n",a-b);
}
return 0;
}
【12】atoi
把字符串转换成对应的整形数据
需要导入头文件:
#include <stdlib.h>
int atoi(const char *nptr);
参数:
const char *类型的指针
功能:
把字符串转换成相应的整形
【13】二级指针
一级指针:指向数据,保存数据的地址
二级指针:指向一级指针,保存一级指针的地址