本文旨在记录学习复试科目C语言中所遇到的问题或是一些知识点,以便加深印象以及以后复习
一、C语言概述
1、输出已声明但未赋值的参数
在VS2019下,无法通过编译,会报错“未初始化”;
在CodeBlocks下,能通过编译,但结果是随机数。
至于为什么在VS2019下会报错,可能是编译器不同的原因吧,不太确定,但这侧面反映出VS2019的强大。
2、关于形参和实参
顾名思义,形参仅代表这里是一个参数,但不是真正使用的参数,而实参才是具有实际含义的参数。
#include
void main(void) {
int num_count;//one of a declaration
int a=5, b=6 ;
num_count = Sum(a, b);//这里的a,b就是实参
printf("Sum = %d",num_count);
}
int Sum(int a,int b) {//このa and b are both 形式参数
return a+b ;
}
3、C语言的bool类型
说来惭愧,接触了这么长时间才知道,以前手写的伪代码都是基于C++的,只有C++才支持“bool”的写法,
而C语言的布尔类型需要
1)、自己定义仿布尔型
#define TRUE 1
#define FALSE 0
2)、C99引入关键字“_Bool” 来定义布尔型变量。_Bool类型长度为1,只能取值范围为0或1。将任意非零值赋值给_Bool类型,都会先转换为1,表示真。将零值赋值给_Bool类型,结果为0,表示假。
3)、在C99标准下,引入#include
二、C语言基本数据类型
1、short类型的取值范围
书中有一例:
long i= 65537;
printf("i=%hd",i);
结果为i=1;
在书中得知当下最常见的计算机设置short为16位,下面又写到short的取值范围是[-32767,32767],因此若根据数据溢出的观点可知65537超过了short类型可表示的上限,会重新从最小值-32767开始往上加,但我却怎么算都是2。
后来在VS2019和CB都运行了以下程序后:
int main()
{
short i = 32767;
short j = i+1;
printf("short类型的i=%hd\n",i);
printf("short类型的i+1=%hd\n",j);
}
得到结果
因此可知,short类型的取值范围实际是[-32768,32767]。后上网查阅后确实如此,书上这个地方有误。
2022.3.19准备复试C语言,开始记录关键知识点:
标识符组成为字母、数字、下划线,数字不能开头。
C语言区分字母大小写。
变量:变量名实际是一个符号地址,编译时由系统给变量名一个地址,从程序取值时,首先变量名所在的地址中存放的是变量值存放在的内存单元地址,通过这个地址取得变量值。
声明(定义)变量:int a,b,c; 变量初始化:a = 1;b = 2;c = 3;
各个基本数据类型所占空间大小:
char:1字节
short int、无符号short int:2字节
int、无符号int、float:4字节
long int、无符号long int、double:8字节
float提供7为有效数字,double提供15-16位有效数字。
无符号整数与有符号整数的转换:
数值在内存中以补码的形式存放,并且最高位0为正,1为负。
字符变量char在内存中存放的是字符的ASCII码的二进制码,与整数的存储形式类似,因此C语言字符数据和整型数据可以通用。但注意char只占4个字节,只能存放0~225范围的整数
数据类型转换:
分为自动转换与强制转换:
1)自动:发生在不同类型的数据混合运算时,由编译系统自动完成:
转换保证不降低精度,因此往占位多的方向转,如int和long混合运算,int会转换成long、float和double混合运算,float转换为double,char和short参与运算,先都转换为int。int、double、float混合运算,都先转换为double。
赋值运算中,=两边数据类型不同时,先把右边的转换成左边的类型,然后再赋值。因此可能会使得右边长的数据类型多的位被舍弃,使得精度降低。
转换表:
根据转化表可知,当int和float进行混合计算时,都会先转换为double型,然后进行计算。
2)强制转换
表达式:(类型)(表达式),如(float)36:把36转换为float型;(int)x:把x转换为int型。
运算符:*在运算符里表示乘,指针中为取指针指向地址的值;
&在运算里表示双目按位与运算,指针中为取变量的地址。
除运算符/ 当都为整型时,结果为整型,当两个中有一个为实型时,结果为double型。
赋值运算符
"="两边数据类型不同时,先把右边的转换成左边的类型,然后再赋值。
符号扩展:1)将char赋给int型时,将char的8位赋值给int的低8位,高位有两种情况:若把char为无符号的unsigned char,则高位补0;若为有符号的,则字符最高位是1,则int高位补0,字符最高位是0,则int高位补1。2)int赋值给long型,低位为int的数值,若int为正值,则long的高位补0,若int为负值,则long的高位补1。3)unsigned int付给long int时,直接long的高位补0。
逻辑运算符:&&与,||或,!非
逗号表达式:逗号表达式作用是把若干表达式连接起来,优先级在所有运算符中最低,结合方向从左到右。一般形式:表达式1,表达式2:先计算表达式1值,再求解表达式2的值,整个表达式的值为表达式2的值。如x=8 * 2,x * 4; 第一个表达式为”x=8 * 2“,此时x的值被赋值为16,然后表达式2”x * 4“ 值为64,因此整个表达式为64。
位运算符:&按位与、|按位或、^按位异或、~按位取反、<<左移、>>右移,左移运算过程中,高位丢弃,低位补0;右移运算过程中,右端移出的二进制数舍弃,左端移入的二进制数分情况:对于正整数与无符号整数,高位补0,对于负数,高位补1。
异或运算规则:相同为0,不同为1。
#include叫 “预编译命令” #define叫宏定义。
字符输出、输入函数:
putchar(参数); 标准字符输出函数,参数可以是字符变量、整型变量或字符型常量。
getchar(); 标准字符输入函数,从键盘中读取一个字符。函数返回值为输入的ASCII码。
求最大公因数
枚举法:求最大公因数时定义一个temp变量,初值为两个数中小的那一个,然后用这两个数同时对temp求余,余数同为0则该数即为最大公因数,否则则temp–;
欧几里得算法:算法原理:两个整数的最大公约数等于其中较小的那个数和两数相除余数的最大公约数。
最小公倍数算法
求最小公倍数时,定义两个变量分别保存较大和较小的数,然后用较大的数模较小的数,若为0,则较大的数即为最小公倍数,若不为0,则较大的数依次从1开始自乘,直到模为0。
数组初始化:二维数组定义不能省略第二维的长度,在省略第一维的数字时也必须能够使编译系统给能够明确或计算出来。
比如:a[][2] = {{1},{0}};//系统能够明确第一维为2
a[5][5] = {0};//数组整体全部都赋值为0
a[5][] 是错误的,第二维度不能省略。
简单说,就是二维数组的初始化时,第二维度的数字不能省略,省略第一维度的数字时也要使编译系统明白是多少。
字符串可用字符数组表示,最后会默认以’\0’为结束符。字符串有效长度不含结束符。%c和%s分别对应格式化输入输出字符和字符串。
字符数组相关函数:
gets(s)、pust(s):gets()用于从键盘读取一个字符串,并存入字符数组中。与scanf()的用%s输入的区别在于scanf()把回车和空格都看作字符的结束标志,而gets()则只把回车当作结束标志,空格则为字符串的一部分。
puts()用于把字符串输出到显示器,且将结束符转换为换行符,即会自动换行。
strlen(s); 返回字符串有效长度,不含结束符。
strcat(s1,s2);删除s1的结束符,将s1与s2连接起来都存入s1数组,前提是s1足够大。
strcmp(s1,s2); 按照ASCII码顺序依次比较字符数组中的字符大小。相等返回0,小于则返回小于0的数,大于则返回大于0的数。
stccpy(s1,s2,n); 把s2的字符连同结束符复制到s1数组中,s1中原来的s2长度的字符被覆盖,s1数组的长度一定要能放得下s2。n可以指定复制几个字符到s1的前n个字符,为可选参数。
stcupr(s); s中所有小写字母变为大写。
stclwr(s); s中所有大写字符变为小写。
函数相关:
函数的形参:形参在函数被调用前不占内存,只有在函数被调用时才被分配内存单元,调用结束后空间被释放。C语言规定,实参与形参的数据传递方式是值传递,在内存中形参和实参占不同的内存单元。
值传递与地址传递:值传递时形参与实参是占不同的内存空间,形参的变化影响不到实参;而地址传递则是把实参的地址传给形参,此时操作形参就是对实参的操作,这就实现了形参与实参的双向传递。当实参与形参均为数组时,为了保证形参数组长度足够,通常形参不指定数组大小,如a[]。以数组名为实参和形参时,传递的是数组的首地址。用数组名做参数要注意在被调函数和主调函数中分别定义数组。
函数的调用声明:若函数定义在调用前,则不用声明;若在函数的外部已做了函数声明,则在各个主调函数也不用再声明。
全局变量:在函数之外定义的变量,又称外部变量,有效范围为从定义位置到文件结束。所有函数都可以使用。全局变量不符合低耦合的原则,没有必要不使用。
变量存储方式:静态和动态。静态分配是在程序运行期间分配固定的存储空间,而动态分配则是在程序运行时按照需要进行动态存储分配。
局部变量的存储类别:关键字:auto:动态 register:寄存器型存储 static:静态 。函数中的变量不作声明默认都是动态分配的,调用函数时给变量分配空间,调用结束后释放。static定义的是静态变量,存储在静态存储区中,当函数调用结束后,静态变量并不会被释放,下次再调用函数时该静态变量的值就是上次调用结束后的值。register定义的变量存放在CPU的寄存器中,通常只适用于自动局部变量和函数的形式参数变量,寄存器变量常用于在同一变量名频繁出现的地方。
全局变量可使用static和extern说明(注意不是定义),引用全局变量的函数在该全局变量定义前时,需要用extern说明这是一个全局变量。全局变量的定义只能出现一次。static说明的全局变量为静态全局变量,只限于本编译单位使用。
函数也内部和外部。static定义的函数表示该函数为内部函数,只能在本文件中被其他函数调用,其他文件中的函数无法调用。extern定义的函数为外部函数,可以被其他文件的函数所调用。
指针
先明确一个概念:给变量赋值是指将赋的值写到该变量对应地址开始的存储单元中。使用变量是从该变量的开始地址单元读取它的内容。
指针的概念:指针就是具有某种数据类型的存储单元的起始地址。指针的类型是指该指针所指向的存储单元的数据类型,如int *p;p指针指向的存储单元的类型为int型。
指针变量:有一个变量,专门用来存放另一个变量的地址(指针),该变量就叫指针变量。如a存放了b的地址,a就是一个指针变量,可以说成a指向b。
指针变量的定义:
指针运算符:取地址符&,如&a为a的地址。取值符:*,如 *a为指针变量a所指向的变量b。
指针变量定义为:数据类型 *变量名,int *p;p为指针变量名,指针类型为int *,且不是指p为int型,而是p可以存放一个“int *”的指针值。指针变量只能被赋值地址,不可以赋值其他数据。无论什么类型的指针变量,所占的存储空间都是一样的,具体大小这个看编译器。
指针的运算有加和减,是指向下一个或上一个该类型的存储单元。指针也可自减自增。
指针的引用:已知p为指向已确定的指针变量,p取当前指向对象的地址,*p取指向对象的地址的内容,即取指向对象的值。
*p出现在变量声明语句中时,表示定义指针变量p;出现在表达式中时,表示取p所指对象的值。
数组与指针:指针变量可以指向变量,也可以指向数组元素。所谓数据元素的指针就是数组元素的地址。指针处理一维数组时,指针变量所指对象为数组元素。 数组名为数组连续空的的起始地址,也就是第一个数组元素a[0]的指针。C语言中数组名是一个不占内存的地址常量,因此不能给数组名赋值。
设一个数组名为a,则a == &a[0],a+1为a[1]的起始地址, * (a+i)表示取a[i]的值,这叫用过地址常量引用数组元素。
注意区分 * (p++)和( * p)++,第一个是取p的下一个存储单元中的值,第二个是p所指的对象的值加一。
指针处理二维数组时,指针变量所指的是数组中的行。设有一个二维数组m[3][4],二维数组的元素按行优先存放在连续的地址空间,数组名m为二位数组的起始地址,是构造元素m[0]的指针。每一个元素m[i][j]都有指针为&m[i][j],因此int * 型变量可以指向该数组中的任何一个元素。从二维数组角度看,m为首元素的地址,现在首元素不是一个整型变量,而是由4个元素组成的一维数组,因此m代表第0行的首地址,m+1代表第1行的首地址。m[0]、m[1]为一维数组名,而C语言中数组名又代表数组首元素的地址,因此m[0]表示一维数组m[0]中第0个元素的地址,即m[0] == &m[0][0],m[1] == &m[1][0]。m[0]+1与*(m+0)+1均为&m[0][1],则*(m[0]+1)与 * ( * (m+0)+1)即为m[0][1]的值,即 * (m[i]+j)或 * ( * (m+i)+j)为m[i][j]的值。
二维数组m中,二维数组名m、m[0]、 * m、 * (m+0)都是同一个地址。
C语言可以定义用来存储行指针的指针变量,称为行指针变量,也叫指向一维数组的指针变量。
定义: 类型 ( * 变量名)[指向的数组长度] 如 int ( * p)[5]; ,则p用来指向一个包含5个int型元素的一维数组。
注意区分int *p [5] 与 int (*p)[5],第一个意思是一个数组元素为指针的指针数组p。
指针与函数
指针可以作为函数的参数。
函数名是一个地址常量,若把函数地址赋给一个指针变量,则该指针就指向了函数,指向该函数的变量称为“函数指针”。
将指针作为函数参数,可以实现在被调用函数中改变主调函数定义变量的值,从而实现在函数调用时从被调函数中将多值返回给主调函数(利用return只能返回一个值)。
用指针做参数时,实参是指针表达式,形参是指针变量,它的作用是将实参表达式的值传给形参指针变量。因此,型参与实参指向的存储单元相同,就可以通过形参简介访问实参指向的存储单元的值。
当形参为指针时,实参必须时数据类型相同的地址或是指向某个存储单元的指针变量。
通过传递地址,在被调函数中直接改变调用函数变量的值。
由于普通的形参和实参分别占用不同的存储空间,因此形参不能改变对应实参的值,但利用地址传递,可以实现通过形参改变对应实参的值,利用此形式可以把两个或两个以上的数据从被调函数返回主调函数。
主函数定义 int a = 20,b = 30
以往值传递:
void swap2(int a,int b){
int temp;
temp = a;
a = b;
b = temp;
}
现在用地址传递
void swap(int *a,int *b){
int t;
t = *a;
*a = *b;
*b = t;
}
指针类型的函数:
函数返回一个指针,也就是返回值是地址数据。
指向函数所在内存区首地址的指针:函数指针变量。
定义方法:类型 (*指针变量名)(); 如 int (*p) ();定义了一个指向函数入口的指针变量p,这个函数类型为int型。定义了函数指针,可以将某个函数的指针(函数名即为函数的指针)赋值给p,然后就能通过该函数指针变量来调用该函数,调用形式为( * 指针变量名)(实参列表);
注意区分函数指针变量和指针类型的函数,这是两个完全不同的概念,一个是一种变量,一个是一种函数。
30.一维数组的指针作为函数参数,一维数组名作为实参时,实参传给形参是指针,因此形参变量应该是指针变量。
数组名可以作为实参的同时,数组元素的地址也可以作为实参。
31.二维数组的指针有两种,为元素指针和行指针,若用元素指针作为函数参数,则形参的使用与一维数组相同。
若把数组名或某一行作为参数时,形参必须是一个行指针变量
32.指针数组作为实参
33.指针与字符串:C语言没有字符串的预定义类型,依靠字符数组和字符指针来实现。因此指针可以指向一个字符串:将存放字符串的起始地址赋给指针变量,如char *p = “hello”; p指向了一个无名存储区的首地址。
用字符数组和字符指针实现字符串在存储上是有区别的,引用字符数组的变量总是引用固定的存储空间,而字符指针变量可以指向别的空间。
多字符串可以用二位数组来定义,也可以用字符型指针数组定义。
34.多级指针,常用的是二级指针,即指向指针的指针,比如二维数组名就是一个二级指针常量。定义: int **p;
指向指针的指针数组:int **p[];
指向行指针的指针变量: int *(*p)[指向的数组长度]
指向行指针的指针数组:int *(*p[数组长度])[指向的数组长度]
35.C语言编译系统提供的编译预处理功能主要有#include、#define and #if。
36.结构体
声明:struct 结构体名{ 成员1;…}; 如:struct stu{ int age;};
变量定义:三种方法:1)先声明再定义:struct stu boy1,boy2;
2)声明的同时定义变量:struct stu{int age;}boy1,boy2;
3)省略结构体名的定义变量:struct {int age;}boy1,boy2; 此种定义方法缺点是以后不能再定义此种结构体的其他变量以及指向该结构体的指针。
结构体变量的引用:结构体变量.成员名,如boy1.age
结构体变量的初始化:定义在函数外的结构体,该结构体的变量可以初始化或是定义在函数内为静态存储类型的结构体变量可以初始化。
结构体数组:struct stu student[5];定义了存储5个stu结构体变量的数组。
结构体指针:1)指向结构体变量的指针变量 2)指向结构体数组的指针变量。
1)
struct stu boy;//定义了一个结构体变量boy
struct stu *p1;//定义一个结构体指针
p1 = &boy;//指针指向结构体变量
指针变量的引用:boy.age;或boy->age;
2)
指向结构体数组的指针的定义于指向结构体变量的指针的定义方法相同,这个指针变量指向的到底是数组还是变量,由指向表达式确定。
结构体与函数:关系有两方面,一是结构体作为函数的参数,二是结构体类型的函数
1)结构体作为函数参数其中有三种情况:结构体变量做参数、结构体变量的成员做参数以及结构体指针作为函数参数。
2)结构体类型的函数:定义 struct stu student(){};
返回类型是stu型的结构体。
结构体嵌套:结构体作为结构体的成员。
37.动态内存管理函数:
malloc(size):在内存的动态存储去的自由内存部分分配一个长度为size字节长度的连续内存,若成功,则返回内存区域的首字节的指针(地址),失败则返回0。
free(ptr):释放ptr指向的内存。
38.类型定义:typedef
typedef int INT;//INT成为定义整型变量的关键词
也可以定义结构体,即给结构体改名。
39.共用体就是各个成员联合起来共同占用一块内存空间。