C语言学习

 

目录

第一天

数据类型

整型类型

浮点类型 

void类型

基本类型书写 

printf函数规定符列举

转义字符列举

 字节对齐:

条件注释注释:#if0     #endif

第二天

printf()函数:

scanf()函数:scanf(参数,&地址)

getchar putchar

 运算符

声明变量和定义变量的区别是什么? 

变量的三大属性:作用域、链接属性、存储属性。

作用域:

 链接属性

存储属性

堆栈的不同:

其他区域(或者其他叫法)

二维数组

ASCII码对照表

数组指针 

指针数组 

字符数组 

第三天

函数指针 

指针函数 

 typedef关键字

结构体 

大小端判断 

文件句柄 :

C的关键字 

C语言的预处理 

位运算 

积累例题 

switch case的执行总结:

c---清空stdin

scanf("%d",&n); 但输入字符


 


第一天

数据类型

  • 下载安装C/C++语言编译工具--QT(下载链接)。
  • C语言的数据类型:
  • 转载:https://www.runoob.com/cprogramming/c-data-types.html

C语言学习_第1张图片

整型类型

C语言学习_第2张图片

C语言学习_第3张图片

浮点类型 

C语言学习_第4张图片

void类型

C语言学习_第5张图片

基本类型书写 

C语言学习_第6张图片

printf函数规定符列举

C语言学习_第7张图片

转义字符列举

\ddd转义字符表示8进制数(\d,\dd都行最多3个),算一个字符,如'A'='\101' ,  \xdd转义字符表示16进制数算一个字符 , 'A'='x41'

C语言学习_第8张图片

 字节对齐:

可以一用#pramga pack(n); 设置字节对齐数。 

指定数据类型的变量存放的起始地址数值必须是其对应数据类型所占字节的整数倍

比如:C语言学习_第9张图片

上图中:结构体C的成员占据10个字节,而结构体C的有效对齐值是其成员b的自身对齐值4,10不是4的倍数,故还需补齐两个字节,此时结构体C占据12个字节,是4的倍数。

 

printf("%-10d\n",60);     //左对齐60后面补齐10个空格
printf("%10d\n",60);      //右对齐60前面补齐10个空格
printf("%10.3f\n",1.12356);     //%10.3f作用为:四舍五入保留3位有效数字 所以输出为1.124
printf("%f\n",12345678e-5); //小数的指数法输出123.456790  因为float为单精度浮点数,只能保留6位有效数字

条件注释注释:#if0     #endif

可以成对嵌套使用:

 

 #if 0
    sss    
#endif

第二天

printf()函数:

  • 调试C程序:gdb,  printf(使用printf调试时必须加换行符,因为要把输出缓冲区的内容输出出来)。
  • printf函数返回值的含义:返回最终打印字符的个数,不包括格式符因为最终结果,格式符被具体数值替换了,而包括转义字符(eg:\n, \t,等等,除了'\0'空字符)。

    int m;

    m=printf("Hello World!\n");

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

   输出为:m=13. 

printf()函数里'\n'的含义:

(1)换行

(2)将输出缓冲区中的所有字符输出

scanf()函数:scanf(参数,&地址)

(1)第一个参数字符里不能有换行符 \n

(2)从第二参数起,必须是变量地址

(3)在两个数据之间可以用一个或多个空格tab 键回车键分隔

(4)在用 %c 输入时,空格和"转义字符"均作为有效字符。

scanf 类型说明符:

类型 合格的输入 参数的类型
%a、%A 读入一个浮点值(仅 C99 有效)。 float *
%c 单个字符:读取下一个字符。如果指定了一个不为 1 的宽度 width,函数会读取 width 个字符,并通过参数传递,把它们存储在数组中连续位置。在末尾不会追加空字符。 char *
%d 十进制整数:数字前面的 + 或 - 号是可选的。 int *
%e、%E、%f、%F、%g、%G 浮点数:包含了一个小数点、一个可选的前置符号 + 或 -、一个可选的后置字符 e 或 E,以及一个十进制数字。两个有效的实例 -732.103 和 7.12e4 float *
%i 读入十进制,八进制,十六进制整数 。 int *
%o 八进制整数。 int *
%s 字符串。这将读取连续字符,直到遇到一个空格字符(空格字符可以是空白、换行和制表符)。 char *
%u 无符号的十进制整数。 unsigned int *
%x、%X 十六进制整数。 int *
%p 读入一个指针 。  
%[] 扫描字符集合 。  
%% 读 % 符号。  

实例:

    scanf("%d%d",&m,&n);

    printf("%d,%d",m,n);

  输入为:12  15(中间有空格) 输出为:12,15

int m,n;

scanf("%d,%d",&m,&n);

printf("%d,%d\n",m,n);

输入为:12 ,15(中间必须有‘,’且',' 前一定要紧跟在数字后面,数字与 ', '之间不能有空格) 输出为:12,15 

 scanf("%c%c%c",&a,&b,&c);

printf("%c,%c,%c\n", a,b,c); return 0; }

产生以下结果:

