自定义博客皮肤VIP专享

*博客头图:

格式为PNG、JPG,宽度*高度大于1920*100像素,不超过2MB,主视觉建议放在右侧,请参照线上博客头图

请上传大于1920*100像素的图片!

博客底图:

图片格式为PNG、JPG,不超过1MB,可上下左右平铺至整个背景

栏目图:

图片格式为PNG、JPG,图片宽度*高度为300*38像素,不超过0.5MB

主标题颜色:

RGB颜色,例如:#AFAFAF

Hover:

RGB颜色,例如:#AFAFAF

副标题颜色:

RGB颜色,例如:#AFAFAF

自定义博客皮肤

-+
  • 博客(115)
  • 收藏
  • 关注

原创 13.2.4 位段

我们取得了uu这个变量的地址,这个地址它的类型是一个指向的struct u0的指针,然后我们把它强制转换为int*,即指向int的一个指针,然后我们再取这个指针所指向的那个int,然后再交给prtbin这个函数.在底层程序中,其实这种做法还是比较常见的.因为明明它是一个struct,可是呢我要把它当作一个int来做一些事情,所以我就用这种手段瞒天过海的把它给换过去了.不管怎么说,它就会说这个uu的大小是8.因为这时候的所有位数加起来已经超过一个int了,所以我们要用两个int来表达这个位段.

2024-10-30 20:23:51 202

原创 13.2.3 位运算例子

一开始还想去找chatgpt的)我们知道怎样去输出一个十进制,八进制,十六进制的数,它们都分别有不同的格式化字符用作输入输出.但是对于二进制我们并没有现成的格式化字符.我们可以通过写一个程序用于实现.看了这么多基础的运算,按位的与,按位的或,取反,移位,有什么用?1.输出一个数的二进制。

2024-10-30 17:16:43 257

原创 13.2.2 移位运算

我们对比看到,0xc和原来0x8往右移一位的结果一样.这是int的情况,那如果是unsigned int呢?我们很容易算出后者是前者的4倍,即2^2倍.我们最多能移的位数,取决于我们计算机里的int有多大.最高位的1往左移1位,同时在原来的位置上填入原来的最高位(即1).而无论有无符号,上面的a,b左移1w之后得到的结果都是0.

2024-10-30 16:40:48 146

原创 13.2.1 按位运算

c语言被称作是接近底层的语言,当然一方面,c语言的程序编译完以后,能够直接在机器上运行,我们目前看到基本上任何一台新机器,新硬件出来以后,c语言是唯一的可以在上面写程序的一个手段,当然再往下还有汇编,但这是另外一回事了.另外一方面,c语言本身提供了一些比较接近底层的操作,这些操作呢如果你不是要直接去操作硬件,直接去做一些非常底层的事情,你一般是用不到的.我们今天就来看一些这样的操作.对一个数a用b做按位异或操作,把得到的结果c用b再做一次按位异或操作,就会得到最开始的a.

2024-10-30 15:50:17 232

原创 13.1.3 二进制文件

这是为了匹配数组下标,如当你输入1的时候,会自动转成数组的第一个单元,它的下标为0.)交给read函数.我们首先打开了student.data文件,刚才是w的,现在我们是r的,我们要打开来读.然后如果打开成功,我们先fseek到seek_end,fseek的意思是,把我们读写的位置移到什么地方去,seek_end表示说从尾巴上开始,往前倒过来算,0L的意思是文件位置指针从文件末尾向后移动0个字节,就是在文件末尾不动.所以这个语句执行完之后我们当前的位置就在尾巴上了.

2024-10-30 15:31:43 905

原创 13.1.2 文件输入输出

当然上面这种不是一般的方式,一般的方式是说,我们要做一个file.在stdl.h这个头文件里已经给我们声明好了这种类型,叫做File*,即指向File的指针.所以我们需要定义一个File的变量,用fopen函数去打开这个文件.然后打开以后你用fscanf和fprintf来做文件的读和写.我们做的事情首先是运行它,因为我们已经有12.in在那了,所以我们就得到了12345.后面我们把12.in删除了,然后再运行,它会给我们输出"无法打开文件".创建一个新的文件 12.in,并等待用户输入内容。

2024-10-29 16:42:38 407

原创 13.1.1 格式化输入输出

