欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

C语言学习笔记

程序员文章站 2024-03-23 19:30:28
...

一、C语言

  • 算法的特性
    有穷性:有限的操作步骤
    确定性:每一个步骤是确定的
    有输入:零个或多个
    有输出:一个或多个
    有效性:每个步骤都能有效执行
  • 结构化程序设计
    结构化程序:高级语言表示的结构化算法
    结构化程序设计方法的思路:把一个复杂问题的求解过程,分段进行,每个阶段的处理控制在容易理解和处理的范围内
    方法:自顶向下,模块化设计(自顶向下,逐步细化;自下而上,逐步积累)

二、数据类型

  • 常量
    程序执行过程中,值不能被改变.
    符号常量:#define PRICE 30 (此后出现PRICE都表示30),在其作用域不能改变,不能被赋值
  • 变量
    变量表示内存中具有特定属性的一个存储单元,用来存放数据。
    变量名是以一个名字代表一个地址,编译系统给每一个变量名分配对于的内存地址。
    变量名只能字母、数字、下划线,第一个必须是字母或下划线
    区分大小写字母,长度最后不超过8个字符,先定义后使用
  • 整型常量
    八进制:以0开头,0123、-012
    十六进制:以0x开头,0x123、-0x123
  • 浮点型常量
    表示形式:小数、指数
    指数形式:(数字.)数字E/e+/-数字
  • 字符常量
    单引号包含一个字符
    以\开头的特殊字符成转义字符
    字符常量的存储,不是将字符本身存放,而是将字符对于ASCII代码放到存储单元中,这样字符型和整型可以通用,一个字符数据既可以以字符输出,也可以整数形式输出
  • 强制类型转换
    (类型名)(表达式):int(a%5)、(double)a
  • 自增自减
    i++:先使用i的值,在i=i+1
    ++i:先i=i+1,再使用i的值
  • 复合运算符
    a+=3 等价于 a=a+3
    x*=y+8 等价于 x=x*(y+8)
  • 逗号表达式
    表达式1,表达式2:先求解1,再求解2
    a=35,a4 先a=35=15,再a4,结果为60

三、C程序设计

  • 输入输出
    字符输出:putchar©; //c为字符或整型变量
    字符输入:getchar©;
    字符串输出:puts(str);
    字符串输入:gets(str);
    输出:printf(); //%md指定宽度 %m.nf 输出数据占m列,其中n位小数,如长度小于m,左端补空格
    输入:scanf("%d",&a); //a是内存中的地址,&是地址运算符
  • 选择结构
    &&与:a&&b&&c 只要a为假就不判断b和c了
    ||或 :a||b||c 只要a为真就不判断b和c了
    !非
    条件运算符:(a>b)?a:b:如果为真,返回a,否则返回b
    switch(表达式)
    { case 常量:语句1; //case ‘A’:printf(“85~100”);
    default:语句;
    }
  • 循环结构
    while(表达式)语句:先判断表达式,后执行语句
    do 语句 while(表达式); :先执行一次语句,后判断
    break:跳出循环体,结束循环
    continue:结束本次循环(即不执行本次循环剩下语句,开始下一次循环)

四、数组

1. 一维数组

数组名和变量名相同
举例:82页 Fibonacci数列、冒泡法排序

2. 二维数组

初始化
int a[2][3]={{1,2,3},{4,5,6}}; // 也可不加里面的花括号
int a[2][3]={{1},{2}};
int a[][3]={1,2,3,4,5,6}; //前面可省
举例:87页 行列互换、找最值

3. 字符数组

char c[5];
char c[4][5];
字符串结束标志:\0 字符串常量会自动加一个\0作为结束符,10个字符的有效字符为9个
初始化:char c[]=“I am a boy”;
char c[]={‘I’,’ ‘,‘a’,‘m’,’ ‘,‘a’,’ ‘,‘b’,‘o’,‘y’,’\0’};
scanf("%s",str); //不能获取带空格的字符串
gets(str); //可以获取带空格的字符串,缺点不会管str大小,容易污染内存
char *fgets(char *s, int size, FILE *stream) //可以获取带空格的,也可保证内存不越界
//size是最大长度-1

