1.指针的强制转化
2.const与一级指针和二级指针的结合
第一节:指针的强制转化
引言
强制转化
扩展
第二节 const与一级指针和二级指针的结合
const与一级指针的结合
例题
const与二级指针的结合
例题
扩展
Windows系统的存储方式为小端存储模式
小端存储模式:数据中的低字节,存储在低地址中;数据中的高字节,存储在高地址中;
大端存储模式:数据中的高字节,存储在低地址中;数据中的低字节,存储在高地址中。
正常情况:
假设有一组数据存储在数组arr中:int arr[] = {1,2,13,14};那么该组数据如何在内存中存储呢?
如上图所示,即为数据的存储方式
当想读取不同的数据时,即可通过指针实现,首先定义一个指针:int* p = arr;(数组名arr代表该数组首元素的地址,变量p存储将其进行存储)
即指针首先访问数组元素的首地址,对第一个数据进行读取,当想读第二个数据时,通过p+1即可完成,即*p+1 = 2
指针由第一个位置移动至第二个位置,进而读取第二个数据:2
在这里强调:p+1,即指针p向右移动了1个单元格,而1个单元格的大小由指针指向的变量的类型决定,此处,int为四个字节,故而移动四个小格。
int *p = arr;
char *q = p;(这句语句正确吗?)
显然错误,此时接受该指针变量的类型已经发生了改变,所以需要进行强制转换;
int *p = arr;
char *q = (char*)p;
通过强制转换来保证语法的正确性,但同时结果也发生了改变:
(char*)p+1的结果肯定不是2了,那是多少呢?
char类型为1个字节,所以1个小格为一个单元格,故而,(char*)p + 1 = 0;
#include
int main()
{
int arr[4] = { 1,2,13,14 };
int* p = arr;
char* q = (char*)p + 1;
printf("%d", *q);
return 0;
}
如果将数据类型提升为 long long,那结果又会是如何呢?
long long为8字节,所以指针移动如上,所以结果是d吗?不,进行数据读取时也是按照数据类型进行读取,所以应该读取为8个字节的数,那是否为d e?也不是。
#include
int main()
{
int arr[4] = { 1,2,13,14 };
int* p = arr;
long long* q = (long long*)p + 1;
printf("%llx", *q);
return 0;
}
结果为何为e 00 00 00 0d?
这与栈的内存开辟与数组存储的方式以及小端模式有关:
首先要开辟内存空间(X86系统分配内存空间为4G),局部变量内存的开辟位于栈区(栈区在Win系统下为1M,在Linux系统中为10M)
栈内存开辟:从高地址到低地址进行内存开辟;
数组内存:从低地址到高地址进行存储。
由于Win系统的小端存储模式,故而,低字节的数据存储于低地址处,低字节的数据存储于低地址处,但我们一般读取数据时,应该从高位向地位读取,从而我们读取的顺序为从高地址到低地址进行读取。
所以读取的结果为 e 00 00 00 0d
总结:指针+num 《=》 指针偏移sizeof(指针指向的类型)* num 个字节
存储空间内的“地雷”
在数组中,一般位于数组的头部与尾部“地雷”以“cccc”四字节的形式出现,若访问到此处,程序可能会崩掉或者出错。
例:定义两个变量:
int a = 0;
int b = 0;
则两个变量之间差多少个字节?
首先用代码实现:
#include
int main()
{
int a = 0;
int b = 0;
printf("%p\n",&a);
printf("%p\n",&b);
printf("%d", &a - &b);
return 0;
}
答案为3;
由于int类型为4字节,所以两个变量之间相差12个字节,内存空间分配如下:
同时也可以通过VS 2019自行调试:
1.给出断点;
2.开始调试;
3.进入窗口;
4.点击内存并选择;
5.输入"&+变量"。
(const与变量结合使其变为常变量,从而使其变为常量,即不能通过赋值方式改变变量的值)
首先定义一个一级指针:int* p = &a;
当const与其结合使用时,共分为三种情况:
1.const放在指针前:const int* p = &a;
const放在指针前,意味着const作用于(*p),即不能通过对指针变量p解引用的方式改变a的值
但是,对变量p不产生影响,p可以改变,去存储其他变量的地址。
2.const放在指针后,变量前:int* const p = &a;
const放在指针后,意味着const作用于p本身,即指针变量只能存储变量a的地址,指针变量p不能指向其他变量
3.指针前与指针后都放const,则二者皆不能改变:const int* const p = &a;
此种情况下,指针变量与指针皆为常变量,所以都没有办法改变
给出条件,判断语句的对错:
int a = 0,b = 0;
const int* p = &a;
*p = 10; //(1)
p = &b; //(2)
int*const p = &a;
*p = 10; //(3)
p = &b; //(4)
const int* const p = &a;
*p = 10; //(5)
p = &b; //(6)
const int a = 0;
const int b = 0;
int *p = &a; //(7)
(1)错(2)对
原因:const对指针*p 起作用,则*p不能通过解引用的方式来改变a的值,但是指针变量并没有受到限制,所以指针变量p的值可以被改变
(3)对(4)错
原因: const作用于p本身,即指针变量只能存储变量a的地址,指针变量p不能指向其他变量,但是*p并没有受到限制,所以可以通过解引用的方式改变a的值
(5)错(6)错
原因:二者皆受到限制
(7)错
原因:此时a,b变量皆为常变量,若(7)式成立,则意味着可以通过解引用的方式改变a的值,所以错位,正确语句应该显示不能通过解引用的方式来改变a的值: const int *p = &a;
首先定义一个二级指针:int** s = &p;
与const结合时,共有六种情况:
1.const int **s = &p;
2.int * const *s = &p;
3.int **const s = &p;
4.const int* const * s = &p;
5.const int* const * const s = &p;
6.int*const* const s = &p;
int a = 0;
int b = 0;
const int *p = &a;
int* const q = &a;
int** s1 = &p;
s1 = &q;
**s1 = 20; //(1)
const int **t1 = &p;
t1 = &q; //(2)
*t1 = &b; //(3)
**t1 = 10; //(4)
int *const*s2 = &p;
*s2 = &a; //(5)
**s2 = 100; //(6)
(1) 对 对**s1没有任何限制;
(2)对 对t1没有任何限制;
(3)对 对*t1没有任何限制;
(4)错 const int **t1 = &p;不能通过对t1解引用的方式改变a的值;
(5)错 int* const*s2 = &p;不能通过对s2解引用的方式改变p的值;
(6)对 对s2没有任何限制。
const ,指针与字符串常量
首先判断这条语句是否正确:
char* str = "hello";
用编译器实现:
显然无法实现,因为hello是一个字符串常量
字符串常量与全局变量,静态内存等等共同存储在数据区中,即(.data)(局部变量存储在栈区,动态内存存储在堆区)
而语句意思为:str保存字符串常量存储的地址,若上试成立,则意味着可以通过解引用的方式改变字符串常量中的字符: *str + 1 = ‘a’;显然错误
正确的应为:const char *str = "hello";
练习:
const int a = 0;
int b = 0;
const int* p = &a;
p = &b; //(1)
*p = 10; //(2);
const int* const* s = &p;
*s = &a; //(3)
**s = 10; //(4)
const int **t = &p;(5)
*t = &a; //(6)
const int* const* t = &p;
**t = 10; //(7)
*t = &a; //(8)
p = &a; //(9)
分析:根据题干,首先,变量a为一个常变量,不能被修改,b为一个变量,*p为常变量,不能通过解引用的方式改变变量的值;
(1)对 p没有任何限制;//此时p已经指向变量b
(2)错 不能通过解引用的方式改变b的值;
(3)错 不能通过解引用的方式改变p的值;
(4) 错 不能通过解引用的方式改变b的值;
(5)对 *p在以上语句中已经被限制,所以不加const,即可通过解引用两次t,改变b的值,但是当解引用一次时*t = p,解引用第二次时,*p则无法进一步解引用,所以不能通过该种方式进行改变b的值,所以在**t前加上const;
(6)对 *t无限制;
(7)错;
(8)错 **t与*t都被限制;
(9)p本身不受限制。