$ ./a.out 
请输入三个字符:run
r,u,n
$ ./a.out 
请输入三个字符:r u n
r, ,u

 printf("请输入用户名:");

scanf("%s", str1);

printf("请输入您的网站:");

scanf("%s", str2);

printf("输入的用户名:%s\n", str1);

printf("输入的网站:%s", str2);

运行结果为:

请输入用户名:admin
请输入您的网站:www.runoob.com
输入的用户名:admin
输入的网站:www.runoob.com

getchar putchar

  • getchar是以行为单位进行存取的。
  • getchar函数每次从缓冲区中得到一个字符,putchar函数每次输出一个字符。
  • 结束一个文件的输入就需用到EOF (Enf Of File)
  • 如用户在按回车之前输入了不止一个字符,其他字符会保留在键盘缓存区中,等待后续getchar调用读取.也就是说,后续的getchar调用不会等待用户按键,而直接读取缓冲区中的字符,直到缓冲区中的字符读完为后,才等待用户按键
  • 为什么要用int型来接受getchar函数:因为getchar函数除了返回终端输入的字符外,在遇到Ctrl+D(Linux下)即文件结束符EOF时,getchar ()的返回EOF,这个EOF在函数库里一般定义为-1.
    while((c = getchar()) != EOF){
      putchar(c);  
   }

上述结果, 必须输入是文件结束符EOF,Windows下为组合键Ctrl+Z, Unix/Linux下为组合键Ctrl+Dgetchar才会停止执行,循环才能结束,整个程序将会往下执行

#include "stdio.h"

main()

{

  char c,d,e,f;

  printf("please input two characters:\n");

  c=getchar();

  putchar(c);

  putchar('\n');

  d=getchar();

  putchar(d);

  putchar('\n');

  e=getchar();

  putchar(e);

  putchar('\n');

  f=getchar();

  putchar(f);

  putchar('\n');

  printf("c= %c\n",c);

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

  printf("e= %c\n",e);

  printf("f= %c\n",f);

}

 运行后先输入“12”,回车,再输入“34”,回车。运行流程:

  • 首先输入了两个字符12,然后回车,注意这时写入缓存中的有3个字符1,2,回车。
  • 程序中有四个getchar(),于是c='1',d='2',e='\n'。
  • 这时运行到f=getchar();输入缓存中的三个字符均被前三个getchar获取,这时需要用户输入,
  • 这里输入了34
  • 于是f='3',4和后面的回车没有被利用。
  • 这便是整个流程。

 运算符

  • 双目运算符:+ - * / %
  • 单目运算符: ++  --
  • 自动转换: char
  • 遇到长的运算时,应该从右往左算:
  • 关系运算符>= , > , < , <= , ==
  • 逻辑运算符| |   &&   !
  • 条件运算符 eg: 取最小数  min=x
  • sizeof运算符:返回类型的变量常量占内存字节的个数。
  • 逗号运算符:从左向右计算,取最右边的值。
printf("int_%lu\n",sizeof (int));

运行结果:int_4 

a=10;  b = a+=a*=a-=a+a;

运算过程即:a+a=20; a=a-20=-10;  a=a*a=100;  a=a+a=200;  b=a=200;

  • 运算符优先级

C语言学习_第10张图片

C语言的运算符包括单目运算符、双目运算符、三目运算符,优先级如下:

  • 第1优先级:各种括号,如()、[]等、成员运算符 -> . ;
  • 第2优先级:所有单目运算符,如++、--、!、~等;
  • 第3优先级:乘法运算符*、除法运算符/、求余运算符%;
  • 第4优先级:加法运算符+、减法运算符-;
  • 第5优先级:移位运算符<<、>>;
  • 第6优先级:大于运算符>、大于等于运算符>=、小于运算符<、小于等于运算符<=;
  • 第7优先级:等于运算符==、不等于运算符!=;
  • 第8优先级:按位与运算符&;
  • 第9优先级:按位异或运算符^;
  • 第10优先级:按位或运算符|;
  • 第11优先级:逻辑与运算符&&;
  • 第12优先级:逻辑或运算符||;
  • 第13优先级:三目条件运算符 ?: ;
  • 第14优先级:各种赋值运算符,如=、+=、-=、*=、/= 等;
  • 第15优先级:逗号运算,  

1、最高级:出现同级别运算符时的结合方向是从左往右(下面级别没写结合顺序时,默认是从左往右)。

( )圆括号

[ ]下标运算符号

->指向结构体成员运算符

.结构体成员运算符

C语言学习_第11张图片

2、第二级:!、~、++、--、-、(类型)、*、&、sizeof。

这一级都是单目运算符号,这一级的结合方向是从右向左。

比如出现*p++,这时*和++同级别,先算右边,再左边。

所以*p++等价于*(p++),而不是(*p)++。

C语言学习_第12张图片

3、第三级:+、-、*、/、%

这一级都是算术运算符,结合顺序和数学学习中一致的,先乘除取余数,后加减。

C语言学习_第13张图片

C语言学习_第14张图片

 

4、第四级:<<、>>