4. 二维字符数组

char str[3][4]={"","",""}; //有3个字符串,每个字符串占1行,每个字符串占4-1个字节
scanf("%s",str[i]);

五、函数

  • 函数
    程序编译以源程序文件为单位,不是以函数为单位进行编译的
    程序执行从main函数开始,在main结束

1. 形参和实参

有参函数中,定义函数时函数名后面括号中的变量称“形参”,调用函数时,函数名后括号中称“实参”.
形参从未出现在函数调用时,并不占存储单元,调用结束后,所占内存单元也被释放.
实参可以是常量、变量、表达式,但必须有确定的值.
实参想形参传参是 值传递,单向传递

2. 函数递归

调用一个函数的过程中又直接或间接调用该函数本身
举例:p111,汉诺塔Hanoi

3. 数组元素作函数实参

实参可以是表达式,数组元素是表达式的组成部分,可作为实参,单向传递,值传递
举例:114,排序

4.数组名作函数参数

此时形参用数组名或指针变量
举例:115,求平均、选择法排序

5. 局部变量和全局变量

  • 局部变量
    在一个函数内部定义的变量是内部变量,只在本函数范围内有效。
    主函数定义的变量只能在主函数中有效,不能使用其他函数中定义的变量
    形参是局部变量
    一个函数内部,复合语句中定义的变量只在符合语句中有效
  • 全局变量
    函数外定义的变量
    全局变量在全部执行过程都占用存储单元,而不是仅在需要时才开辟单元

6.静态存储和动态存储方式

从变量值存在时间角度,分静态存储方式(系统分配固定存储空间)和动态存储方式(动态分配存储空间)
存储空间分三部分:程序区、静态存储区、动态存储区
四种存储方式:自动的(auto)、静态的(static)、寄存器的(register)、外部的(extern)

  • auto(动态)
    局部变量不声明static,都是动态分配存储空间,在调用函数时系统分配存储空间,调用结束自动释放存储空间,称自动变量,用auto声明(auto int b,c=3;)
  • static(静态)
    局部变量的值在函数调用结束后不消失而保留原值,起占用的存储单元不释放,下一次调用时,变量已经有值,是上一次结束调用的值,称静态局部变量,用static声明
  • register(包含静态和动态方式)
    程序用到哪一个变量的值,由控制器发出指令将内存中该变量的值送到运算器中,为提高执行效率,允许将局部变量的值放到CPU寄存器中
  • extern(静态)
    外部定义的全局变量,可在程序中各个函数所引用,可在一个文件内声明外部变量,也可在多个文件声明外部变量。如果想只限于本文件引用,可定义外部变量时加一个static声明

7. 变量的定义和声明

定义:需要建立存储空间,如 int a
声明:不需要建立存储空间,如 extern a;

8. 内部函数和外部函数

  • 内部函数
    一个函数只能被本文件中其他函数调用,定义时在前面加static
    如static int fun(int a,int b)
  • 外部函数
    定义函数是,加上extern,可供其他函数调用,如果省略extern隐含为外部函数
    调用外部函数,用extern对函数声明,表示该函数是在其他文件中定义的外部函数

6. 预处理

  • 预处理命令
    宏定义、文件包含、条件定义,这些命令以#开头,如#define 、#include
  • 宏定义
    #define 标识符 字符串
    #define PI 3.1415926
    可以层层置换
    带参数的宏定义:不是简单的字符串替换,还要进行参数替换
    #define 宏名(参数表) 字符串
    #define S(a,b) ab //Area=S(3,2) 结果为32=6
  • 文件包含
    指一个源文件可以将另一个源文件全部内容包含
    被包含文件与其所在文件,编译后是同一个文件
  • 条件编译
    是对部分内容指定编译的条件,使其满足一定条件才进行编译
    几种形式
    #ifdef #ifndef #if
    #else
    #endif

