# 特别
while
一般而言,所有非零值
都视为真
,只有0
被视为假
。
#include <iostream> | |
using namespace std; | |
int main(void) | |
{ | |
printf("%d\n", 1); | |
int i = -6; | |
//while 一般而言,所有非零值都视为真,只有 0 被视为假 | |
while (i) | |
{ | |
printf("%d\n", i); | |
i++; | |
} | |
return 0; | |
} |
# 指针与 const
指针在初始化时,引用了,其它变量的值,无论对指针的地址还是对指针值
+ const
。修改变量的值,都将影响到指针
- 对
*
后面加入 const 代表将地址设置为常量,地址不可变
,但是对应的值
可以改变。 - 第
*
前面的值加 const,对应的值*p
不能改变,但是可以改变p
的地址,通过此形式改变了*p
对应的值。 - 对
*
的前后都
添加了const
,代表对应的地址
,和值都不能改变
,但是如果在const初始化
时是引用其它变量
的值,就可以修改其它变量的值
,来间接修改此值
。
/* | |
关于 const 与指针之间的运用问题 | |
*/ | |
#include <iostream> | |
using namespace std; | |
int main(void) | |
{ | |
int a = 6; | |
int i = 1; | |
// *p 常量化 | |
const int* p = &a; | |
cout << "a: " << *p <<endl; //6 | |
p = &i; | |
cout << "p = &i : " << *p << endl; //1 | |
//*p = 10; //*p 为 const 值不可改变 | |
cout << "*p: " << *p << endl; // | |
// 对 a 的值进行修改,间接的修改 * p 的值 | |
a = 7; | |
p = &a; | |
// 通过对 a 值的修改,间接修改了 指向 p 地址的 p | |
cout << "*p: " << *p << endl; // 7 | |
int b = 8; | |
//*p 固定值不能改变,但是 p 的地址可以改变 | |
p = &b; // 通过赋值新的地址 p | |
cout << "*p: " << *p << endl; | |
//t 地址常量 | |
int* const T = &a; | |
cout << "const p: " << *T << endl; // 值为 7 | |
// T = &b; // 地址为 cosnt 常量,不可进行修改 | |
// 当忍让可以通过修改 a 的值,间接修改引了 a 地址的 T | |
a = 9; | |
cout << "const p: " << *T << endl; | |
// 地址和值都是常量 | |
const int* const S = &a; // 初始化为 a 的 9 值 | |
// S = &b; // 地址值不再允许改变 | |
// 通过 a 的值可以 改变 S 的值 | |
// | |
a = 10; //a 仍然为变量,仍然可以进行改变 | |
cout << "const int* const S :" << *S << endl; | |
// 对 * S 以及 S 的地址,在初始化后不可再次改变 | |
return 0; | |
} |
# 数组 a [] 中 a 与 & a 区别
值相同
a
指的是a[0]
的地址
&a
指的是数组a
的地址
- 数组名代表数组第一个元素的地址,
&数组名
代表整个数组
的地址,从而导致a+1
和&a+1有本质
的区别
# char* 与 char a []
char *a = "abcd"
char a[20] = "abcd"
- 读写能力
字符串数据存放在
常量存储区
,通过指针只可以访问字符串常量,而不可以改变它
数组数据存放在栈
,可以通过指针
去访问
和修改
数组内容
- 赋值时刻
指针编译时就已经确认了,因为是
常量
数组运行时确认
- 存取效率
存于
常量存储区
,在栈上的数组
比指针所指向字符串
快数组
存放在栈上
,因此块
strlen
不计\0
, 但是sizeof
计算字符串容量时算\0
, 占两
个字节。
# 读取位置
⛵
// 不加 * 代表真个字符串地址,加 * 对应地址的单个 字符 | |
const char* a = "helloworld"; // 常量字符串 | |
// 在没有取单独值时,就是指针移动,指针移动的是对应的字符对应的字节 | |
cout << a + 1 << endl; // 起始地址右移,变为 elloworld | |
char b[] = "morning"; | |
cout << b+1 << endl; // 同样地址右移,变为 orning | |
// 其相等,取地址位置的一个字符 | |
cout << *(a + 1) << endl; // 取固定值 | |
cout << b[1] << endl;; | |
printf("%c\n", *(a + 1)); | |
printf("%c\n", b[1]); |
# 为 char*
申请 固定空间
char*
代表字符串指针
,单个
,为其申请空间
,要取其字符串指针的地址
即二级指针
进行空间申请
。使用二级指针渠道传入参数 n 的真实地址,而不是局部变量,局部变量在栈中
函数结束将被释放
,申请空间会无效
。要使用二级指针
,指向字符串n
的地址
。
void getMemory(char** n) | |
{ | |
*n = (char*)malloc(100); | |
} | |
char* n = NULL; | |
getMemory(&n); | |
strcpy(n, "nihaohaohao"); | |
cout << n << endl; |
# 整型在内存中的存储及运算规则
有符号
二进制
表示正反
的方式,在首位
1为负数
0为正数
数据以
补码
的形式保存在内存中。
正数
的反码
和补码
是其本身
在类型转换的过程中直接
保存低位
即可在
int转char型
,-2 对应的补码1111 1110
转为 4 位,直接截断
得到四位,即 1110,这个数对应的还是-2
;在还原时:
-1
,再求反码
即得原码得到原数
char转int
,若高位是0
,正数
,高位补0
,若为负数
高位补1
即可
# 截断
当将不同类型元素混合赋值且指向内存空间大小不一样时,就会
发生截断
。截断会高位截断
,保留低位数据,当-1
整型存入字符串数据中
,就会发生截断
# 整型提升
整型提升
的意义
在于:表达式的整型运算要在 CPU 的相应运算器件内执行,CPU 内整型运算器 (ALU) 的操作数的字节长度一般就是 int 的字节长度,同时也是 CPU 的通用寄存器的长度。因此,即使两个 char 类型的相加,在 CPU 执行时实际上也要先转换为 CPU 内整型操作数的标准长度。通用 CPU(general-purpose CPU)是难以直接实现两个 8 比特字节直接相加运算(虽然机器指令中可能有这种字节相加指令)。所以,表达式中各种长度可能小于 int 长度的整型值,都必须先转换为int
或unsigned int
,然后才能送入CPU
去执行运算。
int main() | |
{ | |
char a = -1; //-1 截断后存储到 a 中 | |
//10000000000000000000000000000001 -1 的原码 | |
//11111111111111111111111111111110 -1 的反码 | |
//11111111111111111111111111111111 -1 的补码 | |
//11111111 - a 截断后 char a 中 a 所存的补码 | |
signed char b = -1; | |
//11111111111111111111111111111111 -1 的补码 | |
//11111111 - b b 的补码 | |
// | |
unsigned char c = -1; | |
//11111111111111111111111111111111 -1 的补码 | |
//11111111 - c c 的补码。 | |
// | |
// 整型提升 | |
printf("a=%d,b=%d,c=%d", a, b, c); | |
//-1 -1 | |
//11111111111111111111111111111111 | |
//11111111111111111111111111111110 | |
//10000000000000000000000000000001 | |
//11111111 | |
//00000000000000000000000011111111 | |
return 0; | |
} |
# 八道笔试题集合
指针层级
过深时
,通过画关系
的形式表达
出来。
# sizeof小练
牢记:sizeof 的计算在编译时刻,把它当
常量表达式
使用,且会忽略掉表达式
内部的各种运算
,指针在32位系统
中占4
个字节,在64位系统
中占8
个字节
sizeof
计算字符串容量时算\0
与此同时:sizeof("\0")=2
;- 在
编译阶段处理
,sizeof
作用范围内的内容不能被编译
,所以sizeof()
内的运算不被执行
sizeof(函数)
=sizeof(返回值类型)
- 联合体:
最长成员
的大小对齐
sizeof(2.5+3.14)
实际上是sizeof(double)
切记需要识别出其类型
sizeof
可以对函数调用
求值,实际上是对返回值类型求值
int func(char s[5]) | |
{ | |
cout << endl; | |
return 1; | |
} | |
sizeof(func("1234")); // 得出返回值类型 int 的大小,4 个字节 |
# sizeof 不可用
不能对函数名
求值不
能对不确定返回值
的类型求值,如void
#include <stdio.h> | |
int main(void) | |
{ | |
short num = 20; | |
int a = 1; | |
printf("%d\n", sizeof(num = a+5)); | |
printf("%d\n", num); | |
return 0; | |
} |
# 笔试 1
#include<stdio.h> | |
int main() | |
{ | |
int a[5] = { 1, 2, 3, 4, 5 }; | |
//a 为数组 a [0] 的地址与数组 a 地址重合,&a 取的就是数组 a 的地址 | |
int* ptr = (int*)(&a + 1); // 取了数组 a 之后的地址空间 | |
printf("%d,%d", *(a + 1), *(ptr - 1)); //a 的地址空间减去一个 sizeof (int) 得出结果: 2,5 | |
return 0; | |
} |
指针的类型
决定了指针+1时
的步长
,指针的类型决定了对指针进行解引用操作
时,访问的空间大小
# 笔试 2
指针移动,移动对应类型的
sizeof(类型)
的字节数,注意转换关系,要根据转换关系
灵活应用
#include<stdio.h> | |
// 此结构体的大小是 20 个字节 | |
struct Test | |
{ | |
int Num; | |
char* pcName; | |
short sDate; | |
char cha[2]; | |
short sBa[4]; | |
}*p; | |
int main() | |
{ | |
p = (struct Test*)0x100000;// 假设 p 的值为 0x100000。 //p 的地址 0x0000000000100000 | |
//% p 输出地址 | |
//p 为结构体类型,每次指针移动一位,就是移动的 strct Test 的大小,和数组移动时一样 | |
// 移动了一位,就是内存地址移动了 20 个字节 | |
printf("%p\n", p + 0x1); //0x100020 | |
// 使用了类型转换,无符号长整型 对应的是值 +1 | |
printf("%p\n", (unsigned long)p + 0x1); //0x100001 | |
// 使用无符号整型,那整型 + 1 就是 int, 对应的是指针类型,要加 4 个字节就是 4 个字节 | |
printf("%p\n", (unsigned int*)p + 0x1); //0x100004 | |
return 0; | |
} |
# 笔试3
#include<stdio.h> | |
int main() | |
{ | |
int a[4] = { 1, 2, 3, 4 }; | |
int* ptr1 = (int*)(&a + 1); // 上通 4 &a 取的是整个数组 a 的地址,+1 即跳过一个 int (*)[4] 类型,然后再将它强制转换为 int * 类型 | |
// 首先计算出 a 在内存中的存储,使用小端存储结构,+1 之后 | |
int* ptr2 = (int*)((int)a + 1); | |
printf("%x,%x", ptr1[-1], *ptr2); //% x 以 16 进制打印 | |
return 0; | |
} |
# 笔试 4
# 逗号表达式
逗号表达式
的运算过程为:从左往右
逐个计算表达式- 逗号表达式作为一个
整体
,它的值为最后一个表达式 (也即表达式n
) 的值 逗号运算符
的优先级别在所有运算符中最低
main(){
int x,y,z; x=1; y=1;// 由于后面没有括号,且逗号的优先级是最低的,z 就等于 x++ 了,后面照样运行
z=x++,y++,++y; printf("%d,%d,%d",x,y,x);}
#include<stdio.h> | |
int main() | |
{ | |
// (0, 1) 逗号运算符,取最后一个 | |
// { {1,3},{5,0},{0,0} } | |
int a[3][2] = { (0, 1), (2, 3), (4, 5) }; | |
int* p; | |
p = a[0]; | |
printf("%d", p[0]); //1 | |
return 0; | |
} |
# 笔试5
#include<stdio.h> | |
int main() | |
{ | |
int a[5][5]; | |
//p 是一个数组指针,指向一个有 4 个整型元素的数组 | |
int(*p)[4]; // 取对应的内存,指针画笔 | |
//a 是数组名,数组名代表的是数组首元素的地址,二维数组的首元素是第一行元素, | |
p = a; | |
//-4 取地址,以补码 16 进制的形式 | |
printf("%p,%d\n", &p[4][2] - &a[4][2], &p[4][2] - &a[4][2]); | |
return 0; | |
} |
# 笔试 6
#include<stdio.h> | |
int main() | |
{ | |
int aa[2][5] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }; | |
int* ptr1 = (int*)(&aa + 1); // 取了数组 aa 之后的内存 | |
int* ptr2 = (int*)(*(aa + 1)); // 取的是 aa [1][0] 的地址 | |
printf("%d,%d", *(ptr1 - 1), *(ptr2 - 1)); //10,5 | |
return 0; | |
} |
# 笔试 7
#include<stdio.h> | |
int main() | |
{ | |
// 代表多个字符串 a [0],a [1],a [2] char 变成了字符串 | |
const char* a[] = { "work","at","alibaba" }; | |
//a 的地址默认是 a [0] 的地址, | |
const char** pa = a; | |
pa++; //a[1] | |
printf("%s\n", *pa); //at | |
return 0; | |
} |
# 笔试 8
#include <stdio.h> | |
int main() | |
{ | |
char* c[] = { "ENTER","NEW","POINT","FIRST" }; | |
char** cp[] = { c + 3,c + 2,c + 1,c }; | |
char*** cpp = cp; | |
printf("%s\n", **++cpp); | |
printf("%s\n", *-- * ++cpp + 3); | |
printf("%s\n", *cpp[-2] + 3); | |
printf("%s\n", cpp[-1][-1] + 1); | |
return 0; | |
} |
# 参考资料
关于指针的笔试题
截断
入坑无符号类型转换