如果我们做一个这样的输出,我们的编译器会给我们一个warning.我们先不管,运行得到结果57.原因是因为12345是以int类型传入的(4字节),但%hhd只会读取最低的1个字节,即8位.12345的二进制最低8位对应十进制57,因此输出的结果是57.(老师用十六进制做解释).我们可以在前面加个char做强制类型转换,这样就没有warning了.第一个1的意思是我们的scanf读到了一个"1234",而5的意思是我们的printf输出了五个字符(包括'\n').它把我们第一个输入的123给跳过了.

2024-10-29 15:31:33 488

原创 12.3.4 声明

当然,作为声明,你在写extern的时候后面不能给它初始化,因为初始化是定义一方做的事情.if是一条条件编译指令,意思是没有定义这个宏的话那么我们就定义这个宏,然后一直往下走走到endif.如果已经定义这个宏了,直接到endif的所有代码都不会出现,也就不会被编译了.这样就可以避免头文件里面有重复引用的情况.如果我们普通的写下了一个变量,那么这是一个变量的定义,如int i;.在前面加上extern,它就成了一个变量的声明. 在C语言中,我们需要在max里面放入这样几句话:在开头:加上。

2024-10-28 17:31:30 411

原创 12.3.2 头文件

我们的max和main在各自编译完成之后,然后链接器把他们链接起来的时候,链接器在printf链到那个max函数.这没有问题.它确实链到了正确的max函数,可是传进去的东西错了.传出来的东西也是错的.那怎么能够保证说,我在main这边对max的使用和在max这边,它所定义的那个max是一致的呢.我们需要有一个中间的媒介.这个中间的媒介就是头文件.以点h结尾的一个头文件.我们把max.c的原型放到.h文件里头,然后在需要用到max的地方,我们去引用他.怎么做呢?我们得到了一个奇怪的数字,这是为什么?

2024-10-28 09:31:50 411

原创 12.3.1 多个源代码文件

你看我们啊,总是在做这种分而治之的事情.一开始我们写的所有的程序就在main里头,然后写着写着呢,觉得啊这个main写的太大了,于是我们觉得呢,应该分出一些函数来,所以我们有了函数.在main里面,我们要去调用那些函数,把想应的一些功能剥离出来,放在一个个的函数里头,后来我们会发现说,你这一个.c文件里的函数太多了也不好,整个.c文件很大.于是呢我们又开始要做一件事情,把函数从一个.c文件里拿出来,放到很多个.c文件里头.这时候如果我们再在main下面编译运行,就能通过并且得到正确结果了.

2024-10-27 16:44:01 320

原创 12.2.2 带参数的宏

当它去代替函数的时候,运行效率会比函数来得高,当然因为它到处展开,所以它的代码的大小可能会比调用函数要来得大.但是呢,这是牺牲空间来换取效率的.而且这种宏可以做的非常非常复杂,因为以我们的习惯我们看定会在每个语句的末尾都加上分号所以第一句打印就会有两个分号.第一个分号表示的是打印语句的结束,第二个分号表示空语句,就使得后面这个else没地方可接了.我们想做的事情是从弧度转换为角度.可以看到结果并不符合我们的预期.其实,我们替换之后实际上得到的结果是这样的.还有,在这种宏定义的时候,后面千万不要加分号.

2024-10-27 15:52:58 307

原创 12.2.1 宏定义

都是以井号开头的,实际上这些井号开头的东西,,它并不是c语言的成分,但是呢任何一个c语言的程序都离不开他们,之所以说它不是c语言的成分,是因为这些东西,你看比如说include,它不是c语言的关键字啊,也不是只有c语言在使用,这些编译预处理指令,其他语言也可以去使用,那么我们之前看到的都是井号include啊,它是这个用来呃引入一个头文件的啊,这是我们到后面讲大程序结构的时候。我们再来说,我们先来看另外一个小东西.叫做#define.呃,这事我们之前没做过啊,,我们来做一下.

2024-10-26 21:28:26 410

原创 12.1.3 *返回指针的函数(指针函数),以及使用全局变量的tips

