指针(变量):变量有的性质,指针都有。
变量作用:存值,有大小、地址。有类型
指针变量,也是变量
指针变量,也是变量
指针变量,也是变量
指针变量占多数字节?
看OS的位数32位、64位。
32位占4B,64位占8B
地址是按照字节标号的,一个字节给一个地址,从0开始,比如32位的内存范围是:0 ~ 2^32 - 1,是4GB的大小
指针变量本身自己也有地址。
取变量地址时,取相关变量的首地址(编号最小的地址)
int a;
int *p = &a;
int **p1 = p; // 指向指针的指针
cout << p; //输出的是地址
cout << *p; // 输出的是p地址里面的值
指针变量前面的数据类型,表示指向该数据类型变量的地址。
int *p;
char *q;
//p和q的所占字节数相同。
指针变量可以参与运算 + -
如:
int *p = NULL;
// p + 1就是地址向后加了4B,向不同类型后面+1个类型的字节大小。
---------------------------------------------
// 等价转换
*p <=> a(原始变量)
p + 1 <=> &p[1]
p->filed <=> (*p).filed <=> a.filed
-> 间接引用
. 直接引用
----------------------------------------
题目:
struct Data {
int x, y;
};
struct Data a[2], *p = a;
尽可能的多的形式表示a[1].x
#include <iostream>
using namespace std;
struct Data {
int x, y;
};
Data a[2], *p = a;
int main() {
a[0].x = 111, a[0].y = 222;
a[1].x = 333, a[1].y = 444;
cout << a[1].x << endl; // 直接访问数组元素
cout << p[1].x << endl; // 使用指针数组下标访问
cout << (&a[1])->x << endl; // 数组下标和指针偏移的结合
cout << (&p[1])->x << endl; // 通过指针偏移访问
cout << (p + 1)->x << endl; // 使用指针算术结合结构体指针访问
cout << (*(p + 1)).x << endl; // 使用指针算术结合解引用操作访问
cout << (a + 1)->x << endl; // 使用数组指针算术访问
cout << (*(a + 1)).x << endl; // 使用数组指针解引用访问
cout << *(&a[0].x + 2) << endl; // 通过偏移结构体成员的地址
cout << *(&p[0].x + 2) << endl; // 通过偏移结构体成员的地址
cout << (&a[0] + 1)->x << endl; // 使用地址运算符和指针算术访问
cout << (*(&a[0] + 1)).x << endl; // 使用地址运算符和解引用访问
cout << (&p[0] + 1)->x << endl; // 使用地址运算符和指针算术访问
cout << (*(&p[0] + 1)).x << endl; // 使用地址运算符和解引用访问
cout << *((int *)a + 2) << endl; // 强制转换类型并进行指针算术访问
cout << ((int *)a + 2)[0] << endl; // 强制转换类型并当成数组访问
cout << ((int *)a)[2] << endl;
cout << *((int *)p + 2) << endl; // 使用强制类型转换和指针算术访问
cout << ((int *)p + 2)[0] << endl;
cout << ((int *)p)[2] << endl;
// 新的方法,使用char*进行偏移然后转换为Data*
cout << ((Data*)((char*)a + sizeof(Data)))->x << endl; // 套娃写法
cout << ((Data*)((char*)p + sizeof(Data)))->x << endl;
return 0;
}
函数指针:也是指针,变量,用来存函数的地址。
int (*add)(int, int); // 函数指针变量(add),int类型的,必须加( ),否则引起歧义。
int* add(int, int); // 指针函数的声明,返回值为int*
typedef int (*add)(int, int);
typedef用法:
内建类型的重命名:(类似#define)
typedef long long lint;
typedef char* pchar;
typedef unsiged char uchar;
结构体类型的重名:
typedef struct __node {
int x, y;
} Node, *PNode;
命名了两个类型:
Node:是这个结构体的类型
*PNode:PNode是这个结构体类型的指针类型
**注意是类型,不是变量!**
函数指针命名:
typedef int (*func)(int);
变量:int (*func)(int);
类型:typedef int (*func)(int);
-------------------------------------------
int main();
int main(int argc, char *argv[]);
int main(int argc, char *argv[], char **env);
argc:接收的是外部传入命令行参数的个数。
*argv[]:存char*的一维数组,char*用来保存字符串的首地址。
**env:当前的环境变量。可当成二维的字符数组env[][],env所接收到的内容是:具体的每一个能够获取的环境变量字符串。
---------------------------------------------
求地址偏移量。
以8B作为空间对齐标准
//21.pointer.cpp
#include <stdio.h>
#define offset(T, x) { \
T tmp; \
(char *)&tmp.x - (char *)&tmp; \
}
// 花括号外侧加圆括号是为了获取最后一条语句的返回值
// #define offset(T, x) ({T tmp; (char *)&tmp.x - (char *)&tmp;})
struct Data {
int a;
char b;
double c;
};
int main() {
printf("%ld\n", offset(struct Data, a));
printf("%ld\n", offset(struct Data, b));
printf("%ld\n", offset(struct Data, c));
return 0;
}
函数不能将数据类型作为参数传入,所以要用宏
升级写法:
#define offset(T, x) (long long)&(((T *)(NULL))->x)
①先(NULL)一个空地址
②将(NULL)强制类型转换为(T *)结构体类型的指针
③成员访问(T *)(NULL))->x
④取其地址&((T *)(NULL))->x)
⑤将其地址强制类型转换为(long long型)查看其偏移量。
-----------------------------------------------
定义变量类型typedef和#define的区别:
#include <stdio.h>
#define ppchar char *
typedef char * pchar;
int main() {
pchar p1, p2;
ppchar p3, p4; //实际是 char * p3, p4;
printf("p1 = %lu, p2 = %lu\n", sizeof(p1), sizeof(p2));
printf("p3 = %lu, p4 = %lu\n", sizeof(p3), sizeof(p4));
/*
p1 = 8, p2 = 8
p3 = 8, p4 = 1
*/
return 0;
}