C语言基础

 

一  基础linux命令和vi命令

1.1   linux的简单操作

   1)打开终端:ctrl + tab + t

                          ctrl + shift + n :必须已有一个终端,且路径会和已有终端一致。

                          ctrl + shift + t  :以分屏的方式打开终端

   2)终端初始显示行   linuxubuntu:~$

                           linux :代表用户名

                           ubuntu:计算机名

                           ~ :代表家目录

                           $ :代表普通用户权限

                           #:代表管理员权限

1.2   终端命令

          1)exit : 退出

          2)su :进入对应用户:   su root  是进入管理员目录,sudo临时使用管理员权限

          3)文字调整:ctrl + shift + + :放大

                                 ctrl + - :缩小

          4)上下键:寻找历史命令

          5)关机:sudo shutdown -h now

          6)清屏: clear

                           ctrl + l

          7)ctrl + c:从当前命令中退出

1.3   常用命令

           1)cd:进入目录文件

                         cd  .. :退出当前目录

                         cd -  :进入上次进入的目录

                         cd  /  :进入根目录

                         cd  进入家目录。与cd ~功能相同

            2)ls:查看当前路径下的文件名

                          -a:所有文件,包括隐藏文件

                          -l  :列举文件,显示文件详细信息

                                   例如: drwxr-xr-x    7   linux  liunx   4096  5月 2009    class.c

                                 d为文件类型:linux中有bcd-lsp其中文件类型。

                                 rwxr-xr-x :表示权限:三三一组,分别对应用户主,用户组,其他用户

                                                        rwx分别对应可读,可写,可执行

                                 7:表示文件链接数

                                  linux:用户名

                                  linux :组名

                                  4096:文件大小,默认大小即为4096

                                  5月 2009:时间戳

                                  class.c:文件名

             3)pwd:显示当前路径

             4)mkdir:创建目录文件    touch :创建文件,更新时间戳

             5)rmdir:删除空目录        rm :删除文件    rm -rf:删除目录(内部有无文件均可)

             6)cp:复制文件

                             cp  文件名   目录名   -a(使用可以复制目录)

             7)mv   :移动,不分文件还是目录均可移动            

1.4   vi编辑器

      进入编辑器:vi   文件名(后缀为.c)

三种模式:

1)  命令模式:主要是为了对当前文件进行复制,剪切和粘贴等操作

      A:如何进入命令模式:esc键

B:从命令模式进入到插入模式

i:从光标所在位置前面开始插入

I:在光标所在行的最前面开始插入

a:从光标所在位置后面开始新增

A:在光标所在行的最后开始插入

o:在光标所在行的下一行开始插入

O:在光标所在行的上一行开始插入

编辑操作

yy: 复制一行

nyy: 复制n行

p:  粘贴(将其粘贴到光标所在的下一行)

dd: 剪切一行

ndd:剪切n行

u:撤销(ctrl    r   反撤销)

gg:将光标移动到第一行

G:将光标移动到最后一行

2)  插入模式:写代码

3)   底行模式:主要是为了保存文件,退出文件等操作

在命令模式下输入“:”进入底行模式

w:保存

q:退出

q!:强制退出

wq:保存并退出

vsp:左右分屏显示内容

sp:上下分屏显示内容

n:将光标移动到第n行

/aa:将含有字符aa的地方标亮,noh退出高亮

%s/a/b/g:将文件里面的所有a换成b

n,ms/a/b/g:将第n行到m行的a变成b

“^”(“0”):代表行首,移动到行首

“$": 移动到行尾

3,5 co  7 将第3~5行的内容copy到第7行的下一行

3,5 m .    将第3~5行的内容以移动到光标所在行的下一行

1.5   gcc编译器

【1】gcc编译器

gcc(GNU CCompiler)是GNU推出的功能强大,

性能优越的多平台编译器,gcc编译器能将C,

C++语言源程序编译连接成可执行文件,

gcc编译流程 (总共四步)                               ===>1.c

1--- 预处理:主要进行宏替换以及头文件的包含展开

gcc -E HelloWorld.c -o HelloWorld.i

2--- 编译:编译生成汇编文件,会检查语法是否有错误

gcc -S HelloWorld.i -o HelloWorld.s

3--- 汇编:将汇编文件编译生成目标文件(二进制文件)

gcc -c HelloWorld.s -o HelloWorld.o