六、指针

1. 指针

  • 地址和指针
    地址:内存区每个字节有一个编号
    按变量地址存取变量值的方式称"直接访问",将变量的地址存到另一个变量中称"间接访问"
    指针:一个变量的地址称该变量的指针
    32位平台任何指针类型都是4字节
  • 指针的定义和引用
    定义步骤:定义变量类型,*修饰变量,建立关系
    int num=10;
    int p;
    p=#
    指针变量中只能存放地址
    & 是取地址、是取值
    举例:142
    []和
    ()关系:[]是
    ()的缩写

2. 数组指针

  • 数组元素的指针就是数组元素的地址 举例:选择法排序,151
    定义指向数组元素的指针:int a[10]; int *p; 赋值:p=&a[0];
  • 引用数组元素:下标法:a[i] 指针法:(a+i)或(p+i)
    数组名作函数参数:数组名是数组首元素地址,传递的值是地址,要求形参是指针变量
    四种情况:形参和实参都用数组名、实参数组名形参指针变量、形参实参都用指针变量、实参指针变量形参数组名
    指向同一数组的两个指针变量可以相减,可以相互赋值,不可以相加
int arr[5]={10,20,30,40};
int *p=&arr[0];
printf("%d\n",*p++); //10
printf("%d\n",(*p)++); //20
printf("%d\n",*(p++)); //21
  • 本质是数组,只是数组每个元素是 指针
int num1=10,num2=20,num3=20;
int *arr[3]={&num1,&num2,&num3};
  • 多维数组指针
int arr[3][4];
int (*p)[4];
p=arr; //p和arr等价的前提是 二维的列数4和指针4相等

int a[3][4]={{1,3,5,7},{9,11,13,15},{17,19,21,23}},首行首地址是2000

表达式 含义
a 二维数组名,指向a[0],即0行首地址 2000
a[0]、*(a+0)、*a 0行0列元素地址 2000
a+1、&a[1] 1行首地址 2008
a[1]、*(a+1) 1行0列元素,a[1][0]地址 2008
a[1]+2、*(a+1)+2、&a[1][2] 1行2列元素,a[1][2]地址 2012
(a[1]+2)、(*(a+1)+2)、a[1][2] 1行2列元素a[1][2]的值 13

3. 指针作为函数的参数

想在函数内部修改外部变量的值,需要将外部变量的地址传给函数

void swap(int *a,int *b)
{
int tmp;
tmp=*a;
*a=*b;
*b=tmp;
}
//调用
swap(&date1,&date2)

4. 函数指针

本质是指针变量,保存函数的入口地址

int mul(int a,int b)
{
	return a*b;
}
int cal(int a, int b, int (*fun)(int,int))
{
	return fun(a,b);
}
void point8()
{
	printf("%d\n",cal(20,10,mul));
}

5. 字符串与指针

字符数组存放字符串:char strint[]=“I love you”;
字符指针指向字符串:char * *string=“I love you”;
对字符串字符的存取可用:下标法、指针法
字符数组只能对各个元素赋值,不能用char str[14]; str=“I love you”;
字符指针变量,可以用char * *a; a=“I love you”; 赋给a的不是字符,而是第一个元素的地址
字符指针变量,赋初值,char *a=“I am” 等价于char * *a; a=“I am”;
字符数组,赋初值,char str[4]={“am”}; 不能等价char str[4]; str[]=“am”;
字符数组,编译时为它分配内存单元,有确定的地址,字符指针变量,如果未分配地址值,并未执行具体一个字符数据
指针变量的值是可以改变的

6. 字符串处理函数

