@TOC(指针注意事项,数据存储方式,位运算)
1.空指针和野指针
不要操作野指针和空指针
-
空指针: 不要去操作空指针,对空指针指向的内存赋值等操作
void test01() { char* p = NULL; strcpy(p, "hello"); //随便指向一个地址 char* p = 0x12345678; strcpy(p, "hello"); } 结果: 报错
-
野指针:
-
指针变量未初始化
void test02() { int* p; *p = 10; } 结果: 报错
-
释放堆区的指针后未置空
void test03() { char* p = malloc(sizeof(char) * 64); strcpy(p, "hello world"); printf("%s\n", p); free(p); printf("%s\n", p); } 结果: hello world 葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺葺 (乱码)
-
空指针可以free,野指针不能free
void test04() { char* p = malloc(sizeof(char) * 64); strcpy(p, "hello world"); printf("%s\n", p); free(p); free(p); } 结果: 报错 void test04() { char* p = malloc(sizeof(char) * 64); strcpy(p, "hello world"); printf("%s\n", p); free(p); p = NULL; free(p); } 结果: hello world
-
2.指针的步长
-
指针+1后向后跳的字节数量不同
void test01() { char* p1 = NULL; printf("%d\n", p1); printf("%d\n", p1+1); int* p2 = NULL; printf("%d\n", p2); printf("%d\n", p2 + 1); double* p3 = NULL; printf("%d\n", p3); printf("%d\n", p3 + 1); } 结果: 0 1 0 4 0 8
-
解引用取出的字节数量不同
void test02() { char buf[1024] = { 0 }; int a = 1000; memcpy(buf+1, &a, sizeof(int)); char* p = buf; printf("%d\n", *(int*)(p + 1)); } 结果: 1000
-
指针步长的练习
struct Person { char a; int b; char buf[64]; int d; }; void test01() { struct Person p = { 'a',10,"hello world",1000 }; struct Person* p1 = &p; printf("%d\n", offsetof(struct Person, d)); printf("%d\n", &p); printf("%d\n", *(int*)((char*)p1 + offsetof(struct Person, d)) ); } 结果: 72 20184152 1000
3.指针的间接赋值
-
通过指针解引用和外部函数指针解引用完成间接赋值
void changeValue(int* p) { *p = 30; } void test01() { int a = 10; int* p = &a; *p = 20; printf("%d\n", a); int b = 20; changeValue(&b); printf("%d\n", b); } 结果: 20 30
-
通过解引用已确定的地址值进行间接赋值
默认VS中变量的地址值是随机的会变化的,通过进行设置可以使其固定不变
void test02()
{
int a = 10;
printf("%d\n", &a); //1703444
*(int*)1703444 = 20;
printf("%d\n", a);
}
结果:
1703444
20
5.指针做函数参数的输入输出特性
-
输入特性:主调函数分配内存,被调函数使用
//输入特性,主调函数分配内存,被调函数使用 void test01() { //栈上分配内存 char buf[64] = {0}; func1(buf); printf("%s\n", buf); } void printString(char* p) { printf("%s\n", p); } //堆上分配内存 void test02() { char* buf = malloc(sizeof(char) * 64); memset(buf, 0, 64); strcpy(buf, "hello world!!!"); printString(buf); }
-
输出特性:被调函数分配内存,主调函数使用
//输出特性,被调函数分配内存,主调函数使用
void allocateSpace(char** pp)
{
*pp = malloc(sizeof(char) * 64);
memset(*pp, 0, 64);
strcpy(*pp, "hello world???");
}
void test03()
{
char* p = NULL;
allocateSpace(&p);
printf("%s\n", p);
}
6.一级指针易错点
指向堆区的指针移动后对其使用free 报错
//指向堆区的指针移动后对其使用free 报错
void test01()
{
int* p = malloc(sizeof(int) * 5);
int* pp = p;
for (int i = 0; i < 5; ++i)
{
*pp = i + 10;
printf("%d\n", *pp);
pp++;
}
free(p);
p = NULL;
}
7.二级指针做函数参数的输入与输出特性
-
输入特性:
//二级指针做函数参数的输入特性 void printArray(int** arr, int len) { for (int i = 0; i < len; ++i) { printf("%d\n", *arr[i]); } } void test01() { int** pArray = (int**)malloc(sizeof(int*) * 5); int a1 = 10; int a2 = 20; int a3 = 30; int a4 = 40; int a5 = 50; pArray[0] = &a1; pArray[1] = &a2; pArray[2] = &a3; pArray[3] = &a4; pArray[4] = &a5; int len = 5; printArray(pArray, len); } 结果: 10 20 30 40 50
//在栈上分配内存 void test02() { int* arr[5]; for (int i = 0; i < 5; ++i) { arr[i] = (int*)malloc(sizeof(int)); *arr[i] = 100 + i; } int len = sizeof(arr) / sizeof(arr[0]); printArray(arr, len); for (int i = 0; i < len; ++i) { free(arr[i]); arr[i] = NULL; } } 结果: 100 101 102 103 104
-
输出特性:
void allocateSpace(int** p) { int* temp = (int*)malloc(sizeof(int) * 5); for (int i = 0; i < 5 ;++i) { temp[i] = i + 10; } *p = temp; } void printarray(int* arr, int len) { for (int i = 0; i < len; ++i) { printf("%d\n", arr[i]); } } void freeallocate(int **pp) { free(*pp); *pp = NULL; } //输出特性 void test03() { int* p = NULL; allocateSpace(&p); printarray(p, 5); freeallocate(&p); p = NULL; } 结果: 10 11 12 13 14
8.数据的存取
存数据
- 无符号数据:原码 = 反码 = 补码
- 有符号数据
- 正数:原码 = 反码 = 补码
- 负数:
- 原码:高位是1代表是负数
- 反码:原码符号位不同,其余位取反
- 补码:反码+1
取数据
-
无符号取:原样的方式进行输出,对于八进制和十六进制,不要考虑正负
-
有符号取:
- 先看补码高位
- 高位是0:原码 = 反码 = 补码,原样输出数据
- 高位是1:原码 = 补码符号位不动其余位取反+1
- 先看补码高位
-
简单测试:
void test01() { char num = -15; printf("%d\n", num); //-15 printf("%u\n", num & 0x000000ff); //241 } void test02() { char num = 0x9b; printf("%d\n", num); //-101 printf("%x\n", num & 0x000000ff); //9b printf("%u\n", num & 0x000000ff); //155 }
9.位运算
按位取反~
注意:做取反操作时包括符号位。
void test03()
{
int num = 2;
printf("%d\n", ~num); //-3
int num2 = -2;
printf("%d\n", ~num2); //1
}
按位与&
同真为真 其余为假
可用于判断奇偶性
void test04()
{
int num = 777;
if ((num & 1) == 1)
{
printf("num是奇数\n");
}
else
{
printf("num是偶数\n");
}
}
按位或|
同假为假 其余为真
用于让指定的位赋为1,在Linux下有些类似位图的数据可以使用|来进行指定属性的添加
void test06()
{
int num = -100;
printf("%d\n", num >> 4); //7
}
按位异或^
相同为假 不同为真
可用于两个变量值得交换
void test06()
{
int a = 10;
int b = 20;
//方法一
int temp = a;
a = b;
b = temp;
printf("%d\n", a);
printf("%d\n", b);
//方法二
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("%d\n", a);
printf("%d\n", b);
//方法三
a = a + b;
b = a - b;
a = a - b;
printf("%d\n", a);
printf("%d\n", b);
}
移位操作
-
左移<<N :相当于原数乘(2的N次方),左移低位补0
-
右移>>N :相当于原数除以(2的N次方)
- 无符号数:高位用0填充
- 有符号正数:高位用0填充
- 有符号负数:高位用1填充
void test07()
{
int num = -100;
printf("%d\n", num >> 2); //-25
int num2 = -2;
printf("%d\n", num2 << 2); //-8
}
移位操作
-
左移<<N :相当于原数乘(2的N次方),左移低位补0
-
右移>>N :相当于原数除以(2的N次方)
- 无符号数:高位用0填充
- 有符号正数:高位用0填充
- 有符号负数:高位用1填充
void test07()
{
int num = -100;
printf("%d\n", num >> 2); //-25
int num2 = -2;
printf("%d\n", num2 << 2); //-8
}