(C语言)请教一下能否将条件运算的返回值返回给定义的变量?

修正的部分内容的索引放在这里进行说明:
第一次修正:关于自定义类型那里进行了部分内容的修正
1.对语句部分进行了大程度的修正
2.对数组部分进行了大程度的修正
3.补上了位段的一系列操作
4.对文章中的一些地方进行了小幅度修改(使之更严谨,更容易阅读理解)
1.增加了文件操作部分的内容
2.对文章中的一些地方进行了小幅度修改。


我在开头这里提一下c语言标识符和关键字

c语言的标识符不能和标准库中的关键字重名,除此外,它由字母、数字、以及下划线组成,只能以字母或者下划线开头。这里两种命名方法,各举一个例子(这两种没有优劣之别,使用完全看个人习惯):

  • 这就代表以后可以用p_char 来代表指向字符的指针类型了。
    1、使复杂的声明变得简单,避免出错和提高代码的可阅读性
    2、可以隐藏指针,数组语法
    他们两个都可以给类型名更改一个名字,但是不同于typedef的是,#define只是简单的符号替换,并没有使一个标识符根本上成为一种已知的类型名( 我这么说你可能听不明白,没关系,我写一段代码,结合我的文字你绝对就理解了 )

第一段代码中的a是一个指向字符的指针,而b只是一个字符变量
第二段代码中的a、b都是指向字符的指针

还有,#define重命名的类型支持扩展,而typedef重命名的类型不支持扩展,看以下代码:

  • volatile【定义】:被这个关键字修饰的变量,当编译程序需要获取或者存储这个变量的值时,都从它的地址中来获取,而不要在某些情况下为了优化速度,从临时寄存器中获取(因为若是此时刚好别的程序通过地址更新了这个变量的值,寄存器中的那个值就会过时,从而出现错误)

在c++的编译器上这个输出结果是:

但是如果把定义num的那行代码改成这种

那么输出结果就会是我们想要的

一、基本数据类型(以下描述均在32位cpu环境下)

下面的书写的格式为: 所占位数->所表示范围

  • 下面我特地讲一下,浮点型和整形在内存中的存储方式

这里我们要知道什么是原码、反码和补码:
1)原码:将一个整数转换成二进制形式就是它的原码(即正数和负数的原码相同)
2)反码:负整数的反码就是它的原码除首位的符号位外其他位按位取反,正数的反码就是它的原码
3)补码:负整数的补码就是它的反码加一,正整数的补码就是它的原码

一切整数在内存中都是以它的补码的形式存储的,而读取的时候则转换回原码。这样的存储方式有两个好处
- 把加法和减法合并为一种运算

2>浮点型数据在内存中的存储方式

对于32位的浮点数来说(float):最高的一个bit位(示意图如下)是符号位,用1来表示负数,用0来表示正数。接下来的8个bit位用来表示指数,剩下来的23位均用来表示数值。
对于64位的浮点数来说(double),大体和32位相同

比方说数字12.125,它首先会转换成二进制的数字:。用指数表示法就是1.100001 x 2^3,又因为所有的数字转换成二进制指数表示法小数点前的数都是1,所以这个数字和小数点就都可以省略了(若是0.1xx怎么办?当然是转换成1.xxx *2^-xx的形式了。。。),然后将截取后的尾数放到尾数位,不满的则补0。指数位置的存储方式是取能表示的数的范围中的中间值(8位范围为0~255,所以中间值就是127),然后给2的指数减去这个数字(读取的时候再加上),然后转换成二进制的形式,然后放到指数的位置。

struct定义的结构体类型、union定义的联合体(共用体)类型、enum定义的枚举类型