4--- 链接:链接库函数,生成可执行文件

gcc HelloWorld.o -o HelloWorld

二  数据类型与运算符

2.1   计算机数据表示****

A: 数值型数据

凡是按进位的方式计数的数制叫做进位计数制,简称进位制

二进制数:

八进制数:通常最前面加0作为标识

十进制数:人类一般使用的是十进制数

十六进制数:通常使用0x标识此数为十六进制

B: 非数值型数据

由于最终还需要将非数值数据转化为0、1组成的机器码,

所以需要用数字标识非数值型数据,使用ASCII

在linux里面使用man ascii查看ascii表

'\n'  10

'0' - '9'   48 - 57

'A' - 'Z'   65 - 90

'a' - 'z'   97 - 122

【3】词法符号

关键字

关键字是由系统预定义的词法符号,有特定的含义,不允许用户重新定义。

下面是一些基本关键字:

auto             break              case            char             const

continue       default               do              double         else

enum           extern              float            for               goto

if                  int              long            register        return

short            signed             sizeof          static           struct

switch          typedef           union              unsighed         void

              volatile        while     

              注意:linux严格区分大小写,所有关键字均为小写 

标识符

标识符是由程序员按照命名规则自行定义的词法符号,用于定义宏名、变量名、

函数名和自定义类型名等。

注意:标识符不包括文件名,只是代码内部定义的名字需要遵循的

C语言的命名规则如下:

1) 标识符由一个或多个字母、数字或下划线组成

2)标识符的第一个字符必须是字母或下划线

3)标识符不能与任何关键字相同

标点符号

, : 一般用于定义两个相同类型的变量等等

; :一般用于标识一句话的结束等等

: :一般在switch...case...

{}:标识代码段

():一般在函数传参等等

分隔符

\n  换行

运算符:

表示运算的词法符号

                     主要包括:

算术运算符、 逻辑运算符、关系运算符、位运算符、

赋值运算符、 递增递减运算符、地址运算符、

逗号运算符、sizeof运算符…

【4】代码跟踪调试                   ===>2.c

#include <stdio.h>

int main()

{

//代码跟踪调试

//主要用于在判断得到具体位置

//%s:字符串类型 %d:整型

printf("%s ---> %s ---> %d\n", __FILE__, __func__, __LINE__);

return 0;

}

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

只有两个量true和false,表示逻辑真值和逻辑假值。

bool(需要添加头文件stdbool.h)和_Bool

c语言里面本身没有逻辑类型,一般用0和1代替,非0即为真(1)

(一)整数类型。

C支持四种整数类型:char, short, int和long,整数类型用于处理整数。

1、 char类型:在内存当中占一个字节,用%d打印             =====>6.C

有符号:signed char   -128 - 127

无符号:unsigned char    0 - 255

如果不定义是否有符号,默认是有符号的

正数的原码、反码、补码是一样的

负数的补码等于原码取反加1(反码加1),一般最高位为1标识负数

例如  5

原码:0000 0101

反码:0000 0101

补码:0000 0101

-5

原码:1000 0101

反码:1111 1010

补码:1111 1011

signed char a = 129;

存储时:

原码:1000 0001

反码:1000 0001

补码:1000 0001

取出时:

补码:1000 0001

反码:1000 0000

原码:1111 1111 = -127

unsigned char b = -1;

存储时:

原码:1000 0001

反码:1111 1110

补码:1111 1111

取出时:

补码:1111 1111

反码:1111 1111

原码:1111 1111 = 255

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)

(三)void类型。

该类型也叫缺省型,用于描述值为空集,主要用于说明不返回值的函数

或指向任一类型的指针等

2.3   常量

常量是指在程序运行期间其数值不发生变化的数据。

注意:常量绝对不能作为赋值语句的左值  例如 5++  或者  5 = 5 + 1

1) 整型常量(整数)  :整型常量通常简称为整数。

C语言的整数可以是十进制数、八进制数和十六进制数。

十进制数用%d打印

八进制数用%o打印,使用%#o可以打印出表示

十六进制数用%x打印,使用%#x可以打印出表示

2)  浮点常量   :浮点常量又称为实数,一般含有小数部分。

在C语言中,实数只有十进制的实数,它又分为单精度实数(float)和双精度实数(double),