这是左移、右移运算符,位运算时可能需要用到。

C语言学习_第15张图片

5、第五级:<、<=、>、>=、!=、==

这些运算符也称为关系运算符,其中<、<=、>、>=高于!=、==。

C语言学习_第16张图片

6、第六级:&、^、|

这三个符号也是位运算符号,其中内优先级,&>^>|。

C语言学习_第17张图片

7、第七级:&&、||

逻辑与&&优先级大于逻辑或||。

C语言学习_第18张图片

8、第八级:?  :

也称为条件运算符号,是C语言中唯一的一个三目运算符,结合顺序是从右往左。

C语言学习_第19张图片

9、第九级:=、+=、-+、*=、/=、%=

这些运算符也叫做赋值运算符,除此之外,>>=、<<=、&=、^=、|=这些赋值运算符也在这一级别内,结合顺序是从右往左。

C语言学习_第20张图片

10、最低级:,

逗号运算符也称为顺序求值运算符,在C语言中,运算级别最低。

C语言学习_第21张图片

声明变量和定义变量的区别是什么? 

(此处转载)

什么是定义,什么是声明

  • 在 C语言中,使用变量之前必须先定义变量。所谓定义变量,就是编译器创建了一个变量,为这个变量分配一块内存并命名(变量名)。例如,定义整型变量 a。int a;
  • 这条语句说明了 a是一个整型变量,编译器将为其分配一块大小为 int型数据所占的内存空间。a没有被赋初值,它的初值默认为 0。在定义的同时,也可以为其赋值进行初始化。如:int a=1;
  • 这条语句不仅说明了编译器为 a分配了内存,还说明了在整型变量 a分配的内存中存储的值。
  • 注意:不应该在头文件中定义变量,因为一个头文件可能会被一个程序的许多源文件所包含。
  • 所谓声明,就是告诉编译器变量的类型,编译器并不为其分配内存,此变量已经定义过,故声明可以多次进行。例如,声明外部变量 a。extern int a;
  • 这条语句说明了 a是一个外部整型变量,并且它的存储空间是在程序的其他区域分配的。extern置于变量前,以标示变量的定义在别的区域中,下面的代码用到的变量 a是外部的,不是本区域定义的,提示编译器遇到变量 a在其他区域中寻找其定义。 声明变量和定义变量的区别:
  • 定义创建了变量,并为其分配内存;声明没有分配内存。
  • 一个变量在一定的区域内只能被定义一次,却可以被多次声明。

变量的三大属性:作用域、链接属性、存储属性

作用域:

变量的使用范围:

  • 文件作用域: 定义开始  直到文件结束
  • 函数作用域: 定义开始  直到函数结束
  • 代码块作用域:定义开始  直到代码块结束
  • 原型作用域: 定义开始  直到原型结束

判断标准:

  • 全局变量 -----------> 文件作用域
  • 局部变量 -----------> 函数内定义
  • 局部变量 -----------> 函数内定义同时代码块内定义
  • 局部变量 -----------> 函数内定义同时为原型内定义

 

 

void fun(int a);    //函数声明(原型)
int a=100;          //全局变量
int main()
{
     
    int a = 200;    //函数内定义变量
    {
     
        int a = 300;  //代码块内定义变量
    }
    fun(500);
    return 0;
}
void fun(int a)     //函数定义  
{
     
    printf("a4");
}

 链接属性

 同一项目不同文件中的相同名称的变量是否是同一变量。

判断标准:

  • 内部链接属性: static + 全局变量  : 不是同一变量
  • 外部链接属性:extern + 全局变量  : 是同一变量
  • 无链接属性:    所有局部变量

存储属性

变量存放在内存的哪个区域:

  • 静态存储区:静态变量----所有的全局变量,static修饰的局部变量。;

从编译开始就占用内存,直到项目运行结束,内存被操作系统回收

  • 栈存储区:自动变量 -----无修饰符修饰的局部变量

用时申请内存,不用系统释放内存。栈的分配运算内置于处理器的指令集中,效率极高,但是分配的内存容量有限。存储在栈中的对象,遇到 ' } ' ,即删除局部变量,不会有有内存碎片。

  • 堆存储区: 也称动态内存分配区

程序员向操作系统申请的内存空间。程序在运行的时候用malloc或new申请任意大小的内存,程序员自己负责在适当的时候用free或delete释放内存。动态内存的生存期可以由我们决定,如果我们不释放内存,程序将在最后才释放掉动态内存。可存储较大的数据。堆是以匿名方式保存的,只能通过指针访问安全性最高,由程序员分配与释放,自由度最高

堆栈的不同:

(1)内存申请方式的不同:如果函数中声明一个局部变量int a,系统会自动在栈中为a 开辟空间;而堆空间需要程序员自己申请,还需要指明变量的大小。