我们现在做的事情是这样的,我们有两个函数,f和g,f函数会返回一个指向int的指针,我们有一个变量int i=12,我们让它返回i的指针.我们在main里面把f返回的指针交给了*p,然后我们来检查一下*p所指的那个地方的整数是多少.显然我们期待它会得到12.然后我们又去调用了g函数,g函数里面有一个另外的变量k=24,我们打印一下k.g函数调用完了以后,我们再来访问p所知的那个地方的值,你还能期望得到12吗?然而,房东可能已经把房间“租”给了其他人(新的数据或变量),所以你的钥匙变成了“无效的钥匙”。

2024-10-26 12:17:29 400

原创 12.1.2 静态本地变量

如果对于一个本地变量,你在这个本地变量的类型的前面加上一个新的关键字叫static,这个时候这个本地变量就成为一种新的本地变量,我们叫他静态本地变量.本来我们说本地变量的生存期就在这个函数里头对吧,进去的时候才有,离开的时候就没有了.而我们这个这个静态本地变量有一个非常有意思的特性,你每次离开这个函数以后,这个静态本地变量他还在那,你下次再进去呢,它是上一次离开时候的那个值,而不是每次都做初始化,他的初始化只用做一次.这个静态变量呢它实际上是全局变量.为什么这么说呢?

2024-10-26 11:35:50 428

原创 12.1.1 全局变量

我们尝试让gall=f();编译器一样说不行.他看到的是你把一个变量的值赋给了g2.但如果我们在定义gall的前面再加一个const,它就可以了.当然我们非常不建议大家用这种方式来初始化像g2这样的一个全局变量,全局变量的值不应该和另外一个全局变量联系在一块.这事儿到后面我们讲大程序的时候你就会明白,我们现在的两个全局变量都是写在同一个.c文件里头的.它们的初始化的顺序是相对比较明确的.如果反过来就会有问题.另外,如果函数内部存在与全局变量同名的变量,则全局变量被隐藏.2.全局变量的初始化。

2024-10-26 11:03:38 479

原创 11.3.2 联合

如果我们说i=1234,那么它就会往那片空间写下去,1234所代表的16进制数(一头雾水).ok,我们把这个数找出来.十进制的1234换算一下16进制,得到0x04D2.所以它的这四个字节里面一定放着04D2的某种形式.我们的这个输出格式意思是要以十六进制的格式输出,而且这就是一个字节,你别给我做扩展了.02是显示两个十六进制数字的这么一个方式,如果不到10(指的是16)的话要补个0.然后它会给我们输出怎样的一个结果呢?等一下,好像跟我们看到的有点不一样?

2024-10-25 20:21:40 501

原创 11.3.1 类型定义

虽然我们已经知道怎么去做struct,我们可以通过struct这个关键字,我们去声明一个结构类型.声明出这个结构类型之后,我们可以用这个结构类型去定义变量.但你觉不觉得,我们每次在用这个结构类型的时候都要跟上struct这个关键字,和int float不一样,我们不能说point(结构类型)那个date(结构体变量),直接就像类型一样说我point什么什么,总觉得它好像不是亲生的对吧.c语言有一个办法让我们摆脱那个struct关键字.这个方法叫做typedef.typedef也是c语言的一个关键字.

2024-10-25 17:16:23 238

原创 11.2.3 结构中的结构

我们先定义了一个timeupdate函数,它的参数是一个struct time变量,它的返回类型也是一个s t变量.在main函数,我们先做了一个s t数组并把里面的每一个结构体初始化.然后我们再对这个数组做一个遍历.我们每一次都对一个数组单元,即一个结构体变量做输出,把它所有的成员变量输出,然后再通过调用t u函数,把它的返回值赋给这个结构体变量,最后再输出这个被赋予新值的结构体变量的每一个成员.如果我们之前就声明过两个结构体,那么我们可以再声明一个包含这两个结构体的结构体.

2024-10-25 16:30:36 363

原创 11.2.2 结构与函数

我们输入12 23,输出结果也是12 23,表明我们的结果。1.结构作为函数参数。二:结构指针作为参数。

2024-10-25 14:48:05 313

原创 11.2.1 结构类型

这是我们对结构体的集成初始化.我们看到我们有两种方式来做这个初始化.一种方式是说,today等于这个大括号,然后里面给了三个值,那你的编译器会知道说,你的这个07是给month,31是给day,而2014是给year.第二种方式是说,我可以具体指明里面的成员变量,然后通过点号给它赋值.这时候day会被默认赋0值,就像我们在给数组做集成初始化一样.我们来试一下这个程序.需要注意的是,出现在点号前面的一定得是具体的结构变量,而不是该结构变量的结构类型.结构变量才是实体.(这个t应该是多打出来的)