它们的表示方法基本相同。实数有两种表示方法, 即一般形式指数形式

      指数常量(科学计数法)                                                                                         指数形式的实数一般是由尾数部分、字母e或E和指数部分组成。  当一个实数的符号为正号时,可以省略不写,使用%e打印

其表示的一般形式如下:[+ / -]m.n[e / E][+ / -]t

        例如:

3.14e12 = 3.14 * (10~12);

-5.6e-45 = -5.6 * (10~(-45));

3)字符常量                                                                                                                           所谓字符常量是指一个单一字符, 其表示形式是由两个单引号包括的一个字符。

              例如,

‘A’,  ‘a’,  ‘Q’,  ‘0’,  ‘9’,  ‘+’,  ‘:’,  ‘?’,  ‘$’ 都是字符常量。

在C语言中, 字符常量具有数值。字符常量的值就是该字符的ASCII码值。

使用%c打印字符,使用%d打印字符的ascii

4)  字符串常量                                                                                                                      所谓字符串常量是指用双引号括起来的一串字符来表示

的数据。用%s打印字符串

注意:

a:一般是一个变量

'a':一个字符

"a":是一个字符串,在a后面有一个字符'\0','\0'是字符串的结束标志

5)标识常量

标识常量又称之为宏定义

所谓标识常量是指用标识符代替常量使用的一种常量, 其名称通常是一个标识符。

标识常量也叫符号常量,为了不与一般变量的名称相混淆,标识常量的名称一般

使用大写英文字母的标识符。标识常量在使用之前必须预先定义

#define   标识常量命称    常量

              #define N 32

2.4  变量

C语言中全局变量和局部变量可以使用相同名字,但是在函数中,只会调用局部变量

C语言的变量在程序中用变量名表示。变量名由用户根据其用途任意命名,

变量名命名遵从标识符的起名规则(只能由字母\数字\下划线组成且头一个

字母只能是字母或下划线. 变量不能是C的特殊字符)。

在程序运行时,变量占据一定大小的存储空间,其大小由其数据类型来决定的,

作为某变量的内存空间的首地址,称为变量的地址。

变量在程序中使用时,必须预先说明它们的存储类型和数据类型。

变量说明的一般形式是:

              <存储类型>    <数据类型 >    <变量名> ;

<存储类型>可以是关键词auto、register、static和extern之一;

<数据类型>可以是前面讲的基本数据类型,也可以是后面要讲解的自定义的数据类型.

<变量名>是一个合法的标识符,其前面的32字符有效。

变量的初始化:

是指变量在被说明的同时赋给一个初值。

全局区会自动初始化为0

静态区也会初始化为0

栈区是随机值

 

2.5  存储类型

1、 auto:

用auto存储类型说明的变量都是局部于某个程序范围内的,只能在某个程序范围内

使用,通常在函数体内或函数中的复合语句里。

C语言中,在函数体的某程序段内说明auto存储类型的变量时可以省略关键字auto。

2、 register

称为寄存器型,使用register关键词说明的变量主要目的是想将所说明的变量放入CPU

的寄存器存储空间中,这样可以加快程序的运行速度。

如申请不到就使用一般内存,同auto ;

使用time命令可以检测可执行文件运行的时间

                        !!!注意:不可用于全局变量:因为CPU寄存器的地址不考虑,而全局变量会被多次调用,可能出现问题。

3、 extern

       称为外部参照引用型,使用extern说明的变量是想引用在其它文件中函数体外部说明的变量。 当变量在一个文件中的函数体外说明,所有其他文件中的函数或程序段都可引用这个变量。一般用于在函数之间传递数据。                 

                       !!!注意:1、 使用extern 必须保证其他文件中有该变量的声明,否则报错

                                            2、 extern    int    x;  正确

                                                  extern    int    x = 10;错误    其他文件中已经赋值,不能在本文件中赋值

4、 static

称为静态存储类型,在C语言中,既可以在函数体内,也可在函数体外说明static 存储类型的变量。

在函数体内说明的static 存储类型的变量也是一种局部变量,与auto最大不同点

是:static存储类型的变量在内存中是以固定地址存放的,而不是以堆栈方式存放

的;只要整个程序还在继续运行静态变量就不会随着说明它的程序段的结束而消失,

它下次再调用该函数,该存储类型的变量不再重新说明,而且还保留上次调用存入的

