C语言中“磨人“的词汇

编译环境:

一切调试结果都是在64位机上调试的

基础必备:

C语言运算符优先级

学习记录:

名称含义
指针数组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个字节。

实际演练:

下面用上面的几个小例子,考一考

  1. 以下的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。

  1. 以下的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 + 1a + 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;
  1. 以下的代码就是打印数组,简简单单?博主就是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

这样就比较符合我们的认知了(当然因人而异吧)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值