这里提一下关键字typedef,typedef的用法就是给原有的数据类型起一个名字,方便以后的使用。例如:
以后再用到上面封装的那个结构体类型时,就不用使用struct Node来定义了,直接用Node来定义可以了,这样一方面时为了减少因为手误而出现的错误,一方面也可以使代码变得简洁。

    这里有一个易错的问题:”悬空else”,下面举一个例子说明:下面这段代码给我们由于缩进的原因给我们的直观感觉就是第一个if和下面的else对应为一对,但是实际上是第二个if和else相对应,因为else总是和上面紧接着的if相对应,但是这里的错误往往不易察觉!最有效的避免这种错误的一个方法就是给每一个if和else后面都加上花括号,将要要执行的代码括起来,这样即使出现缩进错误,也不会再出现上面的问题。
/*下面是修改后的代码,当然也不推荐这样子写,缩进最好还是要按照一定的 原则对齐,下面只是起到一个对照的作用*/
    这里我强调一下,最好不要用default来表示最后一种情况,否则你既丧失了case语句的自说明功能,又失去了default语句处理意外事件的功能。
switch case语句除了上述用法外,还有一个fall though的用法,下面说明这种用法

可以用好几个情况对应一种事件的发生,例如1~5都代表工作日,而如下代码就表示了这种用法的使用方式。事实证明,fall though在某些特定情况下还是非常有用的。。。

  • while(条件表达式)……
  • for(零条或多条语句;判断语句;零条或多条语句)

for语句相对于while语句的好处在于,它的使用可以使代码变得紧凑,且部分变量的增减位于头部,一目了然,不易出错。
do_while语句的作用和while语句类似,不同之处在于它至少要执行一次花括号内的代码块。

对于操作符来说,主要就三点内容:优先级(网上有详细的15级)、结合性、是否控制求值顺序(&&,||……)

左值:标识了一个可以存储结果值的地点。
在使用右值的地方都可以使用左值,但是相反则不一定

复杂表达式的求值是由三个因素决定的:操作符的优先级操作符的结合性,以及操作符是否控制求值顺序,相邻两个操作符究竟哪个先执行取决于他们的优先级,若是优先级相同,则再看结合性。这里我强调一下,优先级的那个表最好还是背过,因为虽然自己可以在所有要执行的运算两边加上括号,以避免优先级的问题,但是不代表别人也会那样,而背过那个表,就会避免很多不必要的麻烦。

PS:笔者是见得多了,用得多了就记住了,这个记忆方法网上还是有很多的,觉得吃力的读者可以去学习学习

1.概念:一组类型相同的数据的集合,在内存地址上从低到高依次排列(也就是说,定义数组时在内存上的栈空间申请一段连续的空间)

2.分类:一维数组和多维数组

(注意:这个维数可不是空间上的维数,所有类型的数组在内存上都是呈线性排列的,读者千万别被和谭书上类似的说法给误导了)

    1.在定义数组时对数组元素赋以初值;
    2.可以只给一部分元素赋值
    3.想使一个数组中全部元素值为0,可以写成:a[10]={0};
    需要注意 int a[10] = {1}; 并不能把数组初始化为全1,只是将第一位初始化为1,后面全部都是0滴.
4.在对全部数组元素赋初值时,可以不指定数组长度。
  • 多维数组的初始化(以二维数组示例)
  • 字符数组的初始化(举栗子)
    ( 若是中括号内的数值等于(或者没有数值)后 面赋值的元素个数,则b后面不会自动加上\0,若是大于赋值的元素个数,则其他的元素缺省为\0 )
    注意:数组只能在定义的时候一次给多个元素赋值,以后一次只能给一个元素赋值

4.定义:类型+标识符+[常量]/[(常量)][常量]……

这里需要注意的地方有以下几点: 1)数组名只有在:sizeof()里面,还有取地址的时候代表整个数组的长度,其他时候都代表数组首元素的首地址 2)数组在使用的时候可以采用下标的方式(eg:arr[3]),也可以采用解引用的方式(eg:*(arr+3))

指针和数组是什么关系呢?答案是:一点关系都没有
这里主要论述一下二者的不同