数值,且默认赋值为0,0.0,\0;

                        !!!注意:由于是静态存储,可以想象,只需要执行一次即可。多次经过该指令,会跳过。              

void fun2(int i)

{

//static修饰的变量在静态区,会保留之前的数据

static int num;

num = num + i;

printf("num = %d\n", num);

}

根据i的不同 static int num;虽然会经过三次,但是只执行一次。

2.6  内存的分类

1)  静态区(用static修饰的变量)会保留地址,不会释放空间

2)  栈区:局部变量放在栈区,栈区的变量会随着当前代码段的结束而摧毁(释放空间)

3)  堆区:手动申请的空间,一般使用malloc函数开辟空间,堆区的空间需要手动申请手动释放

4)  常量区:存放常量的

5)程序代码区

强制数据类型的转换

强制数据类型的转换是指采用某种方式将某种数据类型强制转换成指定的数据类型。

这种转换存在两种方式:一种为显式的数据类型转换,另一种为隐式的数据类型转换。

显式的数据类型转换实现的一般形式为:

(数据类型名称)< 表达式 >

需要注意的是:

强制类型转换符后面的表达式如存在复杂运算,就一定要用小括号括起来

强制类型转换符是一种不安全的转换,一般都是将高级类型转换成低级类型,

要丢失数据的精度;

强制类型转换并不改变表达式中变量的数据类型和其值。

2.7  运算符及其运算

1、 算术运算符:+,-,*,/,%,++,--

%:取余,浮点型数据不能取余

++:自增

--:自减

int a, b = 5;

a = b++;   //先使用,后自增

a = 5, b = 6;

int a, b = 3;

a = ++b;   //先自增,后使用

a = 4, b = 4

2、 关系运算符

> >= < <= == !=

关系运算符主要用于判断

3、 逻辑运算符

! 逻辑反, 表达式为真则为假, 表达式为假则为真

&& 逻辑与,只有二者都为真,才为真

|| 逻辑或,只要有一个为真就为真

int a = 5, b = 6;

c = a > 0 && b > 6;  c = 0;

c = a > 0 || b > 6;  c = 1;

短路原则:

逻辑与中,只要第一个表达式为假,第二个表达式不会执行

逻辑或中,只要第一个表达式为真,第二个表达式不会执行

4、 位运算符

~ 位逻辑反  0 - 1   1 - 0    

!!!注意:表达式取反默认为4个字节(vi编辑器)

& 位逻辑与  都为1才为1

| 位逻辑或  只要有一个为1就为1

^ 位逻辑异或 相同为0 不同为1

>> 右移

<< 左移

5、 赋值运算符与赋值复合运算符

赋值运算符为“=”,其运算的一般形式如下:

<左值表达式> = <右值表达式>

                     赋值复合运算符其运算的一般形式如下:

<变量>  <操作符>=  <表达式>

例子:a = a +b ===> a+=b

6、 逗号运算符

从左到右依次计算,最后表达式的值,作为整个表达式的值

整个表达式的值就是最后一个表达式的值

但是前边所有的表达式都会被计算

例如,

float  x=10.5,  y=1.8,  z=0;

z = (x+=5, y = x+0.2) ;    

z 赋值为15.7, x赋值为15.5, y赋值为15.7

7、 条件运算符"? :"

                     是一个三目运算符, 其运算的一般形式是:    

<表达式1>  ?  <表达式2>  :  <表达式3>

如果表达式1成立,则执行表达式2,否则执行表达式3

8、 sizeof运算符:

sizeof运算符运算的一般形式如下:

                                         sizeof(<类型或变量名>) 

是一个关键字,计算一个变量或者数据类型所占的内存空间的大小,以字节为单位。

                     注意:它只针对数据类型,而不针对变量!

!附图:各种运算符的优先级

三   输入输出函数

              注意:在man手册里面查看函数时,主要查看函数的头文件、结构以及功能、参数和返回值   

3.1、 字符输出函数:putchar()

#include <stdio.h>

int putchar(int c);

功能:输出一个字符

参数:字符,字符的ascii值,存放字符的变量

返回值:字符的ascii值

3.2、 字符输入函数 getchar

#include <stdio.h>

int getchar(void);

功能:从终端输入一个字符

参数:无

返回值:读到的字符的ascii值,

例如:int a;

          a = getchar();//将输入赋值给a

