概述
一、概述
简介
语言历史:
机器语言:最早期的低级语言,直接通过0和1来编写程序,可被计算机直接识别
汇编语言:使用一些符号来替代0和1,同样不容易理解,且依赖于硬件体系
高级语言:通常使用英文来作为程序设计语言,更加人类思维,且屏蔽了底层复杂的硬件体系结果
C语言:一种面向过程的高级语言,70年代初期由美国的程序员丹尼斯·里奇改写,其程序标准由ANSI定义,最开始是用于替代汇编,来重写unix;
优点:高效率、跨平台、灵活、可操作内存、访问硬件、强大函数库等;且由于c的出现,此后诞生的程序语言大致都参照与c语言的风格;
缺点:指针的风险,面向过程性等.
Hello World
#include<stdio.h> /* 头文件包含命令、预处理操作 */
int main(int argc, char *argv[]){ /* main函数、程序入口 */
printf("hello world"); /* 输出语句 */
getchar(); /* 缓冲,避免一闪而过 */
return 0; /* 返回值 */
}
c语言命名规范:常量命名大写、变量命名有意义且首字母小写、函数首字母大写.
注释: // /* */
标识符:必须以字母或下划线开头、且c语言严格区分大小写. 标识符长度根据不同的编译器也不同.
关键字:共32个、如常见的if else return while 都是.
编译过程
编译流程图:
1,预处理:读取.c源程序,对其中的伪指令(#开头)和特殊符号进行处理变成.i文件,主要包括:
- 宏定义指令,宏替换,将程序中所有使用的宏定义变量,替换成对应字符串
- 条件编译指令,如#ifdef #elif(用于处理释放编译代码)等进行过滤.
- 头文件包含指令,如#include. 可将包含的.h头文件内容加载.
- 特殊符号,如源程序的LINE会被解释成当前行号,FILE则是当前编译的c源程序名称
2,编译:经过预处理得到的.i文件,将只有常量、变量、c语言关键字等定义,在确认语法规则后,将其翻译成汇编代码;
3,汇编:将汇编代码翻译成目标机器指令的过程,通过这一处理得到相应的目标文件;扩展名通常为.o、.obj;
4,链接:即关联所有的目标文件(机器指令)、引用的函数库,并链接,最终形成可执行的文件
静态链接:将关联的函数代码,从所在的静态库中拷贝到最终的程序;
动态链接:关联的函数代码被存放在某个位置中,通过映射地址找到关联的函数库;
C编程中文件后缀介绍:
.c:源代码
.h:头文件,本质上就是.c源程序,用于为其它c程序提供服务. 避免重复代码编写.
.i:经过预处理的c源码
.s .asm:汇编代码
.o .obj:目标文件,存放了机器代码.
GCC编译器
编译器:将高级语言解释成计算机所识别的机器语言指令,最常用为gcc,windows可下载mingw-w64,并将mingw64\bin注册到环境变量中;
简单编译(-o):最常用,可直接生成可执行文件,内部包含了预处理、编译、汇编、链接四个阶段
gcc demo.c -o demo
1,预处理:进行预处理,并生成.i文件
gcc -E demo.c 或者 gcc -E demo.c -o demo.i
2,编译:即将预处理后的.i文件,翻译成汇编代码,为.s文件
gcc -S demo.i -o demo.s
3,汇编:将汇编代码翻译成最终的目标文件,即机器指令(二进制),通常为.o、.obj
gcc -c demo.s -o demo.o
4,链接:将程序的目标文件和所有关联的文件,相关联,并最终生成可执行文件
gcc demo.o -o demo
多个程序文件的编译:开发时通常会直接生成可执行文件
gcc demo1.c demo2.c -o demo
检查错误:-pedantic选项可以查看不符合ANSI/ISO C标准的代码 -Werror可以查看错误信息
gcc -pedantic demo.c -o demo
gcc -Werror demo.c -o demo
库连接:有时候可能会使用除了标准c之外的函数库,那么就需要进行库连接,通常是.so、.lib、.dll
gcc -c -I /usr/dev/mysql/include demo.c -o demo.o
强制链接时使用静态链接库:默认情况下,gcc在链接时优先使用动态连接库(通常以.so、.dll结尾),只有动态链接库不存在时,才会考虑静态链接库,可通过-static强制进行
gcc –L /usr/dev/mysql/lib –static –lmysqlclient demo.o –o demo
unix/linux静态库链接时搜索路径顺序:
1. ld会去找GCC命令中的参数-L
2. 再找gcc的环境变量LIBRARY_PATH
3. 再找内定目录 /lib /usr/lib /usr/local/lib 这是当初compile gcc时写在程序内的
动态链接时、执行时搜索路径顺序:
1. 编译目标代码时指定的动态库搜索路径
2. 环境变量LD_LIBRARY_PATH指定的动态库搜索路径
3. 配置文件/etc/ld.so.conf中指定的动态库搜索路径
4. 默认的动态库搜索路径/lib
5. 默认的动态库搜索路径/usr/lib
有关环境变量:
LIBRARY_PATH:指定程序静态链接库文件搜索路径
LD_LIBRARY_PATH:指定程序动态链接库文件搜索路径
二、数据类型/运算符
数据类型
数据类型:规定在内存中开辟空间存储数据的类型. 对存储、获取、解释都有作用.
- 基本类型: C语言中的基本数据类型、包括所有数值类型、枚举.
- 构造类型: 就是使用基本类型的数据、进行添加、设计构造出新的数据类型来解决问题. 如struct
- 指针类型: C语言的精华就是指针、其值代表某个内存地址;
- 空类型: 关键字是void、主要用于对函数返回的限定、对函数参数的限定.
变量: 标识符,用于存储数据 . 指向数据的空间,其值在程序间可被修改 .
常量: 在程序运行期间其值不可被修改,可使用 const 或 # define 宏定义来实现 .
基本数据类型:
整型:short int long long-long
浮点型(实型):float double long-double
字符:char
布尔:_Bool(在stdboo.h头文件或使用bool)
unsigned:代表无符号,只可代表正数,省略空间. 可表示更大的正数.
signed:带符号,可表示正负数,默认情况下所有基本数据类型都带符号;
说明:
- 在c99标准后,可直接使用变量,而无需先声明所有变量、再使用;
- int num = 018; 八进制 int num = 0x18; 十六进制
- 可对数值常量指定存储类型,例如422L:代表long
232.21f:代表float
e计数法:3.15e7 代表3.15乘以10的7次方. 如为e-7则代表除以10的7次方. 7称为指数.
char:用于存储单个字符,但实际存储的是int字符编码,长度为8位;将'FILE'赋给char,只能存储最后8位,最终变量的值为'E';
字符和字符串:使用单引号''代表一个字符,而使用双引号” ”代表一个字符串,c语言存储字符串时会自动加上\0作为结束标志;c语言中使用字符串是通过char指针和char数组;
typedef:可为任意数据类型起别名,例如:typedef int myint; 通常用于block、struct等
自动数据类型转换:
int num1 = 20; //需要级别兼容性、如:int 转 double
double num2 = num1;
强制数据类型转换:
float f = 20.3f; //目标类型必须大于被转换类型,否则可能数据丢失;
int num = (int) f;
自动类型提升机制:float在传参时被提升为double. char和short会被提升为int类型;
运算符
赋值运算符:= += -= *= /= %=; //可多重赋值,从右往左开始
算术运算符: + - * / % ++ —
num++:先运算再自增,例如:num++ * 2 == num + 1 * 2
++num:先自增后运算,例如:++num * 2 == (num + 1) * 2
关系运算符: > >= < <= == !=
逻辑运算符: &&:与 ||:或 !:非
双个具有断电功能,即一边不符合条件,则另外一边不会再判断;
在c语言中、可用逗号运算符将多个表达式分隔、例如:
int value = 2+5,1+2,5+7; //赋值运算符的优先级比逗号高,2+5赋给了value
位运算:直接对二进制进行操作的运算符,高效率,在c程序中1:true 0:false
说明:在计算机中,通常0代表false,1代表true;
逻辑运算符主要有:&(位与)、|(位或)、~(非)、^(异或)
& :位与(AND),两个都是1,返回1,其它返回0;
|:位或(OR),有一个为1则返回1,2个都为0返回0
^:异或,即两个比较的位数不同为1,相同为0;(0和1返回1;0和0,1和1返回0)
~:取反,单目运算符,0变成1,1变成0;
位移运算:将数值的二进制形式,左移或右移n位. 不足补0.
<<:左移变大 高位舍弃,低位补0;
>>:右移变小 低位舍弃,高位补0;
快捷算法:
3 << 2 = 3 * 2②
6 >> 2 = 6 / 2②
定点数表示法:计算机中有原码、反码、补码来表示;
原码:使用数值的最高位来代表符号位,0代表正数;1代表负数;
反码:正数的反码与原码相同;负数的反码等于除了最高位符号外,其它位取反;
补码:正数的补码与原码相同;负数的补码等于其反码+1;
示例:
1100110011 原
1011001100 反,除符号位外,其它按位取反
1011001101 补,反码+1
三、分支/循环
分支:主要用于流程控制,判断用,常见主要为if、switch结构;
if(表达式){
}else if(表达式){
}else{
}
switch(表达式/变量){
case 情况1:
语句块1;
break ;
default ;
默认情况语句块;
break;
}
三目运算符:表达式 ?结果1:结果2
循环:通常有for、while、do-while三种形式;
for(初始化;表达式;循环变量){
}
while(表达式){
}
do{
}while(表达式);
break:退出循环
continue:跳出本次循环、进入下一次
goto 标识符:无条件转移语句、可使程序立即跳转到函数内部的任意一条可执行语句.可用于退出循环.
goto show; //将转到sayhi函数中的show之后代码块执行.
void sayhi(){ show: 代码块}
注意:在c语言中1代表true,0代表false. 因此可将数字或变量作为条件表达式.
四、数组
一维数组:
数组:主要用于存储一组相同数据类型的数据集合;
声明:
int arr[5]; //可不指定长度,内存首地址就是数组名或&arr[0]
char ch[];
初始化:
ch[0] = 'a'; //逐个赋值a,ch[1]...
char ch[5] = {'a','b','c','d','e'};
int numAttr[] = {5,3,21,12,32}; //静态初始化,自动长度
字符数组:
字符数组:在c语言中,通常使用char[],或char*来存储字符串,且会在末尾追加\0作为结束标记,长度需要+1;
char str[] = "hello world"; //可采用字符串方式赋值
char str[12] = "hello world"; //若指定长度则需要12,因为还有\0
char str[11] = {'h','e','l','l','o',' ','w','o','r','l','d’}; //不是字符串,不会有\0
字符数组的输入:
scanf("%s",str); //读取字符串到数组,首地址就是数组名
字符数组的输出:
printf("%s",str); //输出字符数组内容,也可通过&str[0]
printf("%c",str[0]); //输出指定字符
二维数组:
可在声明式进行初始化:
int arr[2][3] = {{1,2,3},{4,5,6}}; //使用多个括号分割、建议使用.
int arr[2][2] = {1,2,3,4}; //2行2列、自动按照行列顺序进行排列
赋值: arr[rowIndex][colIndex] = 值;
五、函数
自定义函数
说明:是完成特点任务的代码块,将程序分为若干个模块,实现代码复用、可扩展性等.
标准做法:先提供函数原型再实现,但如果函数位于调用者之前,则可以省略声明函数;
//声明函数原型,标准做法需要void
void Eat(void);
//实现Eat函数
void Eat(){
printf("this function is eat");
}
形参/实参:
形参:形式上的参数、没有值、声明.由外界传入实参赋给形参. 如果不需要参数在函数体内被修改,可加上const修饰.
实参:有实际值的参数、传入方法、赋给形参.
值传递/引用传递:
值传递:对于基本数据类型int double等,传参时会将值赋值一份传入. 因此在函数体无论如何修改,都不会影响到原值.
引用传递:如数组等作为参数,则传递的是地址,因此函数体内进行的操作,会对原来数组产生影响.
返回值:函数返回给外界使用的一个值.
局部变量:作用域为定义它的代码块内部. 即{};
全局变量:不属于函数、而属于整个源文件. 如果外部文件需要引用则需要extern修饰
内部函数:指函数只在其所在源文件中使用, 又称为静态函数.
static 返回值 函数名 ([参数列表]); //加上此关键字即可.
外部函数:可被外部文件调用的函数.
extern 返回值 函数名([参数列表]); //默认的、因此可省略extern关键字.
递归:函数自己调用本身. 注意每次调用完本身后会返回接着执行;
常见系统函数
通常引用c函数库的方式有三种:
1. 自动访问,常见的函数
2. #include文件包含
3. 库包含如.so、.dll程序集等
math.h(数学类):
int abs(int i); long labs(long l); double fabs(double d);
sin(double d); cos(double d); tan(double d);
ctype.h(字符类):
int isalpha(int ch); //检测是否为字母. 不是返回0false、是返回非0-》true
int isdigit(int ch); //检测是否为数字、是则返回非0。不是返回0
int isalnum(int ch); //检测是否为字母或数字.
int islower(int ch); //小写字母
int isupper(int ch); //大写字母
int tolower(int ch); //转小写
int toupper(int ch); //转大写
bool isspace(int ch); //是否为空白字符
stdlib.h(系统类):
exit(1); //用于终止程序
string.h(字符串处理类):
char *strcpy(char* p1,char* p2); //将p2复制到p1指向的位置(前面). 返回值是p1;
char * strcat(char* p1,char*p2); //将p2追加到p1字符串后面。返回p1
//两个字符串从首字母按照编码表开始比较. 0:相等 正数:p1>p2 负数:p1<p2
int strcmp(char* p1,char* p2);
char *strupr(char* str); //转大写
char *strlwr(char* str); //转小写
int sizeOf(void * str); //获取空间(数组,数据类型,指针等)的字节大小.
int strlen(char* str); //获取字符串长度int len = strlen(“hello world”);
输入/输出函数
简介:主要用于接收用户输入、并返回运算结果的函数
常见转义符:在c、java、php中皆以\反斜杠为转义符,在.net中通常使用@符号;
\n:换行 \t:制表位 \b:退格 \r:回车
\\:反斜杠 \’:单引号
\a:警报
字符类:
int putchar(int ch); //输出单个字符并将其返回;
int getchar(); //从输入队列中获取一个字符. 可读取空白字符;
int puts(char * str); //输出字符串并自动换行
char* gets(char * str); //从键盘录入一行字符串,返回读取后的字符指针.
格式化类:
- int printf(格式控制,输出列表); //返回输出的字符个数.
- int scanf(格式控制,地址列表); //成功返回读入的字段个数. 错误则返回0.
注意:
1,格式说明符和参数类型必须正确,否则会造成数据错乱
2,为避免内存溢出,scaf将自动忽略空白字符,并用其分隔相邻的输入项,因此存储字符串时建议使用gets函数;
常见格式说明符:
%d:十进制 %0:八进制 %x:十六进制
%hd:短整型 %ld:长整型 %hu:无符号短整型
%f:单/双精度 %lf:长双精度 %.mf:精确到小数点m位
%e:指数 %c:字符 %s:字符串
%p:指针、地址. %%:输出百分号
指定宽度,并补齐空格:
%m.nf:浮点数长度为m,并精确到小数点n位.
%ms: 长度不足m,则右对齐,左补空格,否则全部输出例:
printf("this is %10s","love");结果为[ love]
%-ms:同上,但是左对齐,右补空格
%m.ns:表示输出字符占m列、但是只取字符串左侧n个字符、且输出在右侧、左补空格例如:
printf("%10.3s","love"); 结果: lov
%-m.ns:同上左对其,右补空格.
printf("asdas" "zxczx"); //解决一行数据过多的办法. 使用2个双引号即可.
常见标志:
-:左对齐,可和格式说明符结合使用,左右补空格等. 例如:printf("%-s","isfosdjfo");
+:正数则显示+号,负数显示-号. 如:printf("%+d",32); printf("%+d",-32);
空格:正数显示空格,负数显示-; 如:printf("% d",-32);
#:转换可选形式. %0 %x 用于输出八进制或十六进制.
*:如果不事先指定字段宽度而希望是由程序来决定,可在字符宽度部分使用*来代替.
数据重定向:可在命令行使用,在unix、linux、ds下还可使用更复杂的运算符;
输入重定向:< 代表输入来自文件,而不是键盘. 作为一个字符流传入.
输出重定向:> 将其程序的输出到指定文件中. .
缓冲区:getchar、gets、printf等IO函数时,会先存储到缓冲区,当按下回车键后才对程序变为可用,也可\n强制刷新缓冲区;
缓冲分为两种:
完全缓冲:缓冲区装满时被清空,此种类型通常出现在文件输入输出中. 缓冲区大小取决于系统.
行缓冲:遇到一个换行字符时清空缓冲区(发送到目的地),例如键盘输入,按下回车键将清空.
总结:
1,作为标准i/o包下的getchar scanf函数检测到文件末时候都返回EOF
2,如果混合使用getchar和scanf,则在调用getchar之前,恰好有scanf在输入队列中留下换行符时,可能产生对程序错误的问题,需要进行判断
main函数解析
main函数是c程序的入口,其可不带参数或带2个参数. 参数个数和字符串数组. 由在古老的命令行系统中传入. 如linux dos下.执行格式:
文件名字符参数; 多个字符参数使用空格隔开
void main(intargc,char *argv[]){ //默认传入编译后可执行文件路径
printf("参数个数:%d\n",argc);
printf("参数名称:%s",*argv);
}
结果:
六、指针
指针入门
指针:就是一种变量类型,专门用于存储内存单元的地址,可通过其访问内存单元,操作数据;
语法:数据类型 *指针名;
赋值:通过&赋予地址,注意指针本身也有地址,同样通过&;
示例:
int num = 99;
int *p; //定义一个int类型的指针变量
p = # //指针变量指向了num的地址.
printf("%d ",*p); //输出值. 加上*号代表输出指针指向的值.
&:地址运算符,返回数据变量的地址.
*:指针运算符,返回指针变量指向地址的内存单元中存储的值(数据).
*p = 123; //可为指针指向的地址内存中存储的值进行操作.
*p = *p2;
&和*优先级相同,因此从右往左执行:
printf("%d ",&*p); //*获得指针p指向的值,在通过&获得指向值的地址.
指针自增自减运算:指向下一个内存单元,对于数组而已,会指向下一个元素地址,int(4字节)、short(2字节)、short(直接输出值,而不是地址)
指针和数组
简介:数组本质就是一个连续的存储空间,且数组名称就是数组在内存的首地址,即arr = &arr[0],因此可对指针就行自增/自减,来完成迭代数组;
int arr[5] = {1,2,3,4,5};
int *p; //指针变量
p = arr; //获取数组的首地址,也可通过&arr[0]
for ( i = 0; i < 5; i++){
printf("%d",*(p+i)); //*p++ == *(arr+i) == *(&arr[0]+i) 皆可指向下一个内存单元
}
通过两个指针来完成迭代:
while(start <= end) 或 while(start != end)
const和指针:
1,const int* p1; //const放在前面,可改变指向的地址,但不可改变指针执行的值
*p1 = 18; //错误
*p1 = &age; //正确
2,int const* p2; //const放在后面,可改变指向值,但不可改变指针执行的地址
*p2 = 18; //正确
*p1 = &age;
//错误
3,const int* const p3 =
&age; //两个const,则即不可改变指针执行地址、也不可改变指针执行的值
指针数组:即数组中存放的元素都是指针,最常见为char字符指针数组,分别存储一个字符串
int *p_arr[3]; int (*p_arr)[3];
//通过二位字符数组,来存储字符串,不建议使用,大小固定
char ch[4][10] = {"hello","world","servlet","asp.net”};
//建议使用,每个char*来存储字符串
char *month[] = {"one","two","three","four"};
指针与字符串
字符串与指针:c语言中没有对字符串的封装,可通过字符数组、字符指针来完成;会在末尾加上\0标识;
char str[] = "hello"; //字符数组,可指定长度,用于存储字符串
char *p = "world"; //字符指针,指向字符串首个字符地址.
char * month[] = {"one","two","three","four”}; //字符指针数组
使用:两者使用基本相同,可使用下标,也可当做指针进行操纵:str[1]; p[1]; str++; p++;
函数指针
函数指针:通常作为参数使用,并作为回调函数,需提供函数原型、函数名为首地址;
//1,声明函数原型
void sayHi(string str);
//2,函数指针,将函数名赋予即可,格式:返回值 (*指针名) (参数类型) = 函数名;
void (*p) (string) = sayHi;
//3,调用
p(参数) == (*p) (参数);
通常是作为参数,并进回调,格式如下:
void show(void (*p) (string));
函数指针数组:
void(*p_arr[5]) (string);
(*p_arr[index]) (arg);
示例
示例1:指向指针的指针:
int num = 0;
int* p1 = #
int** p2 = &p1; //指向一个指针
示例2 - 指针变量做函数参数-交换值:
void swap(int *a,int *b){
int temp = *a; //交换值操作
*a = *b; //*:指针指向地址中存放的数据. 不是操作地址.
*b = temp;
}
void main(){
int x = 80,y = 90;
int *p_x = &x, *p_y = &y; //两个指针分别指向x y的地址
swap(p_x,p_y); //传入指针、内部对指向地址的数据进行了交换. 直接影响到了变量 x y
printf("x = %d\n",x); //输出
printf("y = %d\n",y);
}
示例3 - 指针完成冒泡排序:
//通过指针、实现冒泡排序;指针p代表数组的首地址
void orderby(int *p,intlen){
int i,j,temp;
for ( i = 0; i < len-1; i++){
for ( j = 0; j < len - i -1; j++){
if(*(p+j) > *(p+j+1)){ //交换值操作、*代表指针地址对应的数据
temp = *(p+j);
*(p+j) = *(p+j+1);
*(p+j+1) = temp;
}
}
}
for ( i = 0; i < len; i++){
printf("%d ",*(p+i)); //打印、从首地址开始.
}
}
void main(){
int arr[10] = {5,3,12,54,23,43,15,32,54,67};
int *p;
p = arr; //将首地址赋给指针。等价于:p = &arr[0];
orderby(p,10); //排序
}
七、结构体、共用体、枚举
结构体
结构体:构造类型,用于存储一组不同数据类型的结构;(可理解面向对象中,为轻量级的类)
语法:
struct MyDate{ //定义一个结构
int year;
int month;
int day;
}
struct MyDate d1; //创建一个结构变量,并使用
d1.year = 2010;
特殊语法:
1,可在声明结构体时,创建结构体变量
struct MyDate{
int year;
int month;
int day;
} d1,d2,d3
2,结构体变量的初始化
struct MyDate d1 = {2010,10,10};
d1.year = 2010;
3,结构体也可作为数组,例如
struct MyDate days[3] = {{2010,10,10},{2010,10,10},{2010,10,10}};
day3[0].year; //访问方式
结构体指针:
struct MyDate today; //创建一个结构变量,并赋值
today.year = 2010;
today.month = 10;
today.day = 1;
struct MyDate *p = &today; //创建一个结构指针,并通过&指向today
p->day; //通过->指针运算符直接调用
(*p).day; //通过*寻址运算符,再通过.调用
共用体
说明:使用类似于struct,其关键字为union,为所有共用体变量所共享,当某个共用体变量的成员改变,其它的成员也会改变;
union Person{
int id; //注意共用体中成员数据是相同的. 二个进制位
char name;
};
void main(){
union Person person = {97}; //创建共用体变量,并初始化为id赋值97
printf("%c ",person.name); //输出a,共用体内成员数据共享的. name == id == 97;
person.name='A'; //将name改成’A’,则id也会变成’A’的ascii码
4
printf("%d ",person.id); //输出65
}
枚举
enum:用于存放一组相关的数据,且每个标识符都对应一个整数,称为枚举常量,使用与struct、union类似,需定义枚举变量
//定义一个枚举
enum Colors{
Red,Green,Blue
} c1,c2;
//枚举常量
enum Colors{
Red=1,Green=2,Blue=3
};
enum Colors c = Red; //定义一个枚举变量,其值只能是Red、Green、Blue
位段:特殊的结构类型,用于一些特殊场合,其所有成员的长度均是以二进制位为单元定义的,结构中的成员称为位段.
struct CpuStatus{
unsigned sign:1; //符号标志
unsigned zero:1; //零标志
unsigned carry:1; //进位
};
八、预处理
简介:C程序在编译前会先经过一个预处理的过程,用于#inclde包含文件,宏替换,特殊符号的处理等,再变成一个.i文件,预处理指令以#号开头;
#include(包含指令)
简介:包含指令,用于包含另外一个源文件的内容;
< >:系统到存放c函数库头文件所在的目录中查找,标准方式.
" ":先在用户指定目录中寻找,若找不到,再到存放c函数库头文件所在的目录中查找.
.h头文件:本质就是.c源程序,通常存储宏定义、函数原型、常量、typeof、结构、枚举、外部函数等. 之后在其它程序通过#include指令引入使用即可.
#define(宏定义)
简介:宏定义,即字符串常量,仅作替换,不分配内存空间;
语法: #define 宏名[(参数列表)] 字符串; //可使用”” ‘’
#define PI 3.141592; //不带参数的宏
.
#define SUM(a,b) (a+b); //定义一个带参数的宏,本质就是一个函数
int x = 10 ,y = 20;
printf("%d \n",SUM(x,y))
#undef PI; //终止宏定义
ANSI标准说明了5个预定义宏替换名:
_LINE_:当前被编译代码的行号
_FILE_:当前源程序的文件名称
_DATE_:当前源程序的创建日期
_TIME_:创建时间
_STDC_:判断当前编译器是否为标准c. 1 = 符合标准c;
#if、#elif、#else(条件编译)
条件编译:当某些代码在满足条件下,才进行编译,否则跳过;
#if 表达式1 //此种类似于if else if结构.
代码块
#elif 表达式2
代码块
#else
代码块
#endif
#ifdef 宏替换名 //如果定义此宏、则编译。否则不编译
代码块.
#endif
#line #error
#line:用于重置由__LINE__和__FILE__报告的行号和文件名.
#line 1000; //将当前行号设置为1000
#iine 10 “cool.c”; //将行号重置为10,文件名称重置为cool.c
#error:使预处理发出一个错误消息(异常),之后编译过程中断.
#error “不支持c99标准”;
#pragma
#pragma 参数; //设定编译器状态,完成一些特定操作;
#pragmacomment(lib,"ws2_32.lib") //在socket编程中需要ws2_32.dll程序集.
常见参数:message,能够在编译信息输出窗口中输出相应的信息
Code_Seg:设置程序中函数代码存放的代码段
Once:保证头文件被编译一次
Comment:可导入程序集,动态链接库德国.
九、文件操作
FILE
标准I/O包:stdio.h头文件,提供了大量函数用于输入/输出
简介:C程序自动打开3个文件,标准输入,标准输出,标准错误输出.
FILE:结构体变量,包含了文件名,状态,位置,大小等信息,通常使用此指针来描述文件信息
typedef struct {
short level; /* fill/empty level of buffer */
unsigned flags; /* File status flags */
char fd; /* File descriptor */
unsigned char hold; /* Ungetc char if no buffer */
short bsize; /* Buffer size */
unsigned char *buffer; /* Data transfer buffer */
unsigned char *curp; /* Current active pointer */
unsigned istemp; /* Temporary file indicator */
short token; /* Used for validity checking */
} FILE; /* This is the FILE object */
FILE * fp; //文件指针指向了FILE(结构体变量),可通过其操作文件.
文件的打开模式:
EOF:通常用于判断文件末尾,其值为-1;#define定义
打开/关闭文件
FILE * fp = fopen("c:\\a.txt","r”); //成功返回FILE指针,失败返回NULL
fclose(文件指针); //关闭成功返回0,失败返回EOF.
文件的读写:
字符:
fputc(int i,FILE *fp); //写入字符,成功返回写入字符数,失败返回EOF
fgetc(FILE *fp); //读取一个字符,当读到末尾时返回结束标志EOF
getc(FILE *fp); //也可实现从指定文件读取一个字符、末尾返回EOF
字符串:
fputs(char * str,FILE *fp); //写入字符串,成功返回写入的字符数,失败返回EOF
fgets(char * str,int len,FILE *fp); //读取len长度的字符串,到str中. 末尾返回EOF
格式化读写文件
fprintf(文件指针,格式字符串,输出列表);
fprintf(fp,"%d",i); //将整型变量i,输出到fp指向文件中.
fscanf(文件指针,格式字符串,输入列表);
fscanf(fp,"%d",&i); //从fp中读取一个字符,存到变量i
//从fp指向文件读入count次,每次读size字节,存储到buffer地址中.
fread(buffer,size,count,fp);
//将buffer开始写入count次,每次写size个字节到fp所指文件中.
fwrite(buffer,size,count,fp);
参数说明:
buffer:一个指针,输出/输入地址
size:每次读取字节数
count:读取多少次
fp:文件指针
示例-读取:
int i;
char arr[20];
FILE * fp = fopen("E:\\遇见未知的自己.txt","r++");
if(fp != NULL){
for (i = 0; i < 20; i++){
fread(&arr[i],1,20,fp);//从fp指向文件中读取20次,每次读取1个字节存到字符数组
}
fclose(fp); //关闭资源
}
printf("%s",arr);
示例-写入:
for ( i = 0; i < 20; i++){
fwrite(&arr[i],20,1,fp); //从fp指向文件中写入20次,每次将arr数组的数据,写入1个字节.
}
文件定位
fseek(文件指针,位移量,起始点); //0:文件头 1:当前位置 2:文件末
示例:
char * str = "hello world";
fseek(fp,5l,0); //将文件指针指向距文件头后5个字节的位置. 负数则之前.
fgets(str,sizeof(str),fp); //再次进行读取字符串
结果:world
int rewind(文件类型指针); //使位置指针重新指向文件头
long ftell(文件类型指针); //得到流式文件中的当前位置,相对于文件头的位移量. 当返回-1l则出错
对于文件复制,可有多种方式如fread fwrite fseek等皆可.
十、存储管理
存储管理
变量的存储类别:从变量的产生时间可分为静态存储和动态存储.
静态存储:指程序运行前分配的固定存储方式.
动态存储:指程序运行期间根据需要动态的分配存储空间.
可通过存储修饰符来指定存储类型变量:
- auto: 修饰一个局部变量为自动,默认的,每次只需到定义变量的代码行时,会产生并初始化
- register:存储在计算器的某个硬件寄存器而不是内存,可提高程序的运行速度,程序员无法获取寄存器变量的地址,不常用;
- static:在程序运行期间,其值始终有效,但是限定该全局变量只能在定义它的文件中使用;
- extern:外部存储变量、声明此变量由外部定义的,由于一个c程序可能有多个文件、因此可使用extern来提供给其它文件使用.
extern int numExtern = 888; //文件1
//文件2
#include<stdio.h>
int main(){
extern int numExtern; //使用extern来引用外部变量
printf("%d\n",numExtern); //此处将输出888
}
简介:static和extern也可作用于函数,且注意c语言中全局变量的定义是有顺序关系的;
外部函数:当前文件的函数允许被其它文件的函数访问、调用,即称为外部函数,默认就是extern
静态函数:又称内部函数,使用static修饰之后,则可定义该函数的文件中使用;
程序通常要先装载到计算机的内核与半导体内存中,再运行程序. 程序被组织成4个逻辑段.
1,可执行代码和静态数据:被存储在固定的内存位置
2. 动态数据(堆):程序请求动态分配的内存来自内存池,也就是堆.
3. 栈:程序不会像堆那样动态分配内存,当程序调用函数,或声明变量时,系统会自动分配内存. 是一个先进后出的数据结构;
类型限定词:const
volatile:告诉编译器该变量除可被程序改变之外,还可被其它代理改变,如硬件等.
restrict:只可用于指针,表明指针是访问一个数据对象的唯一方式.
动态内存分配
动态内存分配:malloc、calloc、free,需引入 <stdlib.h> ,返回值为 void * 代表通用指针
//分配一个size大小的空间,返回该内存地址,可赋予指针
void * malloc(unsignedint size);
//重新改变一个指针的内存大小,并返回该指针(本质是先释放、再分配)
void * malloc(void *p , unsignedint size);
//动态分配n个长度为size的连续内存空间数组,并返回指针,为数组的首地址,错误返回NULL
void * calloc(unsigned n,unsigned size);
//释放由指针p指向的内存空间,使其内存区域可用,是通过calloc或maloc函数时返回的值
void free(void *p);
sizeof:返回类型的大小,例如int占4个字节
size (int);
size (num);
示例:
void main(){
int * pArray;
int i;
pArray = (int *) calloc(3,sizeof(int)); //分配3个连续存储的int空间,并且指向首地址
for ( i = 1; i < 4; i++){
*pArray = 10*i; //使用指针对数组赋值
printf("%d == %d \n",i,*pArray); //输出
pArray++; //移动指针指向下一个存储的数据的位置
}
}
内存泄露:在使用了malloc、calloc等函数分配内存后,必须使用free函数进行释放,否则可能会因为内存没释放而造成内存泄露,从而导致系统奔溃.
浙公网安备 33010602011771号