1>在多文件编程中的声明上来说,在A文件中定义为数组,在B文件中声明为指针是会出错的,定义为指针,声明……也一样会出错。 2>从举藕法来说,两个指针指向同一个字符串,更改一个指针的内容是会改变另一个指针指向的内容,但是两个数组若是都存放同一个字符串,更改一个数组的内容对另一个数组的内容并没有影响。 这是因为两个指针指向的字符串内容存储在字符常量区,而数组中存放的内容则存储在内存上的栈区。 3>看代码 因为数组名放在sizeof里面代表整个数组,而指针的名字永远代表一块地址,而地址在内存中永远占4个字节(windows32位平台下)

7.(柔性数组(和结构体))

柔性数组是什么呢?它定义在结构体的最后一个成员,但是它的前面至少要有一个成员。使用方法如下: 柔性数组的优点主要有这两点:
  • 1、方便内存释放,避免因为用户不知情的情况而导致内存泄露
  • 2、减少内存碎片,提高访问速度

在实际操作中,我们常常需要将一些长度不等的字符串保存在一个二维数组中,你大概会这么做:

这样做确实达成了我们的要求,但是这样操作明显有两个缺点:
1.你为了能够存放下所有的字符串,定义的二维数组第二维大于最大的那个字符串长度,但是对于相对很短的字符串的存储来说就浪费了大量的空间,比方上例中的存储 I 的那块空间
2.我们在实际操作中有时候并不知道需要多大的空间来存储要存储的数据,这样你这只能尽可能的分配更大的空间来达成我们的需求,这样以来,既有极大的不安全性,又有可能浪费大量的空间
为了避免这种问题,我们可以这样做>

这种操作就被称为“锯齿形数组”,由于 [] 的优先级高于 *,所以dst先和 [] 结合形成一个数组,然后数组里面存放的元素就是指针,指向要存储的字符串。

1.概念:实现某一个功能的一段代码块

2.原型(我把一些包括返回值类的注意事项都写在这个条目下)

作用:给编译器声明函数的信息,使函数不用重复定义就可以直接使用
模板: 返回值类型 函数名{参数列表}
1、返回值类型若是没有则写void,否则会缺省为整形类型。这里若是遗忘显式注明返回值类型,而恰好函数返回值不是整形(eg:float)则会出现问题
2、函数名的命名规则见开始的标识符命名规则
3、参数列表的格式:类型名称+变量名称(可写可不写,但是建议写上,因为这可以起到帮助程序员理解函数的作用!),……
若是没有参数传递,则注明void

3.函数调用原理(引出栈帧)

由于在CPU中,计算机没有办法知道一个函数调用需要多少个、什么样的参数,也没有硬件可以保存这些参数。也就是说,计算机不知道怎么给这个函数传递参数,传递参数的工作必须由函数调用者和函数本身来协调。为此,计算机提供了一种被称为栈(在调用的时候形成栈帧)的数据结构来支持参数传递。
栈是一种先进后出的数据结构,它由栈顶、栈底、以及一个指针构成。栈底通常不变,变的都是栈顶的位置。函数调用在传入参数的过程中,会按照一定的顺序将参数压入栈中(push),栈顶指针随之移动,指向最新的栈顶,当调用结束时,又将参数从栈顶弹出(pop)。
函数调用时,调用者依次把参数压栈,然后调用函数,函数被调用以后,在堆栈中取得数据,并进行计算。函数计算结束以后,或者调用者、或者函数本身修改堆栈,使堆栈恢复原装。

栈溢出的原理:函数在调用的时候就会给定一块栈帧空间,而如果操作使得数组的大小超出了给定的空间大小,从而覆盖掉函数的返回值地址,函数返回错误就会出现段错误。eg:你定义了一个具有10个元素的数组,但是在赋值的时候却超过了10个元素,这时候,多余的数据就会覆盖函数的返回值地址,这就是所谓的栈溢出。

常见的调用约定有:stdcall(pascal调用约定)cdecl(c调用约定)

在参数传递中,有两个很重要的问题必须得到明确说明:

  • 当参数个数多于一个时,按照什么顺序把参数压入堆栈
  • 函数调用后,由谁来把堆栈恢复原装

而调用约定就是用来解释说明这两个问题的。

5.库函数相关(常见的函数)