以str开头的都是遇到\0自动结束
strcat(str1,str2); //把2接到1后面,结果放在1中
strcpy(str1,str2); //把2复制到1中,1长度不小于2,\0也要复制
strcmp(str1,str2); //比较1和2,自左到右比,直到遇到不同或\0结束,1=2值为0,1>(<)2值为正\负数
strlen(str); //字符串长度,不包括\0
strlwr(str); //将字符串中大写字母换成小写
strupr(str); //小写换大写
strchr(str1,ch); //查找字符串中,字符c出现的位置,返回c和后面的,失败返回null
strstr(str1,str2); //查找字符串,字符串str2出现的位置,返回str2和后面的,失败返回null
strtok(str," : "); //字符串切割函数,失败返回NULL
第一次切割,str是首元素地址
第2-n次切割,str执行NULL
atoi、atol、atof:将字符串转成int long float型
sscanf(“abcDeFABC”,%[a-z],buf); //提取a-z
举例:97页

7. const

const int *p;  //const修饰的是*,而不是p,*p只读,*p可读可写
int * const p; //const修饰的是p,而不是*,*p可读可写,p只读

8. 动态内存申请

  • malloc函数
    void *malloc(int size);
    成功:返回空间起始地址,失败:返回NULL
    对于malloc申请的空间,内容不确定,一般要用memset情空
void test01()
{
int *addr=NULL;
addr=(int *)malloc(sizeof(int)); //前后类型都要是int,进行了强制类型转换
memset(addr, 0, sizeof(int));
free(addr); //释放空间
}
  • calloc函数
    void *calloc(size_t nmemb, size_t size)
    在堆中申请 nmemb块,每块size大小

  • realloc函数
    void *realloc(void *s, int newsize)
    追加,在s指向的内存上重新申请内存,新的内存大小是newsize,包含之前的,因为s是起始位置

七、结构体和共用体

1. 结构体

  • 结构体定义
    定义结构体类型时,没有分配控件,不能赋值
//先定义类型,再定义变量
struct stu
{
int num;
char name[32];
int age;
};
struct stu lucy;

//定义类型的同时定义变量
struct stu
{
int num;
char name[32];
int age;
}lucy;

//定义一次性结构体
struct
{
int num;
char name[32];
int age;
}lucy;
  • 结构体调用
    调用
    左边是普通结构体变量:lucy.num,lucy.name,lucy.age
    左边是地址:p->num,lp->name,p->age

把一个结构体赋值给另一个

// 方法1:逐个赋值
bob.num = lucy.num;
strcpy(bob.name,lucy.name);
bob.age = lucy.age;
// 方法2:相同类型的结构体变量,直接赋值
bob=lucy;
// 方法3:方法2的底层实现
memcpy(&bob,&lucy,sizeof(struct stu));
  • 结构体数组
    struct stu arr[5];

2. typedef

  • typedef给类型起别名,步骤:
    先用类型定义变量:int a;
    用别名替换变量名:int INT32;
    前面加typedef:typedef int INT32;

3. 共用体

  • union
    共用体空间由最大的决定,是最后一次赋值有效

4. 枚举

将变量的值一一列举出来,变量的值只限于列举出来的值的范围内

  • 定义
    enum 枚举名
    {
    枚举值表(也称枚举元素)
    };

八、文件