getchar();//去掉换行符

空格和换行符也会被getchar获得;经常用于去除由于使用scanf而多出的换行符

3.3 、格式输出函数 printf                                                                     

#include <stdio.h>

int printf(const char *format, ...);

功能:按照格式输出内容

%p :打印地址,(逻辑地址(虚拟内存))    进程中将详细介绍

修饰符:

m:输出数据域宽,数据长度<m,左补空格;否则按实际输出

.n:对实数,指定小数点后位数(四舍五入)。对字符串,指定实际输出位数

-:输出数据在域内左对齐(缺省右对齐)   与m同时使用,则m无作用

+:指定在有符号数的正数前显示正号(+)

0:输出数值时指定左面不使用的空位置自动填0

#:在八进制和十六进制数前显示前导0,0x

l:在d,o,x,u前,指定输出精度为long型;在e,f,g前,指定输出精度为double型

返回值:输出字符的个数

3.4、 格式输入函数 scanf

#include <stdio.h>

 int scanf(const char *format, ...);

功能:按照格式从终端输入

格式字符:等同于printf()

%d 十进制整数

%u 无符号十进制整数

%x 十六进制数

%o 八进制数

%c 单一字符

%s 字符串,如果想输入带空格的字符串,需要使用%[^\n],不需要使用取地址符,因为数组默认首地址

%e 科学计数法

%f  浮点类型

修饰符:

m:指定输入数据宽度,遇空格或不可转换字符则结束

*:抑制符,指定输入项读入后不赋给变量

...:可变参

返回值:正确输入数据的个数,如果输入错误返回0;

!!!注意:%d时不会识别空格和回车

                     %c识别空格和回车,会产生垃圾字符

                                          解决方法:1、 在%c之前加入空格,可以回收多个垃圾字符

                                                            2、 在%c之前加入%*c,不能回收多个字符

                                                            3、 使用getchar();只能一个一个输入,且不能回收多个垃圾字符

3.5、 字符串输出函数puts()

#include <stdio.h>

int puts(const char *s);

功能:输出一个字符串

参数:字符串的首地址

返回值:输出的字符串的长度

注意:字符数组必须以‘\0’结束,puts遇'\0'结束 ;输出完毕自动换行

3.6、 字符串输入函数gets()

    例如   char a[n];

               gets(a);

功能:从终端输入一个字符串

参数:输入的字符串的首地址

返回值:字符串的首地址

注意:如果使用数组存储字符串,需要将其数组的元素定义的足够大,否则会造成越界或者溢出; n值必须有,而且需要足够大。否则将会出错

 

 

 

四   控制语句

4.1、 switch...case...

switch (表达式)

{

case 常量表达式1:语句块1;

break;

case 常量表达式2: 语句块2;

break;

….

case 常量表达式n: 语句块n;

break;

default :语句块n+1

}

使用:

  1. switch中的表达式可以是整型或字符型表达式(也可以是枚举类型,新标准的c语言允许为任意类型.),不能使用浮点型。
  2. 每个常量表达式的值必须各不相同,否则将会出现矛盾。
  3. 当表达式的值与case后面的常量表达式值相等时就执行此case后面的语句。

4、 “case 常量:”只起语句标号的作用。

5、 break语句在switch中的作用:强行跳出switch体转到其它某条语句,每个case后面应有一个break语句(否则程序的执行顺序将改变),default分支后的break可以省略。

6、各个case出现的先后顺序不影响程序的执行结果。多个case可以执行一组语句.

!!!注意:1、 case 之后只能是常量表达式

                             2、如果不加break,则会将满足当前条件下的所有语句执行一遍;通过这个特性,可以进行累加;如下程序通过此种方法,可以节省大量代码量。

例如:#include <stdio.h>

#define MONTH_1 31

#define MONTH_2 28

#define MONTH_3 31

#define MONTH_4 30

#define MONTH_5 31

#define MONTH_6 30

#define MONTH_7 31

#define MONTH_8 31

#define MONTH_9 30

#define MONTH_10 31

#define MONTH_11 30

#define MONTH_12 31

int main(int argc, const char *argv[])

