# 特别

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 长度的整型值,都必须先转换为 intunsigned 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; 
}

ptr2的地址

第一问

第二问

第三问

# 笔试 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;
}

07解答

# 笔试 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;
}

第一步

第二步

第三步

第四步

第五步

# 参考资料

  • 关于指针的笔试题

  • sizeof 详解

  • 截断

  • 入坑无符号类型转换