这里我简单的说明一下这些函数的作用,具体的实现网上都有,需要的朋友可以去查

  • 这里要注意两个字符串相等的时候

  • 函数说明: 这个函数用于将字符串source复制在字符串target的后面,若是字符串target的长度比source的长度长,则在复制过去的source后面自动加上\0,即覆盖以前的字符串,但是相对应的,要是……比……短,则不会在后面加上\0,这样用%s输出的时候就会出错!!!

  • 函数说明:这个函数用于求字符串的长度,需要注意的是函数的返回值是一个无符号类型的整数,也就是说,需要注意向下面这种情况:

恐怕这句“OK!”哪种情况都是无法输出的,因为strlen()的返回值是一个无符号类型整数,而两个无符号类型整数运算的结果也是一个正整数,所以……所以如果有需要,则要将strlen的返回值强制类型转换成int型

    函数说明:从字符串dst中查找字符cc(注意这里是整形,用的时候要用单引号括起来),函数返回第一次找到cc的位置,若未找到,则返回NULL,类似的还有strrchr(这个函数是从后向前找)

    函数说明:它从字符串str1中隔离各个单独的称为标记的部分,并且丢弃分隔符。
    这个函数还是相当重要的,我在这里写一段代码来说明一下它的使用方法:

这个函数若用于将一串字符中想要去掉的一些字符拿走,将这些字符前后的字符连起来,则可以封装成一个函数,需要用的时候直接将字符串作为参数传进去还是很方便的!

3) 字符分类函数和字符转换函数

这里我列举几个常用的函数

  • isdigit:判断是否为数字(十进制),是则返回真
  • isspace:判断是否为空白字符,同上
  • isupper:判断是否为大写字母,同上
  • islower:判断是否为小写字母,同上
    • isalnum:判断是否为数字或字母,同上
    • tolower:把大写字母转换为小写字母

4)I/O家族(具体放到文件那里讲)

7.不定参数(它其实是由宏实现的,但是它的用法又体现在函数这里)

详情参见我的另一篇博客:

  1. 预编译阶段(^.i):这个阶段主要是处理预处理指令,包括#difine定义的宏常量等
  2. 编译阶段(^.s):这个阶段就是对代码进行词法分析,语法分析,语义分析,符号汇总
  3. 汇编阶段(^.o):形成符号表,将c代码转变成汇编代码,再转换成二进制机器码,从而形成可执行程序。
  4. 链接阶段:合并段表,符号表的合并以及符号表的重定位

这里易出现的问题有:带歧义的宏、宏的副作用(i++和i+1)

按照命名约定,定义宏名时通常为大写字母。

下面做一下函数和宏的优缺对比

1、从代码量上来说,函数是将一段代码块封装成一条代码(即函数调用),所以它的使用可以减少一个项目中的代码量。但是宏替换每一次都会将一段代码插入到项目代码中,从而增加项目代码量。所以除非宏非常短,否则就使用函数来代替宏的功能
2、从执行速度上来说,由于函数的使用存在额外的返回/调用开销,所以在这点上,宏普遍比函数快
3、从参数类型上来说,函数的参数必须声明为已知类型,也就是说,它的使用一定程度上受到了参数类型的限制。还有函数的参数不可以传递类型。而宏是与类型无关的,而这个特性既是优点,也是缺点(不严谨)。
4、从操作符优先级上来说,由于函数只在函数调用时求值一次,所以它不会存在由于邻近操作符优先级的问题而产生不可预料的结果。
5、函数没有宏的歧义性
6、宏无法调试,因为它在预编译阶段就已经完成了操作

这里说明一下#和##的作用

“#”:把一宏参数变成对应的字符串
//这里利用了相邻的字符串自动连接在一起的特性(c99)
“##”:将两个位于它两边的标识符连接在一起形成一个新的符号(当然,连接后的新标识符必须合法),它还允许宏定义从分离的文本片段创建标识符(示例如下)