1. 文件

  • 文件存取过程
    程序数据区(内存)----文件缓冲区(系统/程序)—文件(磁盘0
    缓冲区目的:提供存取效率
  • 磁盘文件的分类
    物理上所有磁盘文件都是以字节为单位进行顺序存储
    从用户或操作系统使用角度分为:
    文本文件:基于字符编码的文件(ASCII、UNICODE等,可用记事本打开)
    二进制文件:基于值编码的文件(把磁盘中的数据按在内存中的存储形式原样输出到磁盘上,提高效率,但不好打开)
    C语言不能直接操作文件,要用库函数间接对文件进行操作
  • 库函数操作文件的相关信息,不是文件数据

2. 定义文件指针

FILE *fp=NULL;

  • 说明:
    FILE是系统用typedef定义出来的有关文件信息的一种结构体类型
    FILE结构体含有文件名、文件状态、文件当前位置等信息
    一般操作文件前,要定义一个文件指针指向将要操作的文件

3. 文件打开和关闭

  • 文件打开fopen()
    FILE *fp = NULL;
    fp=fopen(文件名,文件使用方式);
    文件名:要操作文件的名字,可包含路径信息

  • 文件使用方式:读、写、文本或二进制
    r:只读方式打开,文件不存在返回NULL
    w:只写方式,文件不存在创建,文件存在则清空内容
    a:追加方式打开,文件不存在创建,存在在结尾追加
    +:同时以读写打开,即rw
    b:二进制文件
    t:文本文件,默认是t,可省略
    打开方式与文件的存储无关,与操作系统有关
    fp文件指针,打开失败返回空

  • 文件关闭fclose()

// 打开文件、关闭文件
FILE *fp_aaa = NULL;
fp_aaa=fopen("aaa.txt""r+");
if(fp_aaa==NULL)
printf("file open error");
fclose(fp_aaa);

4. 文件读写

  • 顺序读写
    字节读写函数:fgetc()、fputc()
    字符串读写函数:fgets()、fputs()
    数据库读写函数:fread()、fwrite()
    格式化读写函数:fscanf()、fprintf()
  • 字节读写
    ch=fgetc(fp);
    文本文件:读到结尾返回EOF
    二进制文件:读到结尾,使用fconf判断结尾
    fputc(ch,fp);
    输出失败,返回一个EOF
    EOF是stdi0.h定义的常量,值为-1
  • 字符串读写
    fgets(str,n,fp);
    从fp指向的文件中读入n-1个字符,在读入n-1个字符之前遇到换行符或EOF,读入结束,并读取换行符,在最后加一个’\0’,str为存放数据的首地址
    读取成功返回字符串首元素地址
    读取失败,返回NULL
    fputs(“china”,fp);
    第一个参数可以是字符串常量、字符数组名、字符指针
    字符串末尾的’\0’不会写到文件中
  • 数据块的读写
    fread(buffer,size,count,fp);
    fwrite(buffer,size,count,fp);
    buffer:指向存储数据空间的首地址的指针
    size:一次读写的数据块的大小
    count:要读写的数据块个数
    返回值:实际读写的数据块数
    fwrite将内存的数据原样的输出到文件中,写入文件的数据不便于用户查看但不会影响程序读取
  • 随机读写
    rewind(fp);
    复位文件流(将文件内部的位置指针移到文件首部)
    ftell(fp);
    获得文件流目前的读写位置
    返回值:距离文件首部的字节数
    fseek(fp,位移量,起始点);
    移到文件流的读写位置(一般用于二进制文件)
    位移量:以起始点为基点,向前、后移动的字节数
    起始位置:
    文件开头 SEEK_SET 0
    文件当前位置 SEEK_CUR 1
    文件末尾 SEEK_END 2
  • 文件格式化操作
    fprintf(文件指针,格式化字符串,输出表列);
    fscanf(文件指针,格式化字符串,输入表列);
    用fprintf和fscnaf对磁盘文件读写使用方便,但在输入时要将ASCII码转换为二进制形势,输出时将二进制形式转换为字符,花费时间较多。
    在内存与磁盘频繁交换数据的情况下,最好用fread和fwrite函数

5. 文件结束和出错检测

  • 文件结束检测
    feof(fp);
    文件未结束返回0,文件结束返回非0
  • 读写文件出错
    ferror(fp);
    检查文件在用输入输出函数进行读写时是否出错(比如只读方式打开文件,却调用写函数)
    返回0表示未出错,否则有错
  • 文件出错标志和文件结束标志置0
    clearerr(fp);
    清除出错标志和文件结束标志,使他们值为0,无返回值
相关标签: C语言 c#