7.19知识小结

一、关于计算机用补码方式存储数据

     有坑的代码如下:

int main(void)
{
    char a,b;
    char c;
    
    a = -100;
    b = -100;
    c = a + b;

    printf("c=%d", c);

    return 0;
}

     其结果是: c=56

     先把相关知识点列一下:

原码表示法是整数的一种简单的表示法,符号位用0表示正号,用1表示负号,数值一般用二进制形式表示。
整数的反码可由原码得到,如果是正数,则反码与原码一样;如果是负数,则反码是对它的原码(符号位除外)各位取反而得到的。
整数的补码可由原码得到。如果是正数,则补码与原码一样;如果是负数,则补码是在反码基础上,末位加1而得到。
详细解释链接: https://zhuanlan.zhihu.com/p/36036038       

  过程: 先找到100的二进制表示,然后根据按位取反再加一,得到-100的二进制表示,然后在加起来,转成十进制,就是结果。   

     今天新学了一种转二进制更快的办法: 比如a,a是个正数

    从左到右依次写下: 128 64 32 16 8 4 2 1,然后用a去除以每一位,能除就写1,然后保存余数,不能出就写0,原数不变;下次就用原数或者余数来除下一位,除完为止。最后从左到右就是二进制表示。

以100为例子   ()内表示余数

128     64       32      16        8         4         2          1

0      1(36)1(4)0(4)0(4)1(0)0(0)0(0)     100所以是0110 0100 所以-100 就是1001 1011 +1 =1001 1100

-100+(-100)=0011 1000 

转成十进制也可以用这个方法:

0      0   1   1   1   0   0   0        

128 64 32 16  8   4   2   1   先这样一位一位的写出来,有1的 就把下面数字加起来,32+16+8=56。

就这样一个很神奇的东西。

补充一个比较坑的点:

 

这个地方还有一点坑:

int main(void)
{
    char a,b;
    char c;
    
    a = -100;
    b = -100;
    c = a + b;

    printf("%d", a+b);

    return 0;
}

其运行结果是:-200

二、指针进阶和命名法则

int main(void)
{
    int a [5]={1,2,3,4,5};
    int *p = (int *)(&a+1);
    printf("%d   %d ",   *(a+1), *(p-1));
    return 0;
}

 其运行结果是 2    5 

a与&a是不一样的,虽然两者地址相同,但意义不一样,&a是整个数组对象的首地址,而a是数组首地址,也就是a[0]的地址

解释 2:  a是以int型为位移基准 a+1是第二个元素的地址,*取值后即为2

        5:  在这个定义中 int *p = (int *)(&a+1);  &a是以一个数组为位移基准,&a+1已经越界,是5后面的长度为5个int型的数组的第一个元素的地址(在数值上等于),然后强制转换成int *型,也就是变成按照int型为移动基准,p-1也就是移动到了5的位置,   *(p-1)即为5。 注意此处强制转换
 

int main(void)
{
    int a [5]={1,2,3,4,5};
    int (*p)[5] = (&a+1);
    printf("%d   %d ",   *(a+1), **(p-1));
    return 0;
}

 其运行结果是    2    1

看这个定义方式: int (*p)[5] = (&a+1);
命名法则分析: 顺时针按照token分析,p遇到(),不变,然后遇到*,*p是个指针,然后遇到[ ] ,指向一个数组,然后遇到int,数组类型是个int  型,然后遇到5,说明个数为5,综合:p是一个指向有5个整型元素的数组的指针。

 2不需要解释 1 :和上面一样,&a是以一个数组为位移基准,&a+1已经越界,是5后面的长度为5个int型的数组的第一个元素的地址(在数值上等于),然后p本身就是以5个单位为操作基准的,然后p-1又向前移动回来了,所以没变化,就是首地址,取值后就是1。

  为什么这个地方是**p取值呢?

int main(void)
{
    int a [5]={1,2,3,4,5};
    int (*p)[5]=&a;
    printf("%d %d %d %d",p[0],*p[0],*p,**(p));
    printf("%d %d %d %d",p[0]+1,*(p[0]+1),*(p)+1,*(*(p)+1));
    return 0;
}

