一 基础linux命令和vi命令
1.1 linux的简单操作
ctrl + shift + n :必须已有一个终端,且路径会和已有终端一致。
1.2 终端命令
2)su :进入对应用户: su root 是进入管理员目录,sudo临时使用管理员权限
1.3 常用命令
例如: drwxr-xr-x 7 linux liunx 4096 5月 2009 class.c
rwxr-xr-x :表示权限:三三一组,分别对应用户主,用户组,其他用户
4)mkdir:创建目录文件 touch :创建文件,更新时间戳
5)rmdir:删除空目录 rm :删除文件 rm -rf:删除目录(内部有无文件均可)
1.4 vi编辑器
1) 命令模式:主要是为了对当前文件进行复制,剪切和粘贴等操作
“^”(“0”):代表行首,移动到行首
“$": 移动到行尾
3,5 co 7 将第3~5行的内容copy到第7行的下一行
3,5 m . 将第3~5行的内容以移动到光标所在行的下一行
1.5 gcc编译器
gcc(GNU CCompiler)是GNU推出的功能强大,
gcc -E HelloWorld.c -o HelloWorld.i
gcc -S HelloWorld.i -o HelloWorld.s
gcc -c HelloWorld.s -o HelloWorld.o
gcc HelloWorld.o -o HelloWorld
二 数据类型与运算符
2.1 计算机数据表示****
关键字是由系统预定义的词法符号,有特定的含义,不允许用户重新定义。
continue default do double else
short signed sizeof static struct
switch typedef union unsighed void
标识符是由程序员按照命名规则自行定义的词法符号,用于定义宏名、变量名、
printf("%s ---> %s ---> %d\n", __FILE__, __func__, __LINE__);
2.2 基本数据类型
32位平台
char 1个字节8位
short 2个字节16位
int 4个字节32位
long 4个字节
long long 8个字节
指针 4个字节
64位平台
char 1个字节
short 2个字节
int 4个字节
long 8个字节(区别)
long long 8个字节
指针 8个字节(区别)
!!!特殊:逻辑类型(bool类型) ===>3.c 4.c 5.c
c语言里面本身没有逻辑类型,一般用0和1代替,非0即为真(1)
C支持四种整数类型:char, short, int和long,整数类型用于处理整数。
1、 char类型:在内存当中占一个字节,用%d打印 =====>6.C
负数的补码等于原码取反加1(反码加1),一般最高位为1标识负数
2、 short类型:在内存当中占2个字节,范围 0 - 65525 或者 -32768 - 32767
3、 int 类型:在内存占4个字节 0 - 2~32 或者 (-2~31) - (2~31 - 1)
4、 long类型:在32为操作系统里面,占4个字节, %ld打印
C支持两种浮点类型:float和double,浮点类型用于处理包含小数部分的数值。
float 占4个字节,单精度,%f打印数据,小数点后四舍五入保留6位
double 占8个字节,双精度,%lf打印数据,小数点后四舍五入保留6位
浮点型和零值比较需要注意:浮点型并没有完全等于零的数值,只有无限接近
所以判断为:if (fabs(x) < 0.00001f)
该类型也叫缺省型,用于描述值为空集,主要用于说明不返回值的函数
2.3 常量
注意:常量绝对不能作为赋值语句的左值 例如 5++ 或者 5 = 5 + 1
在C语言中,实数只有十进制的实数,它又分为单精度实数(float)和双精度实数(double),
它们的表示方法基本相同。实数有两种表示方法, 即一般形式和指数形式。
指数常量(科学计数法) 指数形式的实数一般是由尾数部分、字母e或E和指数部分组成。 当一个实数的符号为正号时,可以省略不写,使用%e打印
其表示的一般形式如下:[+ / -]m.n[e / E][+ / -]t
例如:
3)字符常量 所谓字符常量是指一个单一字符, 其表示形式是由两个单引号包括的一个字符。
‘A’, ‘a’, ‘Q’, ‘0’, ‘9’, ‘+’, ‘:’, ‘?’, ‘$’ 都是字符常量。
在C语言中, 字符常量具有数值。字符常量的值就是该字符的ASCII码值。
4) 字符串常量 所谓字符串常量是指用双引号括起来的一串字符来表示
"a":是一个字符串,在a后面有一个字符'\0','\0'是字符串的结束标志
所谓标识常量是指用标识符代替常量使用的一种常量, 其名称通常是一个标识符。
标识常量也叫符号常量,为了不与一般变量的名称相混淆,标识常量的名称一般
2.4 变量
C语言中全局变量和局部变量可以使用相同名字,但是在函数中,只会调用局部变量
C语言的变量在程序中用变量名表示。变量名由用户根据其用途任意命名,
变量名命名遵从标识符的起名规则(只能由字母\数字\下划线组成且头一个
在程序运行时,变量占据一定大小的存储空间,其大小由其数据类型来决定的,
<存储类型>可以是关键词auto、register、static和extern之一;
<数据类型>可以是前面讲的基本数据类型,也可以是后面要讲解的自定义的数据类型.
是指变量在被说明的同时赋给一个初值。
静态区也会初始化为0
2.5 存储类型
用auto存储类型说明的变量都是局部于某个程序范围内的,只能在某个程序范围内
C语言中,在函数体的某程序段内说明auto存储类型的变量时可以省略关键字auto。
称为寄存器型,使用register关键词说明的变量主要目的是想将所说明的变量放入CPU
!!!注意:不可用于全局变量:因为CPU寄存器的地址不考虑,而全局变量会被多次调用,可能出现问题。
称为外部参照引用型,使用extern说明的变量是想引用在其它文件中函数体外部说明的变量。 当变量在一个文件中的函数体外说明,所有其他文件中的函数或程序段都可引用这个变量。一般用于在函数之间传递数据。
!!!注意:1、 使用extern 必须保证其他文件中有该变量的声明,否则报错
extern int x = 10;错误 其他文件中已经赋值,不能在本文件中赋值
称为静态存储类型,在C语言中,既可以在函数体内,也可在函数体外说明static 存储类型的变量。
在函数体内说明的static 存储类型的变量也是一种局部变量,与auto最大不同点
是:static存储类型的变量在内存中是以固定地址存放的,而不是以堆栈方式存放
的;只要整个程序还在继续运行静态变量就不会随着说明它的程序段的结束而消失,
它下次再调用该函数,该存储类型的变量不再重新说明,而且还保留上次调用存入的
!!!注意:由于是静态存储,可以想象,只需要执行一次即可。多次经过该指令,会跳过。
根据i的不同 static int num;虽然会经过三次,但是只执行一次。
2.6 内存的分类
1) 静态区(用static修饰的变量)会保留地址,不会释放空间
2) 栈区:局部变量放在栈区,栈区的变量会随着当前代码段的结束而摧毁(释放空间)
3) 堆区:手动申请的空间,一般使用malloc函数开辟空间,堆区的空间需要手动申请手动释放
5)程序代码区
强制数据类型的转换是指采用某种方式将某种数据类型强制转换成指定的数据类型。
这种转换存在两种方式:一种为显式的数据类型转换,另一种为隐式的数据类型转换。
强制类型转换符后面的表达式如存在复杂运算,就一定要用小括号括起来
强制类型转换符是一种不安全的转换,一般都是将高级类型转换成低级类型,
2.7 运算符及其运算
!!!注意:表达式取反默认为4个字节(vi编辑器)
是一个关键字,计算一个变量或者数据类型所占的内存空间的大小,以字节为单位。
!附图:各种运算符的优先级
三 输入输出函数
注意:在man手册里面查看函数时,主要查看函数的头文件、结构以及功能、参数和返回值
3.1、 字符输出函数:putchar()
int putchar(int c);
3.2、 字符输入函数 getchar
空格和换行符也会被getchar获得;经常用于去除由于使用scanf而多出的换行符
3.3 、格式输出函数 printf
int printf(const char *format, ...);
%p :打印地址,(逻辑地址(虚拟内存)) 进程中将详细介绍
.n:对实数,指定小数点后位数(四舍五入)。对字符串,指定实际输出位数
-:输出数据在域内左对齐(缺省右对齐) 与m同时使用,则m无作用
+:指定在有符号数的正数前显示正号(+)
l:在d,o,x,u前,指定输出精度为long型;在e,f,g前,指定输出精度为double型
返回值:输出字符的个数
3.4、 格式输入函数 scanf
int scanf(const char *format, ...);
%s 字符串,如果想输入带空格的字符串,需要使用%[^\n],不需要使用取地址符,因为数组默认首地址
3、 使用getchar();只能一个一个输入,且不能回收多个垃圾字符
3.5、 字符串输出函数puts()
注意:字符数组必须以‘\0’结束,puts遇'\0'结束 ;输出完毕自动换行
3.6、 字符串输入函数gets()
注意:如果使用数组存储字符串,需要将其数组的元素定义的足够大,否则会造成越界或者溢出; n值必须有,而且需要足够大。否则将会出错
四 控制语句
4.1、 switch...case...
….
- switch中的表达式可以是整型或字符型表达式(也可以是枚举类型,新标准的c语言允许为任意类型.),不能使用浮点型。
- 每个常量表达式的值必须各不相同,否则将会出现矛盾。
- 当表达式的值与case后面的常量表达式值相等时就执行此case后面的语句。
5、 break语句在switch中的作用:强行跳出switch体转到其它某条语句,每个case后面应有一个break语句(否则程序的执行顺序将改变),default分支后的break可以省略。
6、各个case出现的先后顺序不影响程序的执行结果。多个case可以执行一组语句.
2、如果不加break,则会将满足当前条件下的所有语句执行一遍;通过这个特性,可以进行累加;如下程序通过此种方法,可以节省大量代码量。
#define MONTH_1 31
int main(int argc, const char *argv[])
printf("please input year-month-day >>> ");
scanf("%d-%d-%d", &year, &month, &day);
if(year < 0 || month < 1 || month > 13 || day < 1 || day > 31 )
if(year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
if(month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12)
if(month == 4 || month == 6 || month == 9 || month == 11)
4.2、 循环语句
注意:goto语句使用会导致代码可读性差,程序混乱,尽量少用。
注意:while循环在执行的时候需要有一个结束条件,一般需要在语句块里面进行添加
各种形式的循环都可以用来处理同一问题,一般情况下它们可以互相替代。
首先运行表达式1,然后运行表达式2,如果表达式2为真,运行语句块
再次运行表达式3,判断表达式2是否成立,如果位真,再次运行语句块,以此类推
for语句中的表达式1可以省略,但在for循环之前应给循环变量赋值.
例如:i=1;for(;i<=100;i++) sum=sum+i;
表达式3也可以省略,但应在循环体中增加使循环变量值改变的语句.
4.3、 辅助控制语句
如果函数的返回值为void,return 可写可不写。使用return如下
五 数组与字符串
5.1、数组
构造数据类型包括:结构体,联合体等(liunx c中详细介绍)
数组定义:数组是具有一定顺序关系的若干个变量的集合 ,组成数组的各个变量称为数组的元素;数据中各个元素的数据类型相同;数组中的元素都可以通过数组名和下标来表示。
内存中最小的存储单位是字节byte;数组中的元素在内存中是连续存储的
数组的说明的一般形式: 存储类型 数据类型 数组名【表达式】
字符串定义:结束有\0,是系统自动添加的,不需要考虑在申请时添加(gcc)
地址:数组的地址为第一个元素的地址。数组名表示数组在内存中的首地址,数组名是地址常量。所以下图中的a+=1语句是错误的。
使用static,不初始化,会使用默认初始化的值0,0.0,\0;
给数组中部分元素赋初值,未赋值元素为默认值0,0.0,\0;
当全部元素赋初值时,可不指定数组长度。系统根据初始化的值的个数,自动开辟空间
5.2、二维数组
数据类型 数组名[常量表达式1][常量表达式2];
2) 二维数组的存储
二维数组在内存中是连续存储的
按照行序优先的原则来存储
a【0】a【1】a【2】都是地址常量,和a相同
包括全部初始化和部分初始化。
5.3、字符数组与字符串
字符数组是元素的数据类型为字符类型的数组
比如:char c[10], char c[3][4]
字符串的结尾有‘\0’。但是不算在字符串长度;即“adad”
!!!因为ch为地址,输入不需要&符号。输入时遇到空格和回车表示输入结束。
正则表达式:%【^\n】表示输入时遇到回车表示输入结束。空格将作为有效字符
正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。
5.4、字符串函数
格式:strcat(字符数组1,字符数组2)
功能:把字符数组2连到字符数组1后面
返值:返回字符数组1的首地址
说明:(1)字符数组1必须足够大
(2)连接前,两串均以‘\0’结束;连接后,串1的‘\0’取消, 新串最后加‘\0’
2) 字符串拷贝函数strcpy
格式:strcpy(字符数组1,字符串2)
功能:将字符串2,拷贝到字符数组1中去
返值:返回字符数组1的首地址
说明:(1)字符数组1必须足够大
(2)拷贝时‘\0’一同拷贝
(3)不能使用赋值语句为一个字符数组赋值
(4)讲短的字符串拷贝到长字符串,长字符串剩余部分依旧存在
3)字符串比较函数strcmp
格式:strcmp(字符串1,字符串2)
功能:比较两个字符串
比较规则:对两串从左向右逐个字符比较(ASCII码),直到遇到不同字符或‘\0’为止 ;
返值:返回int型整数,a. 若字符串1< 字符串2, 返回负整数
b. 若字符串1> 字符串2, 返回正整数 c. 若字符串1== 字符串2, 返回零
说明:字符串比较不能用“==”,必须用strcmp
4)字符串长度函数strlen
格式:strlen(字符数组)
功能:计算字符串长度
返值:返回字符串实际长度,不包括‘\0’在内
六 指针
6.1、 指针
1) &与*运算符
指针变量的数据类型与所指向的变量的数据类型相同
p作为一个变量,用于存储指针。而c语言中并没有这种数据类型。*代表的意思为取指针所指向的变量的内容;*p,即为p中地址所指向的位置中的数据。而之前定义的a为整形,所以*p的数据类型也应该为整形;
指针声明的时候不能直接赋值,例如: int *a = 10;错误,(0除外,表示为空指针)
初始化:存储类型 数据类型 *变量名 = 地址;
指针变量的占用内存空间为4个字节,与定义的类型无关,而与操作系统的位数有关
6.2、数组与指针
1) 一维数组与指针
图中将数组a打印出来,因为 p = a,所以 a【i】= *(a+i) = *(p+i)=p【i】
2)二维数组与指针
a[i][j] = *(a[i] + j) = *(*(a+i)+j)
二维数组的遍历:因为二维数组在内存中是依次存放的,使用元素指针就可对其进行遍历。具体如下图:
不同在于*q = “hello”不能更改,*q = ch,可以更改参数。“hellofarsight”是一个常量,是一个整体,不能修改。这种方式只能用于字符串。
3)数组指针与指针数组
A: 数组指针(行指针)就是一个指针指向一个一维数组,也可以形象的称呼为行指针,一般用来存储二维数组的数组名
数组指针的格式:
<存储类型> <数据类型> (*<指针变量名>)[数组的长度];
比如:int (*p)[4];定义了一个数组指针,指向一个可以存储四个整型数据的一维数组
p+1就移动一个一维数组的大小,在我们就是移动4个整型数据,16个字节
可以看做四个元素的步进,用于一维数组。但是会编译时会有警告,且在输出是需要使用两个‘*’;
问题: a = &a[0],该公式虽然左右两端都是地址。但是所代表的含义是不同的,类似于这就好像是问,你的高度是多少?
你的头在什么高度
这两个答案是一样的不同在于,一个是以整个人为单位,另一个,只是以你的头为单位
对于a[i][j],有下图特性
B: 指针数组是指由若干个具有相同存储类型和数据类型的指针变量构成的集合
指针变量数组的一般说明形式:
<存储类型> <数据类型> *<指针变量数组名>[<大小>];
比如:int *p[4]; 定义一个指针数组,数组的长度为4,这个指针数组中的每个元素都是一个整型指针。
6.3、多级指针
我们把一个指向指针变量的指针变量,称为多级指针变量。对于指向处理数据的指针变量称为一级指针变量,简称一级指针。而把指向一级指针变量的指针变量称为二级指针变量,简称二级指针。
二级指针变量的说明形式如下:
<存储类型> <数据类型> ** <指针名> ;比如:int **q;
注意:指针数组的数组名为二级指针
二级指针和数组指针的区别:二级指针是指向指针的指针;数组指针是指向数组的指针
6.4、void指针
void型的指针变量是一种不确定数据类型的指针变量,它可以通过强制类型转换让该变量指向任何数据类型的变量或数组
一般形式为:
void *<指针变量名称> ;
对于void型的指针变量,实际使用时,一般需通过强制类型转换才能使void型指针变量得到具体变量或数组地址。在没有强制类型转换之前,void型指针变量不能进行任何指针的算术运算
分析::对于一个地址来说,总是需要32位,不会因为是int还是char而有所不同。证明地址是不需要区分数据类型的。但是在调用或者输出时,必须知道数据类型,否则会出错。因此需要在调用或者输出时进行强制类型转换。
6.5、const指针
常量化的指针变量
const类型修饰符可以将指针变量常量化,主要有下面三种形式。
1---常量化指针目标表达式
一般说明形式如下:const <数据类型>*<指针变量名称>[= <指针运算表达式>] ;
常量化指针目标是限制通过指针改变其目标的数值,不能通过*p进行改变数值。
2---常量化指针变量
一般说明形式如下: <数据类型> *const <指针变量名称>= <指针运算表达式>;
常量化指针变量,使得<指针变量>的地址值不能修改。但可以通过 *<指针变量名称> 可以修改指针所指向变量的数值。
3---常量化指针变量及其目标表达式
一般说明形式如下:const <数据类型> * const <指针变量名> = <指针运算表达式> ;
常量化指针变量及其目标表达式,使得既不可以修改<指针变量>的地址,也不可以通过*<指针变量名称>修改指针所指向变量的值。
七 函数
7.1、函数的定义
函数是一个完成特定功能的代码模块,其程序代码独立,通常要求有返回值,也可以是空值
7.2、函数的一般形式
<数据类型> <函数名称>( <形式参数说明> )
{
语句序列;
return[(<表达式>)];
}
其中:
<函数名称>是一个标识符,要求符合标识符的命名规则;
<数据类型>是整个函数的返回值类型,如无返回值应该写为void型;
<形式参数说明>是逗号”,”分隔的多个变量的说明形式,通常简称为形参; 大括弧对 {<语句序列> },称为函数体
<语句序列>是大于等于零个语句构成
注意:在函数体中,表达式语句里使用的变量必须事先已有说明,否则不能使用。
return[(<表达式>)]语句中表达式的值,要和函数的<数据类型>保持一致;如函数的<数据类型>为void可以省略或者无表达式结果返回(即写成return ;)。
在写函数的时候需要明确两点:
1.这个函数是否需要未知参数参与运算,从而确定我这个函数的参数是什么样子
2.这个函数的最后的结果是什么,从而确定函数返回值的类型
7.3、函数的声明
函数的说明就是指函数原型
其中,<形式参数说明>可以缺省说明的变量名称,但类型不能缺省。
例如,
double Power(double x, int n) ;
double Power(double, int);
7.4、函数的调用
函数的使用也叫函数的调用,形式如下:
函数名称(〈实际参数〉)
其中:
<函数名称>是一个标识符,符合标识符的命名规则;
实际参数〉需要确切的数据,也可以是具有确定值的表达式。实参就是在使用函数时,调用函数传递给被调用函数的数据,用以完成所要求的任务
注意:
函数调用可以作为一个运算量出现在表达式中,也可以单独形成一个语句。对于无返回值的函数来讲,只能形成一个函数调用语句。
7.5、函数之间的参数传递
基本数据类型
1、复制传递方式
复制传递方式是函数间传递数据常用的式。调用函数将实参传递给被调用函数,被调用函数将创建同类型的形参并用实参初始化。即把实参赋给一个新的变量,把实参复制到新建形参的存储区域中。采用复制传递方式传递数据,被调用函数不能访问调用函数里的实参。被调用函数处理的数据是复制到其形参的数据,因此,即使改变形参的值也不会影响实参的值,一旦被调用函数完成了其任务时,这些形参通常就释放其占用空间 。
2、地址传递方式
地址传递方式和复制传递方式正好相反,这种方式是将调用函数的参数本身传给被调用函数。因此,被调用函数中对形参的操作,将直接改变实参的值。调用函数将实参的地址传送给被调用函数,被调用函数对该地址的目标操作,相当于对实参本身的操作。按地址传递,实参为变量的地址,而形参为同类型的指针。
3、全局变量
全局变量就是在函数体外说明的变量,它们在程序中的每个函数里都是可见的。实际上,全局变量也是一种静态型的变量。将它初始化为0。全局变量一经定义后就会在程序的任何地方可见。使用全局变量传递数据的先后顺序的不同会影响计算结果,应用顺序不当,会导致错误,这种方式尽量少用。
数组传递
- 复制传递方式
函数与函数之间的数组传递,复制传递方式只是提供一种形式,被调用函数的形参数组的数组名实际上是一个指针变量,因此,复制传递方式与数组的指针传递方式完全相同,只是形参的说明形式不同而已。调用函数将实参数组传递给被调用函数形参,形参接收是实参的地址。由于复制传递方式与下节介绍的指针传递方式一样,被调用函数里对形参数组的操作会影响调用函数里的实参数组。形参数组的说明形式好像是实参的备份,其实不然。
2、地址传递方式
函数与函数之间数组的地址传递方式是将调用函数中的实参数组的起始地址传递给被调用函数的指针变量形参。因此,被调用函数中对形参地址的目标操作,相当于对实参本身的操作,将直接改变实参数组的值。地址传递方式,实参应该为数组的指针,而形参应为同类型的指针变量。另外,数组的地址传递方式不仅要把实参数组的指针作为参数传递给函数,同时还需要传递数组的长度。
总结:函数之间的参数传递总结起来主要分为,有关和无关;
无关仅仅为基本数据类型的复制传递。即调用函数和被调用函数的参数完全互不影响。而其他形式的参数传递都会相互影响。尤其是全局变量,最好不要使用
7.6、指针函数和函数指针
指针函数是指一个函数的返回值为地址量的函数。通常一个函数都有返回值的数据类型。如果一个函数没有返回值,则该函数是一个无值型函数。指针函数只不过是一个函数的返回值是某一数据类型变量的地址。
指针函数的定义的一般形式如下:
<数据类型> *<函数名称>(<参数说明>)
{
语句序列;
}
在<函数名称>之前的*符号,说明该函数返回一个地址量。指针函数归根结底还是一个函数,只不过是一个返回地址的函数。
函数指针是用来存放函数的地址,这个地址是一个函数的入口地址,而且是函数调用时使用的起始地址。当一个函数指针指向了一个函数,就可以通过这个指针来调用该函数,函数指针可以将函数作为参数传递给其他函数调用。
函数指针变量说明的一般形式如下:
<数据类型> (*<函数指针名称>)(<参数说明列表>);
其中
<数据类型>是函数指针所指向的函数的返回值类型;
<函数指针名称>符合标识符命名规则
<参数说明列表>应该与函数指针所指向的函数的形参说明保持一致;
(*<函数指针名称>)中,*说明为指针()不可缺省,表明为函数的指针。
函数指针数组是一个保存若干个函数名的数组。
函数指针数组
<数据类型> ( * <函数指针数组名称> [<大小>] ) ( <参数说明列表> );
其中,<大小>是指函数指针数组元数的个数,其它同函数指针。
*数组指针,指针数组,函数指针,指针函数总结
数组指针:指向一个一维数组的一个指针 int(*p)[4]; 4==>指向的一维数组的长度
指针数组:一个包含若干个指针的数组 int *p[4]; 4==>数组中元素的个数
函数指针:指向函数首地址的指针 int (*p)(int a, int b) (int a, int b)==>指针指向的函数的形参
p = fun; 将fun函数的首地址赋值给p
p = &fun; 将fun函数的地址赋值给p
以上两种赋值方法都是正确的
指针函数:返回值为指针的函数
函数指针数组:一个包含若干个函数指针的数组 int (*p[4])(int a, int b) 4==>数组的元素个数
7.7、递归函数
所谓递归函数是指一个函数的函数体中直接调用或间接调用了该函数自身的函数。这里的直接调用是指一个函数的函数体中含有调用自身的语句,间接调用是指一个函数在函数体里有调用了其它函数,而其它函数又反过来调用了该函数的情况。
递归函数调用的执行过程分为两个阶段:
递推阶段:从原问题出发,按递归公式递推从未知到已知,最终达到递归终止条件。
回归阶段:按递归终止条件求出结果,逆向逐步代入递归公式,回归到原问题求解。
总结:递归函数适用在多次重复一个操作的函数中。使用过程中一定要有一个终止条件。
7.8、main函数
argc:是参数的个数
argv:是一个指针数组,这个指针数组中每个指针都指向传递过来的参数
如何给main函数传参?
运行这个程序的时候,在./a.out后面可以携带数据,多个数据之间用空格隔开,每个数据都看做一个字符串
C陷阱与指针
- 词法陷阱
1、词法分析中的“贪心法”:
每一个符号应该包含尽可能多的字符。
也就是说,编译器将程序分解城符号的方法是,从左到右一个字符一个字符地读入,如果该字符可能组成一个符号,那么再读入下一个字符;
需要注意的是:符号的中间不能嵌有空白(空格符,制表符和换行符)。
- 避免出现二义性:
例如:a=-1
可以理解为:
a = -1; a =- 1;
3、字符串打印:
char hello[] = “adadf”;
printf(hello);
不加%s竟然能打印,在vim,使用gcc编译仅仅是警告test.c:5:2: warning: format not a string literal and no format arguments [-Wformat-security]
实测: printf(变量名)的方式
float double不行报错
int char 有警告,但是运行的时候会出现段错误
char 【】 完美支持
int 【】 只打印第一个元素对应的ASCII符号
- 语法陷阱
- 运算符的优先级
- switch语句的运用,有break和无break时候的区别,详细解释请看 4.1
第三章 语义陷阱
1、使用strcpy strcat对指针进行操作
例: char *r;
strcpy(r,s);
strcat(r,t);
错误,因为r不能确定执行那个位置,所以应该先对其赋值
char r【100】;
strcpy(r,s);
strcat(r,t);
正确 但是存在问题;因为定义数组,需要开辟空间,所以r可以用于strcpy等操作,但是因为数组的大小被固定(大小不固定无法申请),所以如果s 或者 r 的字符串过长的话会丢失数据。
char * r;
r = malloc(strlen(s) + strlen(t));
strcpy(r,s);
strcat(r,t);
正确,这样就完美解决问题了
2、避免“举隅法”:
举隅法的定义:以含义更宽泛的词语便是含义相对较窄的词语,或者想法。
- 栏杆错误:
- 首先考虑最简单情况下的特例,然后将得到的结果外推,
- 仔细计算边界,绝不掉以轻心
第四章 连接
1、什么是连接器?
C语言中的一个重要思想就是分别编译(separate compilation),即若干个源程序可以在不同的时候大度进行编译,然后在恰当的时候整合到一起。
编译器需要处理同名函数和同名变量
外部变量只能定义一次