{

int year, month, day;

int leap = 0;

int days = 0;

printf("please input year-month-day >>> ");

scanf("%d-%d-%d", &year, &month, &day);

//先判断输入的日期是否符合实际情况

//大的方向考虑

if(year < 0 || month < 1 || month > 13 || day < 1 || day > 31 )

{

printf("date error\n");

return -1;

}

//年有闰年和平年

if(year % 4 == 0 && year % 100 != 0 || year % 400 == 0)

{

leap = 1;

}

//判断月

//1 3 5 7 8 10 12也没个月最多31天

if(month == 1 || month == 3 || month == 5 || month == 7 || month == 8 || month == 10 || month == 12)

{

if(day > 31)

{

printf("date error\n");

return -1;

}

}

//4 6 9 11

if(month == 4 || month == 6 || month == 9 || month == 11)

{

if(day > 30)

{

printf("date error\n");

return -1;

}

}

//2月闰年最多29天,平年最多28天

if(month == 2)

{

if(leap == 0)

{

if(day > 28)

{

printf("date error\n");

return -1;

}

}

else

{

if(day > 29)

{

printf("date error\n");

return -1;

}

}

}

switch(month)

{

case 12:

days += MONTH_11;

case 11:

days += MONTH_10;

case 10:

days += MONTH_9;

case 9:

days += MONTH_8;

case 8:

days += MONTH_7;

case 7:

days += MONTH_6;

case 6:

days += MONTH_5;

case 5:

days += MONTH_4;

case 4:

days += MONTH_3;

case 3:

days += MONTH_2 + leap;

case 2:

days += MONTH_1;

case 1:

days += day;

}

printf("days = %d\n", days);

return 0;

}

4.2、 循环语句

注意:循环都必须有结束条件,否则是死循环

1、 goto

loop:

                     if (i<=100)

            { 

                sum=sum+i;

                i++;

                goto loop;

            } 

注意:goto语句使用会导致代码可读性差,程序混乱,尽量少用。

2、 while循环

while(表达式)

{

语句块;

}

注意:while循环在执行的时候需要有一个结束条件,一般需要在语句块里面进行添加

3、 do...while...

do{

语句块;

}while(表达式);

while循环线判断,再执行

do while循环先执行,后判断

注意:一定要在while后面加';'

4、 for 循环

for语句为非条件循环语句,功能比条件循环要强,也更灵活,

凡能使用条件循环的场合,用for循环都能实现。

各种形式的循环都可以用来处理同一问题,一般情况下它们可以互相替代。

for(表达式1; 表达式2; 表达式3)

{

语句块;

}

执行过程:

首先运行表达式1,然后运行表达式2,如果表达式2为真,运行语句块

再次运行表达式3,判断表达式2是否成立,如果位真,再次运行语句块,以此类推

注意:语句省略问题

for语句中的表达式1可以省略,但在for循环之前应给循环变量赋值.

例如:i=1;for(;i<=100;i++) sum=sum+i;

表达式2也可以省略,但循环将陷入死循环.

例如:for(i=1;;i++) sum=sum+i;

表达式3也可以省略,但应在循环体中增加使循环变量值改变的语句.

例如:for(sum=0,i=0;i<=100;)

{ sum=sum+i; i++;}

4.3、 辅助控制语句

1、 break:

         用于从循环体跳出循环体,提前结束循环

         只能用于循环语句和switch

         一个break对应一个循环

2、 continue:

          跳出本次循环,直接进入下一次循环

3、 return:

          结束当前函数

          返回一个结果

           如果函数的返回值为void,return 可写可不写。使用return如下

                           void  fun()

                                     {

                                                 return;

                                        }

五   数组与字符串

5.1、数组

构造数据类型之一;

构造数据类型包括:结构体,联合体等(liunx c中详细介绍)

数组定义:数组是具有一定顺序关系的若干个变量的集合 ,组成数组的各个变量称为数组的元素;数据中各个元素的数据类型相同;数组中的元素都可以通过数组名和下标来表示。

内存中最小的存储单位是字节byte;数组中的元素在内存中是连续存储的

数组的说明的一般形式: 存储类型   数据类型   数组名【表达式】

字符串定义:结束有\0,是系统自动添加的,不需要考虑在申请时添加(gcc)

地址:数组的地址为第一个元素的地址。数组名表示数组在内存中的首地址,数组名是地址常量。所以下图中的a+=1语句是错误的。

!!!注意:变量不能作为数组的定义长度使用

