# 特别
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; | |
} |
# 参考资料
关于指针的笔试题
截断
入坑无符号类型转换