上面这段代码的结果是给sum1加上10

  • 原来除了注释掉代码外,还可以这样!!!(调试性编程)
    eg:有些调试性的语句只要进行选择性的编译便能够实现它的功能而且不用删除这些语句

在文件的开头加条件编译指令:

代码块(函数声明,定义的宏,定义的类型……)

1.概念:内存中字节的编号即为指针

    指向自定义类型的指针(eg:结构体指针……)

两个指向相同数组的指针相减的值是他们相差的元素个数,指针不可以相加。
指针变量名+n代表指针指向向前跳n个指向元素类型的偏移量

我在这里举几个复杂声明的例子来说明问题(附解析)

这是一个什么东西!?别晕,听我讲。
看到这种复杂的声明,我们首先从标识符入手,它的标识符是signal,左边是星号,右边是括号,但是由于括号的优先级高于星号的优先级,所以signal首先被解释为一个函数,既然是函数,那么signal后面紧接着的那个括号内肯定就是参数了,第二个参数是一个指针,指向一个返回值为空,具有一个整形参数的函数,然后再来看signal的返回值。首先我们可以看到它的返回值是一个指针,指向什么呢?这时你可以把signal左边的括号去掉,就剩下了 void 空缺(int),这时就一目了然了,指针的指向就是一个返回值为空,有一个整形参数的函数。
这时我们总结一下,signal就是一个函数,这个函数有两个参数,一个整形,一个指向(返回值为空,具有一个整形参数的函数)的函数指针,返回值为一个(返回值为空,具有一个整形参数的函数)!
一般碰到这种情况我们可以这样来简化声明:将重复出现的类型用typedef关键字进行重新命名,然后再声明就会变得简单多了,比方说上面这个:

我们来看,取arr的地址加1代表将数组的地址整个向后偏移一个数组长度,然后再强转成int * ,此时的地址类型由数组变成了整形,再赋值给ptr1。ptr-1,根据指针运算,减一就代表减一个类型的步长,它的类型是整形,也就是此时ptr1由指向arr数组后面那数组大小的地址的开始向前偏移一个整形大小,那么它此时就指向arr数组的末尾元素,解引用的结果就是10。第二个,(arr+1),从数组那里我们可以知道,数组名大多数情况下都代表数组首元素的地址,这里数组arr的首元素是一个一维数组,给它加一,就相当于偏移了一个一维数组的长度,此时它的地址就是数组arr第二个元素,对它解引用,翻译成另一种你容易理解的形式就是arr[1],而这代表第二个元素的地址,再将其转换成int *,输出的时候减一就相当于向前偏移一个整形的长度,所以第二个输出结果为5

//这个例子貌似在网上广为流传,我在这里做一个解析

c是一个指针数组,cp是一个二级指针数组,cpp是一个三级指针。第一个输出:先对cpp进行自加一,此时它指向cp[1],而cp[1]又指向c+2,所以对其两次解引用的结果是:POINT。第二个输出:cpp前面的操作符的优先级都高于加法操作符的优先级,所以我们先看前面,先对cpp自加1,此时它指向cp[2],对cpp一次解引用得到c+1,然后再自减1得到的是c,对其解引用再加3,它的输出是:ER。第三个输出:cpp[-2]存放的是c+3的地址,对其解引用得到的结果加三也就是:ST。第四个输出:cpp[-1][-1]的意思翻译成另一种形式就是 * ( *(cpp-1)-1 ),也就是c+1,然后对解引用结果再加1,所以输出结果就是:EW。


回调函数(参见qsort()函数的使用)

1.概念:将一堆类型相同的数据或者类型不相同的数据放在一(注意不能放函数,这里不同于c++)

2.结构体大小(内存对齐->原因?方法?)

说到结构体的大小,就不得不提一下内存对齐

内存对齐(性能原因):数据结构中内存需要对齐到自然边界上,原因在于由于CPU访问未对齐的内存需要两次访问,而访问对齐的内存只需要一次,提高了效率
1。结构体中的第一个成员实际操作(后面有解释为什么是实际操作)上不需要内存对齐,也就是说它在与结构体偏移量为0的地址处。