2024-10-24 17:17:48 957

原创 11.1.1 枚举

几乎可以说枚举在c里面是一个不那么成功的东西.c以前或者以后的语言基本做的都比c好.但是它毕竟是c语言当中我们可以去做的一种自定义的数据类型.我们还是需要去了解.可是呢我们更需要去知道说,在现实当中,人们拿c语言的枚举主要的目的是定义符号量,而不是把他当作一种枚举类型来使用.我们定义了三个变量,同时声明它们的值为const.现在呢,我们可以把这个事情往前再推进一步,我们用一个更加方便的方式去定义这样一些罗列起来的,可以排列起来的这样的一些数字,我们把它叫做枚举.2.套路:自动计数的枚举。

2024-10-24 11:57:18 276

原创 10.2.6 字符串搜索函数

这段代码做的事情是,我们找到了llo,然后我们让*p等于0,即让0填充p原来所指的第一个l的位置,那么s字符串就只有前面的he,我们的目的就达成了.然后我们把这个he拷贝到t里面去.当然,一般做完这个事情我们还要做一件事情,程序里没有写,就是*p=c,把l再写回指针p的那个位置,把s恢复回来.strstr是用来在一个字符串当中寻找另一个字符串的.strcasestr的作用是基于上一个函数,在寻找的过程中它会忽略大小写来做一个寻找.这两个函数我们不展开,大家有需要的时候再去自行使用好了.

2024-10-24 11:13:23 133

原创 10.2.5 strcat函数

函数名在中间多了一个n.在参数表多了一个n.n对于strcpy来说,你最多能拷贝过去多少个字符,对于strcat来说,你能够cat连接上多少个字符.也就是说我们可以通过这个方式告诉这两个安全版本的函数你最多只能拷贝过去这么多东西,如果多了怎么办,掐掉.因此它是安全的,不会越界.对于strcmp我们也有一个带n的版本, 但是这个n不是为了安全,而是说有时候我们想比较两个字符串的前面n个字符是否相等,如是否前面三个字符都是abc,如果是的话就返回0,大于就返回1,小于返回-1.1.strcat函数。

2024-10-24 10:36:18 140

原创 10.2.4 strcpy函数

这种时候就不能用我们的strcpy来进行拷贝.因为src和dst是重叠的内存区域,strcpy直接从源复制数据到目标,可能会在复制过程中破坏源数据.这样的后果是数据被破坏,导致未定义行为,性能下降等等.另外这个函数要小心的一点是,它第一个参数表示的目标区域,而第二个参数表达的是源.按照我们中国人的思维,我们说把a拷贝到b,一般都会先写a再写b,但是这个函数就反过来了.因为我们发现while的条件是不等于0,所以我们可以直接把后面这段去掉.变成while(src[idx]).这是数组版本,那指针版本呢?

2024-10-24 09:46:11 376

原创 10.2.3 字符串函数strcmp

我们把第二个字符串改成bbc.结果是-1.表明s1比s2小.如果改为Abc,这个时候的结果就是1,表明s1比s2大.那如果我们再改一下,把s2改成abc (有空格).结果是-1.它告诉我们第二个比第一个更大.其实这相当于拿\0的ascii码值减去空格的ASCII码值,得到-32,即s1小于s2,最后返回-1.(有些编译器会返回两者ASCII码的差值)结果为0,表示这两个字符串相等.这点不舒服的地方是不能用来作为if的条件.我们省去了对idx的定义,直接让指向这两个字符串的指针边往后走边对比.

2024-10-23 19:17:30 318

原创 10.2.2 字符串函数strlen

对于字符串,c语言提供了很多函数来帮助我们处理字符串,这是c标准库的函数.这些函数的原型包含在string.h里.我们可以写一个自己的mylen函数.我们先来看strlen函数.

2024-10-23 16:51:53 217

原创 10.2.1 单字符输入输出,用getchar,putchar

对应于putchar,我们还有另外一个函数叫做getchar.getchar不需要又有参数,它返回给你它从标准输入读到的一个字符.同样它的返回也是int,它的返回是读到的那个字符,为什么不是char而是int呢?因为它要返回eof来表示说这个时候输入结束了,输入为什么会结束?