(2)系统响应的不同:只要栈的剩余空间大于所申请的空间,系统将为程序提供内存,否则提示overflow,栈溢出;而对于堆,系统在收到申请空间的要求后,遍历操作系统用于记录内存空间地址的链表,当找到一个空间大于所申请空间的堆结点后,就会将该结点从记录内存空闲地址的链表中删除。并将该结点的内存分配给程序,然后在这块内存区域的首地址处记录分配的大小。这样在使用delete来释放的时候,delete才能正确识别并删除该内存区域的所有变量。另外,申请的内存空间与堆结点的内存空间不一定相等,这是系统个会自动将堆结点上多出来的那一部分内存空间回收到空闲链表中。

(3)空间大小的不同栈是一块连续的区域,大小在编译时就确定的常数,有系统预先根据栈顶的地址和栈的最大容量定义好的;堆是不连续的区域,各块区域由链表串联起来。串联起来的内存空间叫作堆!上限是由系统的虚拟内存来定的

(4)执行效率的不同:栈比较快,由系统自动分批;堆速度较慢,且容易产生内存碎片。

(5)执行函数时的不同:函数调用时,第一个进栈的是被调函数下一行的内存地址(栈的先进后出)。其次是函数的参数,假如参数多于一个,那么次序是从右往左。最后才是函数的局部变量。

其他区域(或者其他叫法)

(1)数据段(属于数据存储区)是存取全局变量静态变量的。全局变量和静态变量是放在一块的,初始化的在一块区域,未初始化的在相邻的另一块区域。程序结束后由系统释放。

(2)代码段是存取函数体的二进制代码的

(3)文字常量区,常量字符串就放在这里,程序结束之后由系统释放。

(4)寄存器区,用来保存栈顶指针和指令指针

****************************************************************************

与存储类型相关的几个属性:

  • atuo:在声明局部变量时,若不指定 static,默认均是 auto,这类变量都是动态分配存储空间的,数据存储在动态存储区中。
  • static:在声明局部变量时,使用关键字 static 将局部变量指定为“静态局部变量”,这样在函数调用结束后不消失而保留原值,即占用的存储单元不释放,在下一次函数调用时,该变量已有值就是上次函数调用结束时的值。
  • register:在声明动态局部变量或者函数形参时,可将变量声明为register,这样编译系统就会为变量分配一个寄存器而不是内存空间,通过这种方式可提升对某些局部变量频繁调用的程序的性能。(寄存器运算速度远高于内存)
  • extern:用于扩展全局变量的作用域,比如如果函数想引用一个外部变量,但该外部变量在该函数后定义,那么这个函数需要使用 extern 来声明变量,这样才能使用在该函数后面定义的全局变量。此外,extern 还可以在多文件的程序中声明外部变量。

二维数组

  • 对于二维数组,int a[2][5] :2表示二维数组的元素个数,元素的类型是 int [5],a等价于a[0][0]。
  • 数组的地址表法:对于一维数组 a[4],&a=&a[0]=a; a+1=&a[1]; &a+1=a+4*4(数组的元素个数*一个元素类型所占的字节数) ,即a表示数组首元素地址,&a表示数组地址。
  • 对于int n=b[m][n]; b+i指第i个一维数组地址。
  • 一维数组数组名: 数组首元素的地址

             a <=====>  &a[0]             *a <=======> a[0]

       地址是有类型的,地址+1 相当于是加了一个类型的字节
            a+1 <=====> &a[1]    

            a+i  <====>&a[i] ;             *(a+i)  <======>a[i]

  • 二维数组:

             &b[0][0] ----------->  第0个一维数组第0个int型的地址
             b         ------------>  第0个一维数组的地址
             &b      ------------>  二维数组本身的地址
             *b        ------------>  第0个一维数组第0个int型的地址
             b[0]     ------------>  第0个一维数组第0个int型的地址

             b+i  ---------------> 第i个一维数组的地址
             *(b+i) -------------> 第i个一维数组的首元素地址
             *(b+i)+j  --------> 第i个一维数组的第j个元素的地址  <=======> &b[i][j]
             *(*(b+i)+j)  -----> 第i个一维数组的第j个元素的值    <=======> b[i][j]

             b[i] + j   ----------->  第i个一维数组的第j个元素的地址  <=======> &b[i][j]
             *(b[i] + j)   --------->  第i个一维数组的第j个元素的值  <=======> b[i][j]

C语言学习_第22张图片

ASCII码对照表