其运行结果为 6422264            1                6422264                    1

                      6422268             2                6422268                    2

int (*p)[5]=&a; p是一个数组指针, *p是第一个元素的地址,p[0]也是第一个元素的地址,

取到第一个元素的值也就得 **p 和 *p[0]

进行位移操作时,注意 p[1] 是(以5个整型为操作基准的下)5后面的位置区域,等价于*(p+1)

所以在一个数组内进行位移操作时,必须是p[0]+1,*(p)+1,对应取值是*(p[0]+1), *(*(p)+1))

三、typedef

A:typedef不是简单的无脑替换,而是引入了一个新的类型。

例如:

typedef char* pchar;

int main(void)
{
    const pchar cstr = "aaaa";
    cstr = "bbbb";      // 这一行会报错 

    return 0;
}

为什么会报错呢?! 

 如果是简单的无脑替换,那么相当于

const char* cstr = "aaaa";
cstr = "bbbb";

其实不是。

const pchar cstr;  等价于  char* const cstr;
而不是   const char* cstr

关于const一个简单的判定方式:把定义中类型都去掉,看修饰什么

typedef是引入的新类型,pchar和 char*是一个等级的、等同的独立的类型,判定时候作为一个类型去掉,则剩下就是const str,即str本身不可变。

B:typedef可以只有一个参数,不一定是两个

样例代码:

typedef char(*p_to_char10)[10];

int main(void)
{
    char chs[10];
    p_to_char10 spz=&chs;
    printf("%ld %ld", chs , spz+1);
    return 0;
}

其运行结果: 6422274       6422284

可见这样typedef的新类型p_to_char10 是可以的

上题:比较有难度 结合了指针和typedef

求输出结果:

#include <stdio.h>
#include <stdlib.h>
typedef char(*AP)[5];
AP foo(char* p){
        for(int i=0;i<3;i++)
        {
            p[strlen(p)] = 'A';
        }
        return  (AP)p+1;
}
int main()
{
        char s[]="FROG\0SEAL\0LION\0LAMB";
        puts(foo(s)[1]+2);     // *(foo(s)+1)+2

    return 0;
}

其运行结果是: ONALAMB

分析:主函数中,把s地址传入foo函数,然后char*p这个指针接到首地址,进行for循环,strlen[p]第一次等于4(到第一个\0),所以p[4]='A',所以把第一个\0填上‘A’,下次运行strlen[p]=9,恰好把第二个\0变成‘A’,所以for循环跑完后,字符串变成了FROGASEALALIONALAMB,然后返回一个AP型的指针还要+1,AP是强制转换,因为函数的返回类型就是AP型。AP是证明类型呢,看typedef char(*AP)[5]; 是一个,指向有五个字符型元素的数组的数组指针,所以p就由原来的char *型转化成了AP型     (   char (*)[5]   )然后p+1,也就是按照新的位移基准,向后移动1个单位(5个char型),也就是由F指向了S,然后看puts里面的东西变成了puts(p[1]+2); p[1]相当于*(p+1) 也就是又向后移动1个单位(5个char型),现在指着L,然后+2,这地方有隐含的类型转换,*(foo(s)+1)+2这地方隐含着把AP型又转成了char* 型,因为+1 +2不是一个等级,这是一个数组指针,*(foo(s)+1)只表示元素的地址,+2后,要按照自己的基准(隐含转换)进行位移,所以指向了O,puts当前位置到\0处,所以是ONALAMB。

四、函数指针

样例代码:char *(*fp) (int , float *);

分析意义:fp是一个指向函数的指针 ,函数的参数是int和 float*,返回值是一个指针 指向char型

命名法则:返回类型 + (*p) (传入参数类型)

补充:sizeof(函数名)没有意义 默认是1。

五、C++

之前没有基础,关于引用问题没听,需要课下去b站找视频看。

 


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值