目录
指针的强转问题:
const问题:
const和一级指针的结合问题:
const放在一级指针前面进行修饰(限制指针变量指向的数据):
const放在一级指针后面进行修饰(限制指针变量本身):
const对一级指针的前面和后面均进行修饰(限制指针变量和指针变量指向的数据的值):
const与二级指针结合的问题:
const放在二级指针之前:
const放在二级指针之后:
const放在二级指针之间:
作业:
作业一:利用指针实现strcpy(拷贝函数):
作业二:利用指针实现strlen(计算字符串长度)函数:
作业三:里用指针实现strcat(链接字符串)函数:
作业四:利用指针实现strcmp(字符串比较)函数:
今天对指针进行了更加深入地学习,回顾一下今天所讲的知识点。
#include
int main()
{
int ar[] = {1,2,13,14};
int *p = ar;//p指针现在是整形类型大小为4字节的指针变量
long long *l = (long long*)p; //在这里我通过long long将p指针的大小强转位8字节
printf("%lld",*l);
return 0;
}
在这段代码中,我定义了一个元素个数为4的数组,定义了一个整形类型的指针变量,在下面我又通过long long对整形类型的指针变量进行强转,强制将指针的大小从原来的4字节变为8字节。我的计算机是以小端存储的方式来进行存储的,即是最低地址存放最低字节,所以数据地址的填充如下图所示:
当我执行p++语句时,语句蕴含的意思应该是:p+sizeof(p),也就是p指针每向后偏移一个单元格,所对应的字节大小应该是4,那当我执行l++语句时(l+sizeof(l)),即应该是l指针每向后偏移一个单元格,所对应的字节大小应该是8字节。
在这里先提前说明,对于栈来说,开辟内存的方式是和数组不一样的:
栈的内存开辟:从高地址向低地址进行内存开辟
数组内存开辟:从低地址向高地址进行内存开辟
在这段代码中,定义的均为局部变量,所以操作系统会在栈区进行开辟,数组包含4个整形值,栈顶指针向上偏移16个字节,与栈底指针相差16字节,这也就是ar所使用的内存区间。此时ar所指向首元素地址,位于栈顶指针所在处。
栈的分派方式
先来了解const:const是一个C语言的关键字,const限定一个变量不允许被改变,产生静态的作用。很多时候使用const可以提高程序的安全性和可靠性。
如果在这里我定义一个使用const修饰的值,并试图对这个值进行修改。
编译器出现了报错,因为这时max已经被const所修饰,不能被改变.
在这里我将const放在指针前面修饰,但在执行第6行代码的时候现世报错,编译器提示变量必须是可修改的左值。
下面来分析报错的原因,在const语句中,const限制了指针变量指向的数据,也就是修饰了解引用的过程。在之前的文章:指针的学习 中提到,“*”属指针的操作符,作用是引用指针指向的变量值,引用该变量的地址,然后再通过*解开,此过程称之为解引用。那么现在使用const对*p进行了修饰,也就意味着这个解引用的过程不能再变,指针p和a产生关联,p存放了a的地址,此时*p就是a本身,所以通过解引用的方式试图对a值修改的语句是会报错的。
所以可以得出结论:const修饰p的指向内容,内容不能做修改。
现在将const放在指针后面修饰,在执行第七行代码的时候报错,编译器依旧显示变量必须是可修改的左值。
分析报错的原因:在第5行代码中:const直接修饰了指针本身,也就是p存放a的地址的过程,此时p本身代表的就是a的地址,那么当我们试图更改p中所存放的地址时,自然就会报错了。
所以可以得出结论:当const修饰指针p本身时,指针p的值(也就是p所存放的地址)不能被修改。
现在将const分别放在指针的前面和后面,执行第六行和第七行的时候编译器报错,报错原因:变量必须是可修改的左值。
分析报错的原因: 在第五行代码const对p变量本身和p变量所指向的内容均进行了修饰,也就是对解引用的过程和p存放a地址的过程均进行了修饰,所以p和*p都是不可被修改的,我们试图更改这两个值,编译器就会报错。
总结:区分const是限制指针变量本身还是指针变量所指向的值:如果const位于*号的左侧,则const用来限制指针变量所指向的值,如果const位于*号的右侧,则const用来限制指针变量本身。
这里同样有三种结合方式:
const int **q;
在这种方式下,const修饰的是int类型的,所以解引用**q等价的值是不能被赋值的,但是可以*q可以被修改地址,在这里二级指针q本身也可以被修改赋值。
int **const q;
在这种方式i下,const修饰的是q二级指针本身,也就是const修饰int**类型,所以q指针是不能被修改赋值的,但是*q和**q均可以被修改赋值。
int *const *q;
在这种方式下,const修饰的是*q,也就是int *类型,所以*q是不能被赋值的,但是q指针本身和**q均可以被修改赋值。
#include
#include
#include
void my_strcpy(int *ar,int *br,int begin_index,int m)//形参分别为:被复制的数组ar,新数组br,元素下标,数组元素个数
{
assert(ar != nullptr && br != nullptr && begin_index >= 0);
int *p = ar + begin_index;//p指针的初始位置是被复制数组ar的起始元素;
int *q = br + begin_index;//q指针的位置也是br数组的起始元素;
assert(p != nullptr && q != nullptr);
for(int i = 0;i < m;i++){
*q = *p;//将p指针对应的地址解引用后的元素赋值给q指针对应地址解引用的元素,循环;
q++;
p++;//每次赋值完之后,p指针和q指针的位置向后偏移一位,直到全部复制完成;
}
}
int main()
{
int m = 0;//定义整型值m用来存放被复制的数组中一共有几个元素
printf("Please input the number of array:\n");//输入元素的个数
scanf("%d",&m);
int ar[m];
printf("Please input these numbers.\n");//填充上面个数个元素
for(int i = 0;i < m;i++){
scanf("%d",&ar[i]);
}
int br[m];
my_strcpy(ar,br,0,m);//调用函数
printf("The array you copied br[] is:");
for(int i = 0;i < m;i++){
printf("%2d",br[i]);//输出
}
return 0;
}
例如在这里我输入元素的个数为5,填充的元素分别为:1,2,3,4,5,运行结果为:
复制完成
#include
#include
#include
int my_strlen(char *ar)
{
assert(ar != nullptr);
char *p = ar;
int m = 0;//定义整型值m来对字符串的长度进行存储
while(*p != '\0'){//循环条件:p指针解引用后的元素不是字符串的末尾'\0'
m++;
p++;//每次循环记录字符串长度的m加1,p指针的位置向后迁移一位
}
return m;
}
int main()
{
char message[] ="";
printf("Please input the string.\n");
scanf("%s",message);
printf("The lenth of the string you put is %d",my_strlen(message));
return 0;
}
如图我输入fuckyou!带上感叹号一共是8个字符,我们来看一下运行结果:
结果正确,字符串长度输出成功 !
#include
#include
#include
void my_strcat(char *message1,char *message2)
{
assert(message1 != nullptr && message2 != nullptr);
char *p = message1;
char *q = message2;
assert(p != nullptr && q !=nullptr);
while(*p){//此循环存在的原因:当p指针还停留在第一个字符串时,一直向后迁移,直到到达第一个字符串的末尾
p++;
}
while(*q != '\0'){//跳出第一个循环之后,当指针没有到达第二个字符串的末尾时
*p++ = *q++;//将第二个字符串的字符添加在第一个字符串的后面,循环。
}
}
int main()
{
char message1[100] = {0};
char message2[100] = {0};
printf("Please input your first string: \n");
scanf("%s",message1);
printf("Please input your second string: \n");
scanf("%s",message2);
my_strcat(message1,message2);
printf("The string you connected is %s\n",message1);
return 0;
}
如图为我输入的第一个字符串和第二个字传分别为Hello和world:
如图,字符串连接成功!
字符串连接成功!
#include
#include
#include
int my_strcmp(char *p1,char *p2)
{
assert(p1 != nullptr && p2 != nullptr);
int i = 0;
while(*(p1 + i) == *(p2 + i)){//循环条件为字符串1和字符串2的存在一个字符相同时
if(*(p1 + i++) == '\0'){
return (0);
}
else{
return(*(p1 + i) - *(p2 + i));//返回值字符串1减去字符串2的ASCII码
}
}
}
int main()
{
char str1[100];
char str2[100];
char *p1 = &str1[0];
char *p2 = &str2[0];
printf("Please input the strings you wanna compare:\n");
scanf("%s%s",str1,str2);
printf("The result of the strings you compared with is :%d ",my_strcmp(p1,p2));
return 0;
}
如图我比较boy字符串和bad字符串,运行结果为:
结果正确