ASCII 编码一览表(淡黄色背景为控制字符,白色背景为可显示字符)
二进制 十进制 十六进制 字符/缩写 解释
00000000 0 00 NUL (NULL) 空字符
00000001 1 01 SOH (Start Of Headling) 标题开始
00000010 2 02 STX (Start Of Text) 正文开始
00000011 3 03 ETX (End Of Text) 正文结束
00000100 4 04 EOT (End Of Transmission) 传输结束
00000101 5 05 ENQ (Enquiry) 请求
00000110 6 06 ACK (Acknowledge) 回应/响应/收到通知
00000111 7 07 BEL (Bell) 响铃
00001000 8 08 BS (Backspace) 退格
00001001 9 09 HT (Horizontal Tab) 水平制表符
00001010 10 0A LF/NL(Line Feed/New Line) 换行键
00001011 11 0B VT (Vertical Tab) 垂直制表符
00001100 12 0C FF/NP (Form Feed/New Page) 换页键
00001101 13 0D CR (Carriage Return) 回车键
00001110 14 0E SO (Shift Out) 不用切换
00001111 15 0F SI (Shift In) 启用切换
00010000 16 10 DLE (Data Link Escape) 数据链路转义
00010001 17 11 DC1/XON
(Device Control 1/Transmission On)
设备控制1/传输开始
00010010 18 12 DC2 (Device Control 2) 设备控制2
00010011 19 13 DC3/XOFF
(Device Control 3/Transmission Off)
设备控制3/传输中断
00010100 20 14 DC4 (Device Control 4) 设备控制4
00010101 21 15 NAK (Negative Acknowledge) 无响应/非正常响应/拒绝接收
00010110 22 16 SYN (Synchronous Idle) 同步空闲
00010111 23 17 ETB (End of Transmission Block) 传输块结束/块传输终止
00011000 24 18 CAN (Cancel) 取消
00011001 25 19 EM (End of Medium) 已到介质末端/介质存储已满/介质中断
00011010 26 1A SUB (Substitute) 替补/替换
00011011 27 1B ESC (Escape) 逃离/取消
00011100 28 1C FS (File Separator) 文件分割符
00011101 29 1D GS (Group Separator) 组分隔符/分组符
00011110 30 1E RS (Record Separator) 记录分离符
00011111 31 1F US (Unit Separator) 单元分隔符
00100000 32 20 (Space) 空格
00100001 33 21 !  
00100010 34 22 "  
00100011 35 23 #  
00100100 36 24 $  
00100101 37 25 %  
00100110 38 26 &  
00100111 39 27 '  
00101000 40 28 (  
00101001 41 29 )  
00101010 42 2A *  
00101011 43 2B +  
00101100 44 2C ,  
00101101 45 2D -  
00101110 46 2E .  
00101111 47 2F /  
00110000 48 30 0  
00110001 49 31 1  
00110010 50 32 2  
00110011 51 33 3  
00110100 52 34 4  
00110101 53 35 5  
00110110 54 36 6  
00110111 55 37 7  
00111000 56 38 8  
00111001 57 39 9  
00111010 58 3A :  
00111011 59 3B ;  
00111100 60 3C <  
00111101 61 3D =  
00111110 62 3E >  
00111111 63 3F ?  
01000000 64 40 @  
01000001 65 41 A  
01000010 66 42 B  
01000011 67 43 C  
01000100 68 44 D  
01000101 69 45 E  
01000110 70 46 F  
01000111 71 47 G  
01001000 72 48 H  
01001001 73 49 I  
01001010 74 4A J  
01001011 75 4B K  
01001100 76 4C L  
01001101 77 4D M  
01001110 78 4E N  
01001111 79 4F O  
01010000 80 50 P  
01010001 81 51 Q  
01010010 82 52 R  
01010011 83 53 S  
01010100 84 54 T  
01010101 85 55 U  
01010110 86 56 V  
01010111 87 57 W  
01011000 88 58 X  
01011001 89 59 Y  
01011010 90 5A Z  
01011011 91 5B [  
01011100 92 5C \  
01011101 93 5D ]  
01011110 94 5E ^  
01011111 95 5F _  
01100000 96 60 `  
01100001 97 61 a  
01100010 98 62 b  
01100011 99 63 c  
01100100 100 64 d  
01100101 101 65 e  
01100110 102 66 f  
01100111 103 67 g  
01101000 104 68 h  
01101001 105 69 i  
01101010 106 6A j  
01101011 107 6B k  
01101100 108 6C l  
01101101 109 6D m  
01101110 110 6E n  
01101111 111 6F o  
01110000 112 70 p  
01110001 113 71 q  
01110010 114 72 r  
01110011 115 73 s  
01110100 116 74 t  
01110101 117 75 u  
01110110 118 76 v  
01110111 119 77 w  
01111000 120 78 x  
01111001 121 79 y  
01111010 122 7A z  
01111011 123 7B {  
01111100 124 7C |  
01111101 125 7D }  
01111110 126 7E ~  
01111111 127 7F DEL (Delete) 删除

数组指针 

比如:int (*p)[4]。则p可以表示一个长度为4的int型的一维数组,sizeof(p)==8(64位系统)。  对二位数组用数组指针输入输出的代码如下

int main(){
    int (*p)[4],a[2][4];
    for (p=a;p

指针数组 

 比如:int*p[4]。则表示包含4个int类型的指针,sizeof(p)==32(64位操作系统)。 对二位数组用指针数组输入输出代码如下:

int main()
{
    int *p[2],a[2][4],i,j;
    for (i=0;i<2;++i) {
        p[i]=a[i];
    }
    for (i=0;i<2;++i) {
        for (j=0;j<4;++j) {
            scanf("%d",*(p+i)+j);
        }
    }
    for (i=0;i<2;++i) {
        for (j=0;j<4;++j) {
            printf("%5d",*(p[i]+j));
        }
        printf("\n");
    }
    return 0;
}

字符数组 

  • char c='s' 和  char c="s" 他俩是不一样的,前者在内存中存的只是 's' 只占一个字节 , 而后者在内存中存放的是 's' 和 ‘\0’ 占两个字节。
  • 对字符数组的初始化: char ss={'H','o','w','/0'};  等价于  char ss="How", 共占4个字节,因为对于第二种赋值操作默认会加上'\0'字符。
  • 库函数  strlen(),strcmp(),strcat(),strncat(),strcpy,strncpy(),strlwr(),strupr().
  • 内存操作相关的库函数:memcpy(),,比如把a数组赋值给b数组。

 int a[5]={1,2,3,4,5},b[5];

memcpy(b,a,sizeof(a));

  • 另外,可里利用memset(a,0,sizeof(a))函数,把a数组初始化。 

memcset(a,0,sizeof(a)); 

第三天

函数指针 

  •  指向函数的指针。定义形式为:int (*ptr) (int x,int y);  使用:ptr =fun_max;  ptr(a,b);
  •  函数名:函数的入口地址,在正文段。所以不可以通过函数指针改变它的值。而数组一般处于内存的栈中或者静态存储区中,其实可以通过指针改变数组中的值。
  • 回调函数:main.c文件中的main函数调用其他文件中的函数比如cross()函数,当cross()函数执行时有需要回调main.c文件中的其他函数。这样的好用就是可以优化任务分工。
  • 函数指针有两个用途:调用函数和做函数的参数

指针函数 

  • 切忌返回栈中的数据地址,因为会自动释放。
  • 指针函数实质是一个函数。函数都有返回类型(如果不返回值,则为无值型),只不过指针函数返回类型是某一类型的指针 
  • 定义格式:类型名 *函数名(函数参数列表);   int *pfun(int, int);
  • 可以在是代码更简洁并在一定程度节约内存;如当你需要返回一个数组中的元素时,你就只需返回首元素的地址给调用函数,调用函数即可操作该数组(让函数返回多个值)。

  • 或者是malloc函数动态分配内存,返回该内存的地址给另外一个函数,另一个函数才好操作该内存。

 typedef关键字

  •  并没有定义变量,而是给现有的类型起一个别名。
  • typedef int array[8];    把array定义为有8个整数的数组类型,array a 就相当于int a[8];
  • 给函数指针取别名:typedef int (*ptr)(int ,int );  ptr p;

结构体 

  • struct 结构体名{ 成员列表 }变量名列表; 
  • 对于同类型的结构体可以互相赋值。 如struct Student student1,student2.  student1=student2; 
  • 对于一个结构体数组 student[5], 定义一个结构体指针 p 指向此数组,则有:(++p)->name 表示取出下一个学生的名字,               (p++)->name  先取得现在学生的名字,然后 p 指向下一个学生。

大小端判断 

转载链接:https://blog.csdn.net/weixin_40315804/article/details/79283397

#include 
typedef union BIAN{
    int a;
    int b;
    char c;
}BIAN;
int main()
{
    BIAN test_bian;
    test_bian.a=0x12345678;
    printf("c=0x%x\n",test_bian.c);           //利用联合体
    printf("c=0x%x\n",*(char *)&test_bian.a);   //利用强转
    return 0;
}

文件句柄 :

  • FILE *pf;
  •  打开文件         pf = fopen(文件路径文件名,  打开方式);

  •  读文件内容     int  ret = fread(&读到某地址, sizeof(每个变量), 多少个变量, fp );返回实际读取的变量个数
  •  写文件内容     int  ret = fwrite(&写到某地址, sizeof(每个变量),  多少个变量,fp );返回实际写入的变量个数
  •  定位文件读写位置           fseek(fp,  偏移量,  参考位置)
  •             参考为指针:
  •                    SEEK_SET      0      跳转到文件的开头
  •                    SEEK_CUR     1      跳转到文件的当前位置
  •                    SEEk_END      2      跳转到文件的末尾
  •  关闭文件           fclose(fp)
#include 
typedef struct student{
    int sum;
    char name[20];
}STU;
int main()
{
    STU array[5]={
     {100,"zhangsan0"},{101,"zhangsan1"},{102,"zhangsan2"},{103,"zhangsan3"},{104,"zhangsan4"}};
    FILE *fp;
    fp=fopen("mystudent.txt","w+");
    int retwrite = fwrite(array,sizeof(STU),5,fp);
    if(retwrite!=5){
        printf("Write Error!   ");
        return -1;
    }
    STU temp[5],*p=&temp;
    fseek(fp,0*sizeof (STU),0);
    int retreed = fread(&temp,sizeof (STU),5,fp);
    if(retreed){
        for (int i=0;i<5;++i,p++) {
             printf("p num=%d,name=%s\n",p->sum,p->name);        
             }
    }
    fclose(fp);
    return 0;
}

C的关键字 

  •  const  制度的变量,不可变的变量。   存储在静态存储区或栈中。

int const a=10;

const int b=20;

int *const ptr;     //表示指针的指向不可变,但指针指向的地址的内容可变。

int const *ptr;    //表示指针的指向可变,但指针指向的地址的内容不可变。

int const *const ptr3;  //指针的指向不可变,指针指向的地址的内容也不可变。

  •  enum 枚举类型     本质还是int 。

enum week 

{

       MON,TUS,WED,THE,FRI,SAT=10,SUN

};

enum week w1=MON;

enum week w2=SUN;

printf("MON=%d,SUN=%d",w1,w2);

        输出: MON=0,SUN=11; 

  • malloc  栈空间系统自动分配,而堆空间需要程序员自己释放使用free()函数,否则就可能出现内存泄漏问题。若申请成功,则 返回申请空间的起始地址(void *) ,可以被转换成任何一种指针类型。若申请失败,则返回NULL。free()函数无返回值。

C语言的预处理 

  • C语言运行过程:预处理-->编译-->汇编-->链接
  • 1.预处理(Preprocess):

宏展开(替换)#define 定义宏,#undef 取消宏定义 ;文件包含(指令#include):相当于复制文件内容;条件编译  如#ifdef,#ifndef,#else,#elif,#endif;特殊符号,例如LINE标识将被解释为当前行号,FILE被解释为当前被编译的C源程序的名称。 最后生成以.i为后缀的文件

#define A(x,y)   x+y

#define B(x,y)  (x+y)

#undef B 

  • 2.编译(Compilation)   

对预编译之后的文件进行编译,将其翻译成等价的中间代码或汇编代码生成以.a为后缀的文件

  • 3.汇编阶段(Assembly) 

将汇编代码翻译成目标机器指令的过程,每一个C语言源程序将得到相应的目标文件,也就是说,一个.c文件就会生成一个.obj文件,而这些文件还没有建立起练习,并不能相互调用

  • 4.链接(Linking)

链接程序的主要工作就是将有关的目标文件彼此相连接,也即将在一个文件中引用的符号同该符号在另外一个文件中的定义连接起来,使得所有的这些目标文件成为一个能够被操作系统装入执行的统一整体。在这一阶段,根据指定的同库函数的链接方式的不同,链接处理可分为静态链接和动态链接

  • a.静态链接 在这种链接方式下,函数的代码将从其所在地静态链接库中被拷贝到最终的可执行程序中。这样该程序在被执行时这些代码将被装入到该进程的虚拟地址空间中。静态链接库实际上是一个目标文件的集合,其中的每个文件含有库中的一个或者一组相关函数的代码。

    b.动态链接 在此种方式下,函数的代码被放到称作是动态链接库或共享对象的某个目标文件中。链接程序此时所作的只是在最终的可执行程序中记录下共享对象的名字以及其它少量的登记信息。在此可执行文件被执行时,动态链接库的全部内容将被映射到运行时相应进程的虚地址空间。动态链接程序将根据可执行程序中记录的信息找到相应的函数代码。

位运算 

  •  按位运算  &与 (位清零)    | 或(位置1)       ^ 异或(位取反)     ~取反 (全部位取反)      >>右移(C中:若无符号数则是逻辑右移,即高位补0;若是有符号数则是算数右移,即高位补符号位)     << 左移

积累例题 

#include 
int main()
{
    char s[]={"0120\x30y"};int i,n=0;
    for(i=0;s[i]!='\0';i++)
        if(s[i]>='0'&&s[i]<='9') n++;
    printf("%d\n",n);
    return 0;
}
输出结果为:5      因为\x30表示16进制,其值为10进制的48,也就是字符'0',所以算一个'0'~'9'之内的字符!  \ddd表示8进制数,如\60为10进制的48,也就是字符'0'!

switch case的执行总结:

如switch(n),则优先找case 中的值能和n相等的语句执行,然后直到遇见break或者switch结束的}为止,才结束运行,在此过程中会可能运行其他case值不与n匹配的语句段。

若有定义语句:int x=10;,则表达式 x - = x + x 的值为  -10!

int  main(){
    char  c1[]={'h','h','h'};
    char  c2[]={"hhh"};
    char  c3[10]={"hhh"};
    printf("sizeof(c1)=%llu\n",sizeof (c1));
    printf("sizeof(c2)=%llu\n",sizeof (c2));
    printf("sizeof(c3)=%llu\n",sizeof (c3));
}
输出结果为: sizeof(c1)=3   
            sizeof(c2)=4     //以字符串的形式赋值给字符数组,会默认在后面添加一个'\0’结束符
            sizeof(c3)=10

判断浮点型(float)和0值比较的的方式:(-1e-6, 1e-6)      即: if(   flag >-1e-6 && flag <1e-6 )  也就是flagj比-0.000001大并且比0.000001小,落在这个范围内,就认为其大致等于0了。

如果想从一个函数中获取一个结构体数组,最好不要把函数的返回值设置为结构体指针,而是把要赋值的结构体指针传到函数中,然后在函数中用for为该结构体指针赋值即可!!! 

在使用C语言中的文件系统时,当写入文件用的是"wb"即以二进制的形式写入时,读的时候也必须用“rb”或“rb+”,即以二进制的形式读出,否则会出错!!! 

c---清空stdin

转载枫继续吹 

今天在Linux程序设计的时候需要清空标准输入缓冲区,于是使用了如下Windows程序设计中的方法:

1.fflush(stdin);这个fflush()函数根本不是标准C中的函数,只是标准C的扩展,所以在Linux中使用根本不行;

2.中网上搜索了下,发现有网友建议使用rewind(stdin);这个函数其实是将指针指向流的开始处。但是它是文件操作中的一个函数,操作的是FILE型流,在Windows程序设计中是可以清空标准输入缓冲区的,但是在Linux中不行。

3.通过读完标准缓冲区中的剩余字符并丢弃掉来清空标准缓冲区,使用的函数是getchar(),此函数的作用是从标准输入缓冲区中读出一个字符,此方法中Linux中可行。

如果需要清除stdin可以通过如下循环实现:

char ch;

while((ch=getchar())!='/n'&&ch!=EOF);

以上语句将清除stdin中的字符,知道遇到换行符或者是读完缓冲区。

以上的方法均可以在Windows程序设计中用来清除缓冲区;

4.在Linux程序设计中也还是有一种用来清空stdin中的方法,直接调用如下的函数:

setbuf(stdin, NULL);

此函数我还没有来得及测试;

通过以上分析,在Linux下清空stdin用两种方法:

1.通过读取剩余的字符并丢弃掉;

char ch;

while((ch=getchar())!='/n'&&ch!=EOF);

或者是:

char s[1024];

fgets(s,1024,stdin);

2.使用函数setbuf(stdin,NULL);

scanf("%d",&n); 但输入字符

应当输入数字,如果输入字母,scanf 语句执行失败。

int flag;
flag = scanf("%d",&n);
if (flag== 1) printf("good\n"); else printf("bad\n");

flag = scanf("%d",&n); 送返成功读入的数据个数。拍入字母,flag 得0,n 原有值不变。

由于输入流里的你拍入的字母 没有被清掉,后面的输入语句连续执行失败。可以用 fflush(stdin); 清除 多余的东西。

#include
using namespace std;
int main(){
    int tmp,t,a[1000],count=0;
    while((t=scanf("%d",&tmp)) != EOF){
        if(!t)
            //scanf函数接收整型数字要跳过所有非数字的字符专接收数字。那么如果输入了字符它就一直留在输入缓冲区
            //只要缓冲区有字符,scanf就要去读,一读不是数字,它就跳过。 
            fflush(stdin);//此函数可以清除缓冲区中的字符 
        else
            a[count++] = tmp;
    }
    for(int i=0; i         cout<     }
    return 0;
}
————————————————
版权声明:本文为CSDN博主「liubin477」的原创文章,遵循 CC 4.0 BY-SA 版权协议。
原文链接:https://blog.csdn.net/qq_36668065/article/details/80154588

总结:用scanf("%d",&x);为变量赋值整数时,应用以下代码过滤:(这是原创不是转载) 

 

while(!scanf("%d",&x))        //若输入的不是数字,返回0,知道输入的是数字为止
{
   printf("[Warining] You must input number!\n");
   fflush(stdin);     //输入的不是数字就清空缓冲区
 }

养成一个每次用完scanf()函数时就清空缓冲区。以免影响程序的正常运行。 为此,自己编写了一个过滤非数字输入的scanf函数:

#define INT 1

 int a;  myScanfInt(INT,&a);

void myScanfInt(int datatype,int *p)
{
    if(datatype==INT)
        while(!scanf("%d",p))
        {
            printf("[Waring] You must input a number!\n");
            fflush(stdin);
        }
    fflush(stdin);
}
 

删除e69da5e6ba90e799bee5baa6e997aee7ad9431333337616635文件部分内容的大概步骤:新建一个临时文件,把原文件内容向临时文件里拷贝,遇到要删除的内容就跳过。结束后关闭文件,用remove("原文件名");把原文件删除,用rename("临时文件名","原文件名");把临时文件名改为原文件名。2、例如在原文件123.txt中删除以2和以4编号开头的行,可用以下代码实现:

 

#include "stdio.h"

#include "stdlib.h"

int main(void){

    FILE *fin,*ftp;

    char a[1000];

    fin=fopen("123.txt","r");//读打开原文件123.txt

    ftp=fopen("tmp.txt","w");//写打开临时文件tmp.txt

    if(fin==NULL || ftp==NULL){

        printf("Open the file failure...\n");

        exit(0);

    }

    while(fgets(a,1000,fin))//从原文件读取一行

        if(a[0]!='2' && a[0]!='4')//检验是要删除的行吗?

            fputs(a,ftp);//不是则将这一行写入临时文件tmp.txt

    fclose(fin);

    fclose(ftp);

    remove("123.txt");//删除原文件

    rename("tmp.txt","123.txt");//将临时文件名改为原文件名

    return 0;

}

 

你可能感兴趣的:(c语言,编程语言)