引用:先定义后引用

          数组只能一个元素一个元素的引用,不能一次引用整个数组

          数组中的元素都有一个编号,从零开始往上递增。

数组初始化:

                    不初始化,数组的值为随机值

                     使用static,不初始化,会使用默认初始化的值0,0.0,\0;

                     给数组中部分元素赋初值,未赋值元素为默认值0,0.0,\0;

                     当全部元素赋初值时,可不指定数组长度。系统根据初始化的值的个数,自动开辟空间

一个数异或同一个数两次,结果 为该数本身(用于加密)

5.2、二维数组

  1)二维数组的定义方式:

       数据类型 数组名[常量表达式1][常量表达式2];

auto  int  a【x】【y】(x行y列)

    2)  二维数组的存储

       二维数组在内存中是连续存储的

       按照行序优先的原则来存储

a【0】a【1】a【2】都是地址常量,和a相同

3) 二维数组元素的初始化:

      包括全部初始化和部分初始化。

求行号:sizeof(a)/sizeof(a【0】)

求列号:sizeof(a【0】)

5.3、字符数组与字符串

1)字符数组的定义:

       字符数组是元素的数据类型为字符类型的数组

        比如:char c[10], char c[3][4]

字符串的结尾有‘\0’。但是不算在字符串长度;即“adad”

使用%s输出是,遇到‘\0’才结束

!!!因为ch为地址,输入不需要&符号。输入时遇到空格和回车表示输入结束。

正则表达式:%【^\n】表示输入时遇到回车表示输入结束。空格将作为有效字符

正则表达式通常被用来检索、替换那些符合某个模式(规则)的文本。

5.4、字符串函数

 1)   字符串连接函数strcat

              格式: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) &与*运算符

2)指针变量的定义形式与初始化

指针变量的数据类型与所指向的变量的数据类型相同

p作为一个变量,用于存储指针。而c语言中并没有这种数据类型。*代表的意思为取指针所指向的变量的内容;*p,即为p中地址所指向的位置中的数据。而之前定义的a为整形,所以*p的数据类型也应该为整形;

指针声明的时候不能直接赋值,例如: int *a = 10;错误,(0除外,表示为空指针)

使用 int *a = &x;因为都是从右向左结合;

初始化:存储类型   数据类型    *变量名  =  地址;

指针变量的占用内存空间为4个字节,与定义的类型无关,而与操作系统的位数有关

32位  4个字节

64位  8个字节

空指针的定义以及初始化

空指针不指向任何区域

int *p = NULL;

int *p = 0;

3) 指针运算:只能进行算术运算,复制运算,关系运算

上图第三行为px++ 与 ++px

6.2、数组与指针

1) 一维数组与指针

a为一个地址常量,不能自增自减。

a【i】= *(a+i)//固定格式

图中将数组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。全局变量一经定义后就会在程序的任何地方可见。使用全局变量传递数据的先后顺序的不同会影响计算结果,应用顺序不当,会导致错误,这种方式尽量少用。

数组传递

  1. 复制传递方式

              函数与函数之间的数组传递,复制传递方式只是提供一种形式,被调用函数的形参数组的数组名实际上是一个指针变量,因此,复制传递方式与数组的指针传递方式完全相同,只是形参的说明形式不同而已。调用函数将实参数组传递给被调用函数形参,形参接收是实参的地址。由于复制传递方式与下节介绍的指针传递方式一样,被调用函数里对形参数组的操作会影响调用函数里的实参数组。形参数组的说明形式好像是实参的备份,其实不然。

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、词法分析中的“贪心法”:

每一个符号应该包含尽可能多的字符。

也就是说,编译器将程序分解城符号的方法是,从左到右一个字符一个字符地读入,如果该字符可能组成一个符号,那么再读入下一个字符;

需要注意的是:符号的中间不能嵌有空白(空格符,制表符和换行符)。

  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符号

  • 语法陷阱
  1. 运算符的优先级
  2. 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. 栏杆错误:
  1. 首先考虑最简单情况下的特例,然后将得到的结果外推,
  2. 仔细计算边界,绝不掉以轻心

第四章 连接

  1、什么是连接器?

      C语言中的一个重要思想就是分别编译(separate compilation),即若干个源程序可以在不同的时候大度进行编译,然后在恰当的时候整合到一起。

       编译器需要处理同名函数和同名变量

  外部变量只能定义一次

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值