2。其他成员需要对齐到对齐数的整数倍的地址处。(对其数:编译器默认的一个对齐数字和该成员本身大小的较小值< Linux下:4 Windows下:8>)

3。结构体的总大小等于成员最大对齐数的整数倍(包括第一个成员,这就是前面为什么是实际操作上)

4。如果是嵌套结构体的结构体求大小,内部结构体对齐到它成员的最大对齐数的整数倍处,而外部结构体的大小则是所有成员的最大对其数的整数倍(包括内部的结构体的最大对其数)。

  • 1>原理及注意:联合体和结构体的定义很相似,不同之处在于联合体内部的成员是共享一段内存的,而结构体是分别享有一段内存,( 这里联合的大小至少要大于最大的一个成员的字节数)这里需要注意:如果联合内部的成员字节数相差太大的话,就会造成一定程度上的内存浪费,所以我在这里建议将联合体内部的成员声明为指针,这样就能最大程度上的节省内存。(如果你的程序里面有1万个联合体……)
    2>用法:如果你需要使指定内存区域在不同时刻具有不同类型的值,则使用联合会是一个好的方法
    3>联合还可以用于判断大小端

当数值的低位段存储在内存的低地址处,这种计算机模式被称为小端模式,反之则被称为大端模式。(这里列举一种用联合体判断大小端的方法)

位段的声明和普通的结构类型相同,但是它的成员是一个或者多个bit位的字段。这些不同长度的字段实际上存储在一个或者多个整形变量中。(概念看不懂不要紧,往下看,最后学会位段的使用就行)

从它的概念中我们就可以看出,位段必须声明为int,signed int或unsigned int类型,其次,在成员的后面加上冒号,冒号后面为位段所占的位数。(这里我强调一下,最好将位段显式声明为signed /unsigned ,不要为了省事而只写一个int,因为只声明为一个int,它被解释为unsigned 还是signed事要根据编译器决定的!)

1.最大的好处在于,它可以节省存储空间,尤其是需要成千上万个这种结构的时候!
2.位段可以很方便的操作一个整数的部分内容。(常见于操作系统的设计代码)

    向函数传递结构参数的效率非常低下,通常可以采用传递指向结构的指针的方法

    位段在本质上是不可移植的

    联合在实现变体记录(内存中的某个特定区域在不同时刻具有不同类型的值)的时候非常有用

    联合变量初始化的时候必须与第一个变量的类型相匹配

九、内存管理(分配的内存都在堆区)

malloc的实现原理涉及到内存池的概念

所谓野指针即没有确定指向的指针,这时候若是对其解引用操作就会出现操作异常失败。野指针通常出现在使用free释放一块内存后,忘记将指针指向NULL,而后面却对其解引用操作。

    注意每一段申请的内存在使用完毕后都要使用free来释放掉,否则,就会导致内存泄露的问题

    进行内存分配的时候要对函数的返回值进行检查,eg:

    这里要注意realloc的返回值有三种可能:其一为原内存后面空闲内存的大小足够新分配的内存,所以直接返回原内存的首地址;其二为原内存后的闲置内存不够分配,所以重新找了一块空间分配,而原来的内存则丢失掉;其三为新找了一块内存还放不下,所以返回NULL,而原来的内存而也会丢失掉,所以在使用realloc的时候,事先准备一个指针来接受新分配的内存,若是分配成功,则赋值给原来的指针,否则就报错,这样原来分配的内存就不至于丢失。

1.打开文件和关闭文件
“打开”函数说明:第一个参数filename是要打开的文件路径(包含文件名称),第二个参数mode是指对文件操作的权限,一般常用的权限有:

r: 以只读的形式打开文件,文件必须存在
w: 以只写的形式打开文件,若是文件已经包含内容,则覆盖其内容,若是文件不存在,则在该路径创建目标文件并写入内容。
a: 以追加的形式打开文件,若是文件已经包含内容,则在其后面继续添加。若是文件不存在,则在该路径创建目标文件并写入内容。
b: 二进制文件,和上述三个操作联合使用,eg:rb,wb。
t: 文本文件,可省略不写。

