编译环境:
一切调试结果都是在64位机上调试的
基础必备:
学习记录:
名称 | 含义 |
---|---|
指针数组 | int *p[4]; |
数组指针 | int (*p)[6]; |
指针函数 | int *p = test(); |
函数指针 | 见解释详情 |
指针常量 | int * const p; |
常量指针 | const int* p ; |
常指针 | const int* const p; |
复杂函数 | 见解释详情 |
解释
- 指针数组 : 如示例所示 int*p[4],按照操作符优先级来看,[ ]优先级高于 * ,所以他先是个数组 ,然后数组里面放指针,上示例代码
/**
大前提 64位机器
先说结果,输出为 32
原因:因为这是个指针数组,所以数组里的每个item放的都是
int* 类型的数据,而 sizeof(int*) = 8 ,所以 你数组长度
为4 ,所以长度当然是 32 咯。
**/
int *p[4];
printf("%d\n",sizeof(p));
- 数组指针 : 如示例所示 int (*p)[6],按照操作符优先级来看,()优先级高于 [],所以他先是指针 ,然后指向数组,上示例代码
/**
大前提 64位机器
先说结果,输出为 8
原因:因为这是个数组指针,是指向数组的指针,所以他的长度就是指针的长度 所以是 8
**/
int (*point)[6];
printf("%d\n",sizeof(point));
- 指针函数 : 如示例所示 int *p = test();返回值类型是指针类的函数,即return返回的值是一个指针类型,接收变量必须是指针类型。
- 函数指针 : 把函数赋值给一个指针变量,那么访问这个变量的之后,就等于执行该函数,上示例代码
void add(int a, int b) {
printf("a+b = %d", a + b);
}
int main() {
void (*p)(int, int);
p = add;
p(10, 20);
return 0;
}
但是 每次都要写一大串void (*p)(int, int);
,可以换种方式
typedef void Func(int,int);
void add(int a, int b) {
printf("a+b = %d", a + b);
}
int main() {
Func *p = add;
p(10,20);
return 0;
}
其实函数指针还能实现java中的回调,不信试试
typedef void CALLBACK(int);
//定义回调函数
void callback(int requestCode) {
printf("result_code:%d", requestCode);
}
void request(char str[], CALLBACK *callback) {
if (strlen(str) > 0) {
callback(200);
} else {
callback(404);
}
}
int main() {
char a[] = "https://www.baidu.com";
request(a, callback);
return 0;
}
输出结果
result_code:200
- 指针常量 : 指针指向的地址是一个常量,地址不可改,内容可改。
- 常量指针 : 内容不能变,指针地址可以变
- 常指针 : 内容不能变,指针地址也不能改变
- 复杂函数: 一串看不懂的代码,网上好像都是这个例子,但是好像都是文字描述,大家可以看了文字描述之后在看一下在这个图解(举一个例子吧,其他都是换汤不换药)。
int (*(*(*func)(int *))[5])(int *);
利用 右左法则
以上就是图解,首先大家可以看到func
是一个指针,指向了一个函数,函数的形参是int*
,返回值又是一个指针,指向了5个元素的数组,并且数组里面存放的是指针,这些指针又指向了一个函数(返回值int
,形参 int*
)
看了这么多,有个问题不知当不当讲?假如func++;
这句代码执行后大家知道是加了多少个字节嘛?
答案 : 40个字节,因为从图中明显看出,func 指向到最后,会是指向了一个数组,func++
就增加了这一个数组的长度,而这个数组的长度刚好是 5 * 8 = 40
个字节。
实际演练:
下面用上面的几个小例子,考一考
- 以下的
int * arr[4]
指针类型是什么?指向的类型又是什么?输出结果是什么?
int *arr[4];
int *t = arr;
printf(" before %p\n",t);
t++;
printf(" after %p\n",t);
答案:
指针类型:int *[4] ; 指向的类型是int
输出结果:
before 000000000061FDF0
after 000000000061FDF4
分析:
从输出结果看:地址自增了4,我们的自增都是根据指针指向的类型所确定的,而指向类型是sizeof(int),所以增长了4。
- 以下的
char(*p)[4]
指针类型是什么?指向的类型又是什么?输出结果是什么?
char ch[5]={'1','2','3','4','5'};
char(*p)[5] = &ch;
printf(" before %p\n",p);
p++;
printf(" after %p\n",p);
答案:
指针类型:char *[5] ; 指向的类型是 char[5]
输出结果:
before 000000000061FDEB
after 000000000061FDF0
分析:
从输出结果看:地址自增了5,我们的自增都是根据指针指向的类型所确定的,而指向类型是 char[5],所以sizeof(char[5])为5。
- 后来追加
p
和&p
傻傻分不清?
int c = 20;
int *p = &c;
printf("%p\n",&c);
//指向的地址 也就是c的地址
printf("%p\n",p);
//指针p的地址
printf("%p\n",&p);
输出结果 :
000000000061FDDC
000000000061FDDC
000000000061FDD0
第一行我们首先输出变量c的地址(栈自动分配的),第二行输出了p指向的地址,即p
代表了c的地址,而在程序中p
又是一个变量,所以栈肯定也会为它分配一个地址,所以&p
就是输出指针p
在栈中的地址。
配张图更好理解
&a + 1
和a + 1
傻傻分不清?
int a[] = {1, 2, 3, 4, 5};
int *ptr2 = &a + 1;
//元素 5 存放地址
printf("%p\n", a+4);
//元素 5 后存放地址
printf("%p\n", ptr2);
输出结果 :
000000000061FE10
000000000061FE14
也就是说ptr2指向了a[4]后面的地址,由于a[4]开始地址000000000061FE10
,又因为int类型占地sizeof(int) = 4
,所以&a + 1
指向了000000000061FE14
开始的地址
配张图更好理解
3. 以下的char *p = "123"
,我们需要怎么修改它的变量值呢?
显而易见我们能轻松写下如下代码,超级简单?
ps:有大神能解释这个问题3里的变量定义为啥会有这种效果的,欢迎留言,手动跪谢。
//常量指针
char *p = "123";
*p = "456";
printf("%s\n",p);
不好意思写了上面代码之后,你会成功的挂掉
,当我们在代码中写下char *p = "123"
,会在常量区(不能修改值)里生成123
这个字符串,然后p
指向这个字符串,所以我们就不能用*p = "456"
的方式来修改值,那咋办呢?当然是换一个指向地址就行了
char *p = "123";
p = "456";
printf("%s\n",p);
这样就行了,可以总结一下这种值不能修改,地址可以修改
的变量是不是很熟悉,这就是我们的常量指针
还有几个跟上面类似的代码例子,
常量指针
char *arr[4] = {"abc","efg","hij"};
//常量指针 下面改变值不行,可以改变地址
// strcpy(arr[1],"hell");
arr[1] = "hell";
printf("%s\n",arr[1]);
return 0;
指针常量
char arr[3][4] = {"abc","efg","hij"};
//指针常量 下面改变地址不行,所以用strcpy()方法改变变量值
//arr[1] = "hell" //这种方式编译检查不通过
strcpy(arr[1],"hell");
printf("%s\n",arr[1]);
指针常量
char a[4] = "123";
//strcpy(a[0],'!'); //会报错
*(a+1)='!';
printf("%s",a);
return 0;
- 以下的代码就是打印数组,简简单单?
博主就是java转过来的
这个题目学过Java的需要看一下
,如果C是你的第一门编程语言可以跳过这题。
void printArray(char arr[3][3]) {
for(int i=0;i<3;i++){
printf("%s\n",arr[i]);
}
}
int main() {
char arr[3][3] = {"abc","def","g"};
printArray(arr);
return 0;
}
输出结果 :
abcdefg
defg
g
结果分析 :讲真,当我看到这个结果的时候我tm是绝望的,这是个啥东西,我以为我的编程白学,当然我们首先要理解一件事,在c中遇到'\0'
,才会停止输出 ,这句话记住以后,然后看下面这张图就明白了。
上面这张图就是arr数组初始化的内容,当我们第一次循环的时候,从 a
开始输出,当输完c
时,并没有遇到\0
,所以就继续输出 defg
,所以第一行abcdefg
,第二行与第一行情况一样,只不过是从d
开始输出,所以第二行输出defg
,第三行也是以此类推输出g
。
但是显而易见 这样的输出非常怪异,我们就需要在每行后面加个\0
void printArray(char arr[3][4]) {
for(int i=0;i<3;i++){
printf("%s\n",arr[i]);
}
}
int main() {
//这里我们在列数上扩大一个维度,这样让c编译器在空的地方自动生成 \0
char arr[3][4] = {"abc","def","g"};
printArray(arr);
return 0;
}
由于增加了列数上的一个维度,数组内容分布变成这样了
输出结果 :
abc
def
g
这样就比较符合我们的认知了(当然因人而异吧)