简介
c语言之所以强大,以及其自由性,很大部分体现在其灵活的指针运用上。因此,说指针是c语言的灵魂,一点都不为过。
一级指针
* 数组做函数参数时没有副本机制,只是一个地址,因此传入数组参数时需要传入数组的大小
void fun(char str[][5], int len) { printf("sizeof str: %d\n", sizeof(str)); for(int i = 0; i < len; ++i) { printf("strlen str[%d]: %d\tsizeof str[%d]: %d\n", i, strlen(str[i]), i, sizeof(str[i])); } } 输出结果: sizeof str: 4循环输出的strlen为每个字符串的长度 循环输出的sizeof为5(原因见函数实参类型)
* 函数改变外部变量
void modifyvariable(int *src, int value) { *src = value; } int main() { int a = 100; printf("a: %d\n", a); modifyvariable(&a, 200); printf("a: %d\n", a); } 输出结果: a: 100a: 200
* 跨进程改变变量(外挂)
* 采用微软的`detours`库 * 将程序编译成动态链接库DLL * 将DLL注入到目标线程中,实现修改某地址的数据,或者拦截函数
* 做函数形参,数组做参数时会退化为一个一级指针,即其内存空间大小为4
void fun(char str[][5], int len) { printf("sizeof str: %d\n", sizeof(str)); } 输出结果: sizeof str: 4
* 储存数组的首地址,指针数组
char str[][6] = { "eth8", "eth1", "eth6", "eth3", "eth4" }; char *pstr[5] = { NULL, NULL, NULL, NULL, NULL };for (int i = 0; i < 5; ++i) { pstr[i] = str[i]; } char *p = NULL;for (int i = 1; i < 5; ++i) { for (int j = 0; j < i; ++j) { if (strcmp(pstr[i], pstr[j]) < 0) { p = pstr[i]; pstr[i] = pstr[j]; pstr[j] = p; } } }for (int i = 0; i < 5; ++i) { printf("%s\n", pstr[i]); } 输出结果: eth1 eth3 eth4 eth6 eth8
* return 有副本机制
* 其值存放在寄存器中,不能取地址 * 如果返回一个在栈上声明的变量,两次输出return的值可能不同 * 如果立马输出,由于寄存器还未被其他变量使用,则可能返回正确值; * 如果延时输出(几条IO输出语句即可),则返回错误值;
* 作为函数的返回地址(不能返回指向栈上的内容)
* 具体原因见上一条
* 存储常量字符串的首地址,不能修改其值
* 间接访问结构体,创建堆上的变量
* 通过`->`运算符访问结构体中声明的变量 * 通过`malloc`、`calloc`、`realloc`、`new`等函数在堆上开辟内存空间
* #的作用,无论类型,直接加""
,可用于获取函数名变量名等
#define funname(fun) \ { \ printf("%s\n", #fun); \ }void modifyvariable(int *src, int value) { *src = value; } int main() { funname(modifyvariable); return 0; } 输出结果: modifyvariable
指针数组
批量管理地址
char str[][6] = { "eth8", "eth1", "eth6", "eth3", "eth4" }; char *pstr[5] = { NULL, NULL, NULL, NULL, NULL };for (int i = 0; i < 5; ++i) { pstr[i] = str[i]; } char *p = NULL;for (int i = 1; i < 5; ++i) { for (int j = 0; j < i; ++j) { if (strcmp(pstr[i], pstr[j]) < 0) { p = pstr[i]; pstr[i] = pstr[j]; pstr[j] = p; } } }for (int i = 0; i < 5; ++i) { printf("%s\n", pstr[i]); } 输出结果: eth1 eth3 eth4 eth6 eth8
函数指针
函数名等价于常指针
函数指针数组:
int (*p[2])(int a, int b) = {add, sub};
int add(int x, int y) { return x + y; } int sub(int x, int y) { return x - y; } int main() { int(*fun[2])(int, int) = { add, sub }; for (int i = 0; i < 2; ++i) { printf("%d\n", fun[i](100, 50)); } return 0; } 输出结果:15050
malloc,realloc,calloc
malloc
和calloc
区别:eg:
int *p = (int *)calloc(10, sizeof(int));
eg:
int *p = (int *)malloc(sizeof(int) * 10);
malloc
在堆上开辟一块内存,不会对内存进行清零操作calloc
在堆上开辟一块内存,并且执行内存清零操作
realloc
重新分配内存,新增加的内存不会置零
关于指针类型
根据指针的内存大小来判定指针的类型
void getaddress(char str[][6]) { printf("In Function: \n"); printf("str:\t\t%p\tsizeof: %d\n", str, sizeof(str)); printf("str[0]:\t\t%p\tsizeof: %d\n", str[0], sizeof(str[0])); printf("str[0][0]:\t%p\tsizeof: %d\n", &str[0][0], sizeof(str[0][0])); } int main() { char str[][6] = { "eth8", "eth1", "eth6", "eth3", "eth4" }; { printf("In Main: \n"); printf("str:\t\t%p\tsizeof: %d\n", str, sizeof(str)); printf("str[0]:\t\t%p\tsizeof: %d\n", str[0], sizeof(str[0])); printf("str[0][0]:\t%p\tsizeof: %d\n", &str[0][0], sizeof(str[0][0])); } getaddress(str); return 0; } 输出结果: In Main: str: 0043FC04 sizeof: 30str[0]: 0043FC04 sizeof: 6str[0][0]: 0043FC04 sizeof: 1In Function: str: 0043FC04 sizeof: 4str[0]: 0043FC04 sizeof: 6str[0][0]: 0043FC04 sizeof: 1
关于指针的遍历
无论多少级指针,其遍历方法都类似
int main() { int num[4][3] = { {1, 2, 3}, {2, 3, 4}, { 3, 4, 5}, {4, 5, 6} }; for (int i = 0; i < 4; ++i) { for (int j = 0; j < 3; ++j) { printf("%d\t", num[i][j]); } putchar('\n'); } putchar('\n'); putchar('\n'); for (int i = 0; i < 4; ++i) { for (int j = 0; j < 3; ++j) { printf("%d\t", *(*(num + i) + j)); } putchar('\n'); } return 0; } 输出结果:1 2 32 3 43 4 54 5 61 2 32 3 43 4 54 5 6
NOTE
printf
不会进行类型转换,即占位符是什么类型,就按什么类型输出
作者:silverlaw
链接:https://www.jianshu.com/p/d0c1faefe961