“关闭”函数说明:文件正常关闭时,fclose() 的返回值为0,如果返回非零值则表示有错误发生。
注意:基于防御性编程的原则,通常要对打开文件文件操作进行检验,一般的格式可以为下(打开当前目录下的文件filename):

最后一定要记得对打开的文件使用fclose()进行关闭。

2.以字符形式进行文件操作
说明:两个函数若是读取/写入成功,则返回读取的字符数目。若是失败,返回EOF(具体是哪个数值要看编译器是怎么规定的,一般为-1)
在文件内部有一个位置指针,用来指向当前读写到的位置,也就是读写到第几个字节。在文件打开时,该指针总是指向文件的第一个字节。使用fgetc(fuptc) 函数后,该指针会向后移动一个字节,所以可以连续多次使用fgetc(fputc)读取(输出)多个字符。
feof函数用来判断文件操作指针是否到达文件的末尾,若是,则返回非零值,否则返回零。ferror函数用来判断文件操作是否出错,是则返回非零值,否则返回零值。
3.以字符串的形式文件操作
函数说明: dststr 为字符数组,n 为要读取的字符数目,fp 为文件指针。
函数返回值,读取成功时返回字符数组首地址,也即 str;读取失败时返回 NULL;如果开始读取时文件内部指针已经指向了文件末尾,那么将读取不到任何字符,也返回 NULL。
遇到换行时,会将换行符一并读取到当前字符串,然后结束本次读取操作。这点和gets()不同,gets()会自动忽略换行符。还有,在设置fgets的接收参数时,要将其空间设置为需要接收的文件字节数加一,这是因为fgets每次读取的字符个数为n-1,最后一个字符默认置为NULL。假如目标文件中目标行的字符数目大于n-1,则fgets函数读取n-1个字符后结束本次操作,下次读取继续从上次未读完的行读取字符。

2.fgets函数不能用于读取二进制文件的内容!

什么是随机读写文件呢?顾名思义,就是可以在文件的任意位置进行文件读写。要实现这个操作的关键就在于怎样按照想法移动文件内部的位置指针,称为文件的定位
说明: 将文件内部位置指针放到文件的开始位置
说明: offset为移动的距离
orign为开始移动的位置,规定有三种:
**注意:**fseek函数通常用于二进制文件的操作


PS:这是作者的脑力劳动成果,希望广大网友转载可以注明出处

在C语言中,if语句后的一对原括号中,用以决定分支的流程的表达式().

C语言源程序文件经过C编译程序编译后生成的目标文件的后缀为().

在C语言中,要求运算数必须是整型的运算符是().

a,b为整型变量,二者均不为0,以下关系表达式中恒成立的是().

C语言中要求对变量作强制定义的主要理由是().

若变量均已正确定义并赋值,以下合法的 C语言赋值语句是().

C语言中的标识符只能由字母,数字和下划线三种字符组成,且第一个字符().

下列四组选项中,均不是C语言关键字的选项是().

整型变量x=1,y=3,经下列计算后,x的值不等于6的是().

设C语言中,一个int型数据在内存中占2个字节,则unsigned int型数据的取值范

在C语言中,int、char和short三种类型数据在内存中所占用的字节数().

以下标识符中,不能作为合法的C用户定义标识符的是().

若变量a与i已正确定义,且i已正确赋值,合法的语句是().

C 语言中以下几种运算符的优先次序()的排列是正确的.

下面四个选项中,均是不合法的用户标识符的选项是().

选择结构中的条件与循环结构中循环成立的条件,在写法上可以是任一表达式,但其值只能被判断为"真"或"假".哪个数作为逻辑"假"值().

为了避免在嵌套的条件语句 if-else 中产生二义性,C语言规定:else子句总是与

已定义ch为字符型变量,以下赋值语句中错误的是().

从循环体内某一层跳出,继续执行循环外的语句是().