2024-10-23 16:35:52 403

原创 10.1.4 字符串数组,以及程序参数

/这里第一个0起的作用是占位符.所以看完字符串数组,你还记不记得我们曾经在讲到switch case的时候, 我们看过这样一个例子:我们要根据输入月份的那个数字来输出对应月份的英文单词.

2024-10-23 15:33:37 439

原创 10.1.3 字符串输入输出

经测试,数组定义的时候word2[8]的地址在word[8]的前面,正好是连续的空间(这个涉及到一些后面的知识,不是说相邻定义的两个数组分配在一片连续的内存里,这只是一种特殊的情况).然后接收时多出来的第九个字符,'\0',被写到了word[0]的位置里.因为word[0]已经是'\0',那么打印的时候%s会识别word[0]就是字符串结束的地方,不再输出后面的字符.word2数组里面仍然是原样,所以被正常输出了.如何再制造一个字符串,然后让s指向那个新的字符串,这件事我们等学到字符串函数再说.

2024-10-23 11:38:18 294

原创 10.1.2 字符串变量

我们再定义一个字符串s2,用和s相同的字符串字面量来进行初始化.结果这两个指针被赋了相同的值.由此我们知道,这两个指针指向了同一个地址.我们发现.这个地址很小.我们再定义一个本地变量,我们输出它的地址,发现这个地址很大.由此我们知道,我们定义的这个本地变量和字符串s不在一个地方,它们离得很远.老师在编译的时候没问题,但是在运行的时候出错了.我自己在dev c++上编译和运行都能通过,但是确实没有输出s[0].看来是printf之前程序就已经出错了,也就是修改s[0]的值的那句代码代码出错了.为什么?

2024-10-23 10:22:10 309

原创 10.1.1 字符串

区别在于,我们在这个数组初始化的最后加了一个'\0',他的意思表示的就是0,你也可以把单引号去掉,再把反斜杠去掉,表达的效果是一样的,这个0就使得我们现在这个字符数组它是一个字符串了,这个word仍然是一个字符数组,这件事没有变,但是因为它最后有一个0,它就成为c语言的字符串.或者我们可以把逗号后面的这两个双引号去掉,在逗号后面加上\表示这行字符串还没有结束,下一行仍然是字符串的一部分.当然如果我们这么做下一行的table也会被输出,所以我们可以把下面的字符串放到行头.在定义之后编译器会帮你生成结尾的0.

2024-10-23 09:17:50 205

原创 9.2.2 动态内存分布

解决的办法:第一,牢牢地记住free,在你的程序一旦有malloc,就给它对上一个free,第二,你需要对你程序的架构有一些比较良好的设计,保证你的程序能够有恰当的时机去free它们,第三,当然这需要经验,需要多阅读别人写的代码,需要多写自己的代码,需要多在失败当中总结教训,然后不断地成长,光是读书是没有用的,编程是实践,所以纸上得来终觉浅,绝知此事要躬行。我们这段代码的意思是,用a来申请number个int的空间,然后遍历数组a,最后free a申请的那片空间.2.malloc函数。

2024-10-22 20:35:12 742

原创 9.2.1 指针运算

我们现代的这些操作系统,无论是Windows,Linux,Unix,macos,这些操作系统都是叫多进程的操作系统,它的基本的管理单元叫做进程,什么是进程呢,你double click一个东西,它运行起来了,它就是操作系统里面的一个进程,对于进程来说,最基本的概念是说,操作系统会给它一个虚拟的地址空间,也就是,所有的程序在运行的时候,都以为自己具有从0开始的一片连续的空间,那个顶是多少呢,如果是32位架构的机器,那个顶就是4GB,当然实际上你用不了这么多,我们很快就会探测你到底能用多少空间.

2024-10-22 17:22:27 375

原创 9.1.5 指针与const(本课限c99)

第一句话其实是什么意思呢,它的意思是,f说你给我一个指针,我保证我在我的函数内部不会去动指针所指的值.当然本来是一个const的值交给f也没有任何问题. 其实这种做法有点类似于我们传数组,我们的数组可以很大,1000个int,2000个int等等,那么我传整个数组进函数的时候,我传的其实是指针,我其实以指针的形式传进去.这是我们常用的一种传结构的方法.这样定义很容易会把i认为是const,但其实是不能通过这个指针去修改那个变量,不能用通过*p的操作来修改i的值.1.指针是const。2.所指是const。