printf函数中用到格式符%5s,其中数字5表示输出的字符串占用5列,如果字符串长度大于5,则输出按方式().

为表示关系x≥y≥z,应使用C语言表达式 ().

结构化程序设计所规定的三种基本控制结构是().

若有条件表达式 (exp)?a++:b--,则以下表达式中能完全等价于表达式(exp)的是

下列四个选项中,均是C语言关键字的选项是().

以下标识符中,不能作为合法的C用户定义标识符的是().

下列关于单目运算符++、--的叙述中正确的是().

整型变量x和y的值相等、且为非0值,则以下选项中,结果为零的表达式是().

已知i、j、k为int型变量,若从键盘输 入:1,2,3<回车>,使i的值为1、j的值为2、k的值为3,以下选项中正确的输入语句是().

能将高级语言编写的源程序转换为目标程序的是().

以下标识符中,不能作为合法的C用户定义标识符的是().

C语言源程序文件经过C编译程序编译连接之后生成一个后缀为()的可执行文件.

已知大写字母A的ASCII码是65,小写字母 a的ASCII码是97,则用八进制表示的字符常量'\101'是().

下列字符序列中,是C语言保留字的是().

以下标识符中,不能作为合法的C用户定义标识符的是().

先用语句定义字符型变量c,然后要将字符a赋给c,则下列语句中正确的是().

在C语言中,为了结束由while语句构成的循环,while后一对圆括号中表达式的值应该为().

下列字符序列中,可用作C标识符的一组字符序列是().

在位运算中,操作数每右移一位,其结果相当于().

以下程序段中,能够正确地执行循环的是 ().

下面四个选项中,均是不合法的浮点数的选项是().

设C语言中,int类型数据占2个字节,则 long类型数据占().

下列字符序列中,不可用作C语言标识符的是().

下列标识符中,不合法的C语言用户自定义标识符是().

从键盘上输入某字符串时,不可使用的函数是().

设以下变量均为int类型,则值不等于7的表达式是().

判断两个字符串是否相等,正确的表达方式是().

C语言中用于结构化程序设计的三种基本结构是().

若变量已正确定义并赋值,下面符合C语言语法的表达式是().

以下数值中,不正确的八进制数或十六进制数是().

在C语言中,char型数据在内存中的存储形式是().

以下关于long、int和short类型数据占用内存大小的叙述中正确的是().

C语言规定,在一个源程序中,main函数的位置().

判断char型变量cl是否为小写字母的正确表达式是().

C语言的if语句嵌套时,if与else的配对关系是().

下面有关 for 循环的正确描述是().

能正确表示逻辑关系:" a≥10或a≤0 "的C语言表达式是().

以下常量中,能够代表逻辑"真"值的常量是().

C语言中,switch后的括号内表达式的值可以是().

若希望当A的值为奇数时,表达式的值为"真",A的值为偶数时,表达式的值为"假",则以下不能满足要求的表达式是().

putchar函数可以向终端输出一个().

任何一个C语言的可执行程序都是从()开始执行的.

关于建立函数的目的,以下正确的说法是 ().

一个算法应该具有"确定性"等5个特性,下面对另外4个特性的描述中错误的是

算术运算符、赋值运算符和关系运算符的运算优先级按从高到低的顺序依次为().

以下关于运算符优先顺序的描述中正确的是().

在C语言中,要求运算数必须是整型或字符型的运算符是().

C 语言程序的三种基本结构是顺序结构、选择结构和()结构.

在C语言中,能代表逻辑值"真"的是().

在以下给出的表达式中,与 do-- while(E)语句中的(E)不等价的表达式是

以下语句中,循环次数不为10次的语句是 ().

设C语言中,int类型数据占2个字节,则 short类型数据占().

为了提高程序的运行速度,在函数中对于整型或指针可以使用()型的变量.

结构化程序由三种基本结构组成,三种基本结构组成的算法().

设变量a是整型,f是实型,i是双精度型,则表达式10+'a'+i*f值的数据类型为().

我要回帖

更多关于 c语言必须有返回值 的文章

 

随机推荐