2024-10-22 12:07:48 316

原创 9.1.4 指针与数组

由此可知我们可以直接把传入函数的一个数组a[0]直接写成*a.会有一些缺漏,因为最后老师没翻下面的代码出来.大概看一下就好.1.传入函数的数组成了什么?2.数组变量是特殊的指针。

2024-10-22 11:11:54 271

原创 9.1.3 指针的使用

这样是不合法的.我们知道,本地变量都不会默认赋初始值,所以p里面是一些乱七八糟的东西,比如ab23,于是如果把它当作地址的话,他可能会指向一个莫名其妙的地方,因此你说*p=16,他就会试图往那个地方写入16,而那个地方如果碰巧是一个不能写的地方,你的程序就会立刻崩溃.所以任何一个地址变量在没有被赋值之前,没有得到任何实际的变量的地址之前,你不能通过它用*去访问任何的变量,那都是没有任何意义的.在swap里面,我们的要的参数是两个指针,然后我们在里面所有的运算都是用*来做的.2.指针最常见的错误。

2024-10-22 10:40:59 370

原创 9.1.2 指针

后面是老师的经典名言,要求全文背诵:学计算机一定要有一个非常强大的心理状态,什么呢,计算机的所有东西都是人做出来的,别人能想得出来的,我也一定能想得出来,在计算机里头没有任何黑魔法,所有的东西只不过是我现在不知道而已,总有一天我会把所有的细节所有的内容,内部的东西全都搞明白了,那个scanf里面到底怎么做事情的,只不过我们现在才刚开始学习,我们还没有来得及去看,scanf的原始的代码是怎么样的,scanf也不过是个函数,也不过是某个人给他写出来的,那个人和我们一样,同样只是一个脑袋而已.(泪目了)

2024-10-22 09:49:46 387

原创 9.1.1 取地址运算

必须对一个变量去取地址,它不能对没有地址的东西取地址.如果它的右边不是一个变量就不能取地址.如int a,b;&(++a)等等都是不合法的.他们大小差值为4,也就是一个int.在内存中,两个相邻定义的变量是紧挨着放的,先定义的变量会在更高的地方,而先定义的变量会在更低的地方,它们分配在内存的堆栈中.在堆栈里我们分配内存给变量是自顶向下分配的.这两者不同是因为64位架构下地址的大小为8字节,而int为4字节,在32位架构下二者大小相等.所以我们可以知道地址的大小和int是否相等取决于编译器.

2024-10-22 09:06:15 298

原创 8.2.3 二维数组

行数是可以省略的,但是我们的列数是必须给出的,是不能省略的.由于这个数组是二维的,因此我们的大括号里面还得有大括号.事实上你可以把它想象成,说我们现在在定义什么呢,说我们现在有一个数组a,这数组里面的每一个呢,是一个五个的数组.所以这就顺理成章了,这个大括号表示的是有五个int的数组那么一个数组,它作为a[0],然后后面的是有五个int的数组,它作为a[1];同样的,如果你在集成初始化的时候有省略的,编译器会帮你补0.在c99里我们也可以用定位,但也得要用两个方括号的来做它的定位的初始化.

2024-10-01 19:54:52 1840

原创 8.2.2 数组例子----素数(难)

我们最开始做的代码是这样的,我们从2测试到x-1,逐一的测试说你这个数能不能把x整除,如果可以的话x就不是素数.所以我们这样的代码要走多少遍呢,对于n来说,我们要走n-1遍,因为从2走到x嘛.当n很大的时候就是n遍(和后面学的时间复杂度有关).这是我们用来估计这个程序效率的一种办法.效率在于它循环的次数,因为程序里头别的代码都跑得很快,比如像ifelse这种只用做一遍的,可循环是要你一遍一遍跑的,所以当n很大的时候,你循环的次数就很大,你循环的语句就会重复执行很多次.循环次数越多,效率越低;

2024-10-01 18:00:22 2095

patch3.xp3

patch3.xp3

2022-10-14

空空如也

TA创建的收藏夹 TA关注的收藏夹

TA关注的人

提示
确定要删除当前文章?
取消 删除