目录
条件编译 #if #ifdef #else #elif #endif
预备知识
体系结构
运算器ALU:进行算术运算和逻辑运算。
控制器Controller:取指令,分析指令,控制其他部件。
存储器Memory:放程序和数据(放在两个不同的地方)。分内存和外存。
输入设备Input Device
输出设备Output Device
进位与数制
十进制:最熟悉。
二进制:基本的0和1。
二进制位运算:与AND(&),或OR(|),非NOT(~),异或XOR(^)。
八进制:可用三位二进制表示。
十六进制:可用四位二进制表示。
十进制向二进制转换:整数部分除二取余,小数部分乘二取整。
原码与补码:原码是第一位为符号位,正数为0,负数为1,其余位为数值部分。
补码是第一位为符号位,正数为0,负数为1,正数的补码为原码,负数的补码为符号位不变,数值位取反加一。
补码的运算:[N1+N2]补=[N1]补+[N2]补,[N1-N2]补=[N1]补+[-N2]补
字符表示:ASCII码。
基本数据类型、运算符与表达式
数据类型
基本数据类型:
整型:整型(int)4字节,短整型(short)2字节,长整型(long)4字节。
实型:
单精度型(float)4字节,精确表示7~8个数,符号位占1位,阶码部分占8位,其余为尾数部分。
双精度(double)8字节,精确表示16~17个数,符号位占1位,阶码部分占11位,其余为尾数部分。
长双精度(long double)10字节,精确表示17~18个数,符号位占1位,阶码部分占15位,其余为尾数部分。
字符类型:(char)1字节。使用时要用单引号(‘’)括起来,特殊字符显示要使用转义(\)字符。字符串:用数组存放每一个字符,操作数组来实现字符串,数组里的最后一个字节里面放的是‘\0’。
构造数据类型:数组,结构体(struct),联合体(union),位域,枚举(enum)。
指针类型
空类型:(void)。
定义类型:(typedef)。
运算符和表达式:
运算符:单目,只带一个操作数,如(--),(++),(~)。
双目,带两个操作数,如(+),(-),(*),(/),(%),(&),(^)(|),(<<),(>>)。
三目,带三个操作数,如(?)。
赋值运算符和表达式:(=),将右边的数值给左边。
自动类型转换:
无符号短长度到长长度:段长度作为长长度的地位部分,高位自动补零。
有符号短长度到长长度:段长度作为长长度的地位部分,符号位向高位自动扩展。
强制类型转换:(类型说明符)(表达式),如(char)(x+y),把x+y的值强制转化为char类型。
算术运算符和表达式:加(+),减(-),乘(*),除(/),取余(%)。
自增自减运算符:前置:先增减再运算。后置:先运算再增减。
位运算:按位与(&),按位或(|),按位取反(~),按位异或(^),左移(<<),右移(>>)。
逗号:运算级别最低,用在for循环中。
sizeof,复合赋值运算符号:
sizeof获取变量或数据类型所占的内存大小(字节数)。
复合运算符:(+=),(-=),(*=),(/=),(%=),(&=),(|=),(^=),(<<=),(>>=)。含义为
exp1 op= exp2 等价于 exp1=exp1 op exp2
输出
运算优先级:括号()>逻辑非!,取反~,自增++,自减--,负号-,sizeof(类型) > 算术运算:*,/,%,+,-,>移位:<<,>>,>关系运算:>, >=, <, <=,==,!= >按位逻辑运算:&,^,| >逻辑运算:&&,|| > 条件运算 ? : >赋值运算 ,复合运算:=,+=,-=,*=,/=,%=,&=,|=,^=,<<=,>>= > 逗号运算,。
(~)>(<<、>>)>(&)>(^)>(|)。
输入输出
输出
格式化输出printf:
形式为printf("格式控制字符串",表达式1,表达式2,表达式3);
常规字符:可显示字符和转义字符。
格式控制符:以%开头一个或多个字符,说明数据的类型、形式、长度、小数位数等,在格式化输出中想要显示%,需要用%%替换单个%。
关于%Type:%d或%i 以十进制输出有符号整型数据,
%x,%X 以十六进制输出无符号整型数据,
%o 以八进制输出一个无符号整数数据,
%u 以十进制数输出一个无符号整型数据,
%c 输出一个字符型数据,
%s 输出一个字符串,
%f 以十进制小数形式输出一个浮点型数据,
%e,%E 以指数形式输出一个浮点型数据,
%g,%G 按照%f或%e中输出宽度比较短的一种格式输出,
%p 显示变量的地址。
一般形式为:%[flag][width][.precision][size]Type
有符号整数一般形式:%[-][+][width][.precision][l][h]d - 表示左对齐(缺省为默认),+ 输出正数,width 输出整数的最小域宽,.precision 表示至少输出precision位,l 表示输出长整型数据,h表示输出短整型数据。
无符号整数的一般形式:%[-][#][0][width][.precision][l][h] u|o|x|X | 为互斥关系,#表示输出%o时,在数字前加0,输出%x或%X时,在数字前加0x或0X。
实数的输出一般形式:%[-][+][#][0][width][.precision][l|L] f|e|E|g|G # 表示必须输出小数点,.precison 表示规定输出实数时,小数部分的位数,l 表示输出double型数据,L 表示输出long double型数据。
字符和字符串输出的一般形式:字符:%[-][0][width]c,
字符串:%[-][0][width][.precision]s .precision表示只输出字符串的前precision字符。
非格式化输出:
int putchar(int c); //应包含头文件stdio.h,显示c所表示的字符,正常返回字符的代码值,出错返回EOF(-1)。
putchar(65); //显示字符A
putchar('a'); //显示字符a
int putc(int c,FILE *stream); //应包含头文件stdio.h,*stream是文件流指针,将c输出到流文件stream。
putc(c,stdout) 与 putchar(c) 等价。
int puts(char *string); //应包含头文件stdio.h,将形参string所代表的字符串输出到屏幕并自动回车换行。
char a[]="I am fine";
puts(a);
输入
格式化输入scanf:
一般格式为:scanf("格式控制字符串",变量1的地址,变量2的地址,..,变脸n的地址);
一般形式:%[*][width] [l|h]Type width 指定输入数据的位宽 遇到空格或不可转换字符结束,* 抑制符 输入的数据不会赋值给相应的变量,l 用于d、u、o、x|X前 指定输入为long型整数 用于e|E、f前,指定输入为double型实数,h 用于d、u、o、x|X前 指定输入为short型整数。
一般来说,scanf和printf的区别在于,输入的变量需要取得此变量的地址,使用取地址运算符&,这个变量对应具体的内存单元,因此是有地址的,而表达式没有地址,只有值。
非格式输入:
int getchar(void); //应包含头文件stdio.h,回车后回显
int getc(FILE *stream); //应包含头文件stdio.h,*stream是文件流指针
getc(stdin) 与 getchar() 等价。
int getche(void); //应包含头文件conio.h,不回车就回显
int getch(void); //应包含头文件conio.h,不回车不回显
结构设计
顺序结构:
逐条语句书写,逐条语句执行。
选择结构
语句:
表达式:表达式;
函数调用语句:函数名(实际参数表);
复合语句:多条语句用{}括起来,在程序中作为单条语句执行。
空语句:只有;组成的语句。
控制语句:条件判断语句:if,switch
循环执行语句:do while,while,for
转向语句:break,goto,continue,return
运算符:
关系运算符和表达式:>, >=, <, <=,==,!=
逻辑运算符和表达式:!,&&,|| (!具有右结合性,&&,||具有左结合性)
条件运算符和表达式:表达式1 ? 表达式2 : 表达式3
选择结构:
if(表达式)
语句;
if(表达式)
语句1;
else
语句2;
if(表达式1)
语句1;
else if(表达式2)
语句2;
else if(表达式3)
语句3;
...
[else
语句n;]
switch(表达式)
{
case 常量表达式C1: 语句组1;
break;
case 常量表达式C2: 语句组2;
break;
...
case 常量表达式Cn: 语句组n;
break;
[default: 语句组;
break;]
}
循环结构
while(表达式)
语句;
do
语句;
while(表达式);
for(表达式1;表达式2;表达式3)
语句;
break; //程序从此停止,跳出循环
continue; // 程序从此停止,继续下一次循环
goto //无条件转移语句
goto语句标号;
语句标号:
或 语句标号:
goto语句标号;
exit() //终止程序,返回操作系统
void exit(int status); //应包含头文件<stdlib.h>,status为0
//或宏常量EXIT_SUCCESS时为正常退出,为非0或为常量EXIT_FAILURE时为出现错误后退出
数组
定义形式:
[存储类型符] 数据类型符 数组变量名[整型常量表达式];
[存储类型符] 数据类型符 数组变量名[整型常量表达式1][整型常量表达式2];
存储类型:auto(自动型), register(寄存器型) ,extern(外部型) ,static(静态型)
数据类型符:int(整型),short/short int (短整型),long/long int(长整型),float(单精度),double(双精度),char(字符型),struct(结构体),union(联合域),位域,enum(枚举),*(指针)。
一维数组:数据单元格式:数组变量名[下标]
赋值:数据类型符 数组变量名[常量表达式] = {表达式1,表达式2,...,表达式n};
int i;
int b[11]={1,2,3,4,5,6,7,8,9,10,11};
for(i=0;i<11;i++)
printf("%d",b[i]);
printf("\n");
int a[]={1,2,3,4,5,6,7,8,9,10,11};
for(i=0;i<sizeof(a)/sizeof(a[0]);i++) //获取数组的大小
printf("%d",a[i]);
printf("\n");
memset(a,0,sizeof(a)); //需要包含头文件string.h,设置数组a的值全为0
for(i=0;i<sizeof(a)/sizeof(a[0]);i++)
printf("%d",a[i]);
printf("\n");
int c[11];
memcpy(c,b,sizeof(b)); //需要包含头文件string.h,复制数组b的值给数组c
for(i=0;i<sizeof(c)/sizeof(c[0]);i++)
printf("%d",c[i]);
printf("\n");
二维数组:数据单元格式:数组变量名[下标1][下标2]
赋值:
分行初始化:
数据类型 数组变量名[行常量表达式][列常量表达式] = {{第0行初值表},{第1行初值表},...,{最后一行初值表}};
按元素在内存中的排列顺序初始化赋值:
数据类型 数据变量名[行常量表达式][列常量表达式]={初值表};
int i,j;
int a[2][3]={{1,2,3},{4,5,6}};
for(i=0;i<2;i++)
for(j=0;j<3;j++)
{
printf("%d",a[i][j]);
}
printf("\n");
int b[2][3]={1,2,3,4,5,6};
for(i=0;i<2;i++)
for(j=0;j<3;j++) //打印数组b的所有值
{
printf("%d",b[i][j]);
}
printf("\n");
for(j=0;j<3;j++) //把数组b第一行的值打印出来
{
printf("%d",b[0][j]);
}
printf("\n");
for(j=0;j<3;j++) //把数组b的第二行的值打印出来
{
printf("%d",b[1][j]);
}
printf("\n");
一维数组转换成二维数组:
#include <stdio.h>
#include <string.h>
int main() {
unsigned short i,j,k;
unsigned char images[]={0x93,0x93,0x93,0x93,0x93,0x93,0x93,0xC7,0x33,0x01,0x01,0x01,0x01,0x03,0x87,0xCF,
0x87,0x86,0xCE,0xCE,0xCE,0xCF,0x87,0x87,};
unsigned char image[17][8];
for(j=0;j<17;j++)
{
for(i=0;i<8;i++)
{
k=j+i;
image[j][i]=images[k];
}
}
for(j=0;j<17;j++)
{
printf("{");
for(i=0;i<8;i++)
{
printf("%#x,",image[j][i]);
}
printf("},");
printf("\n");
}
return 0;
}
字符串:
字符串与数组:
char str[]="china";
printf("%d\n",sizeof(str)); //outcome is 6, that's because there has '\0' at the end of string
char str1[]={'c','h','i','n','a'};
printf("%d\n",sizeof(str1)); //outcome is 5, there has no '\0' at the end
输入和显示字符串 :
char str2[10]="china";
printf("%d\n",sizeof(str2)); //size is 10 , with no '\0',equal to str2[10]={'c','h','i','n','a'};
puts(str2); //show "china"
puts("china"); //show "china" directly
int i;
char str3[10];
gets(str3); //get str3 form the keyboard, the space won't separate each word
printf("%d\n",sizeof(str3)); //size is 10, of course
for (i=0;i<10;i++)
printf("%c",str3[i]);
printf("\n");
int i;
char str4[20],str5[20],str6[20];
scanf("%1s%2s%5s",str4,str5,str6); //the space will separate each string
puts(str4); //print str4 with Enter
puts(str5);
puts(str6);
printf("%1s %2s %-5s",str4,str5,str6); //output str4,str5,str6 values, -5 means the output value's length is 5 and is align left
字符串函数:
char str[]="china";
printf("%d\n",strlen(str));
char str1[]="123456\0457"; //"\045" is a transferred symbol
printf("%d\n",strlen(str1));
char str2[20];
strncpy(str2,str1,3); //should include string.h, copy str1 to str2, length is 3
printf("%s\n",str2);
int v1,v2,v3,v4;
v1=strcmp("abc","ABC"); //compare string symbol's ascii one by one, the value of v1 is 1
v2=strcmp("1234","12345"); //the value of v2 is -1
v3=strcmp("hello","hello"); //the value of v3 is 0
printf("%d, %d, %d\n",v1,v2,v3);
v1=strcasecmp("abc","ABC"); //ignore case, in windows, it's stricmp or stricmpi, but in linux it's strcasecmp
printf("%d\n",v1);
v1=strncmp("123456","1234",4); //the value of v1 is 4-4=0
v2=strncmp("123456","12342",5); //the value of v2 is 5-2=3
v3=strncmp("123458","12347",5); //the value of v3 is 5-7=-2
printf("%d, %d, %d\n",v1,v2,v3);
char str3[20]="1234",str4[]="567";
strcat(str3,str4); //connect str4 to str3, the str3 should be big enough
printf("%s\n",str3);
其他函数:
函数的用法 | 函数功能 | 应包含的头文件 |
strset(字符数组,字符) | 将字符数组中的所有字符都设为指定字符 | string.h |
strlwr(字符数组) | 将字符数组中的所有字符都转换为小写字符 | string.h |
strupr(字符数组) | 将字符数组中的所有字符都转换为大写字符 | string.h |
toupper(字符) | 将小写字符转换成大写字符 | ctype.h |
tolower(字符) | 将大写字符转换成小写字符 | ctype.h |
atoi(字符串) | 将字符串转换为整数 | stdlib.h |
atol(字符串) | 将字符串转换成长整型 | stdlib.h |
atof(字符串) | 将字符串装换成浮点数 | stdlib.h |
ultoa(无符号长整数,字符数组,进制) | 将无符号长整数转换成指定的进制数并以字符串的形式存放到字符数组中 | stdlib.h |
字符串数组:
[存储类型符] char 字符串数组名[行数m][列数n]={字符串1,字符串2,...,字符串m};
例如:
char city[][10]={"Beijing","Shanghai","Tianjing","Guangzhou","Wuhan"}; //each string has '\0' at the end
函数
函数是C语言的基本模块,通过对函数模块的调用实现特定的功能。函数包含有标准库函数和用户自定义函数。
函数的定义:void 函数名(void){} 或 void 函数名(){} //无参无返回值
void 函数名(类型1 形参名1, 类型符2 形参名2, ... , 类型符n 形参名n){} //有参数无返回值
返回值类型符 函数名(类型1 形参名1, 类型符2 形参名2, ... , 类型符n 形参名n){} //有返回值有参数,若返回值省略,默认为int型
函数的声明:void 函数名(void); 或 void 函数名(); //无参,函数放在前面不需要原型声明
返回值类型符 函数名(void); 或 返回值类型符 函数名();
void 函数名(类型1 形参名1, 类型符2 形参名2, ... , 类型符n 形参名n); 或 void 函数名(类型符1,类型符2,...,类型符n);
返回值类型符 函数名(类型1 形参名1, 类型符2 形参名2, ... , 类型符n 形参名n); 或 返回值类型符 函数名(类型符1,类型符2,...,类型符n);
函数的调用:函数名(); 或 变量=函数名(); //无参
函数名(实参列表); //有参,实参必须与形参数量相同、类型相符
函数的返回:return (表达式); 或 return 表达式; //有返回值 return; //无返回值
函数参数的传递:值传递和地址传递
值传递:实参的值复制到形参,为单向传递。
地址传递:将实际数据的存储地址传递给形参,形参与实参占用相同的内存单元,数据双向传递。
变量的作用域与生存期:局部变量:函数中的变量;全局变量:函数外部的变量。
变量的存储类型:静态:存储单元固定;动态:存储空间动态分配和释放
四种类型:auto (自动)(可省略)(局部变量),register (寄存器型),动态,程序结束后变量消失。
extern (外部型)(全局变量),static (静态型)(局部变量),静态,程序结束后变量依然存在,但不可使用;加了staticd的静态全局变量,不能通过extern说明来使用。
函数的作用域:内部函数:static修饰,只能在本文件中调用,外部函数:extern修饰,在整个源程序中都有效。
指针
指针:内存单元的存储地址,是常量。
指针变量:指向内存单元的起始地址,是变量。
指针的变量和引用:
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a=20, *p, *q;
p=&a;
q=p; //p与q都指向a
// int a=20;
// int *p=&a;
printf("%d\n",a); //显示a的值
printf("%d\n",p); //显示a的地址
printf("%d\n",*p); //显示a的值
printf("%d\n",q); //显示a的地址
printf("%d\n",*q); //显示a的值,*为间接引用运算符,*q相当于a本身
unsigned short b;
unsigned short *pi=&b; //pi可以访问全部的a的内存单元
char *pc=(char *)&b; //pc只能访问a的低字节单元,低地址
*pi=0xF0F0;
*pc=0;
printf("b=%x",b);
return 0;
}
零指针(空指针):int *p=NULL; //NULL在C语言中被定义为值为0的符号常量
用于避免指针变量的非法使用,在程序中作为状态比较:while (p!=NULL){...}
void *类型指针:void *p;
char *p1;
void *p2;
p1=(char *)p2;
p2=(void *)p1;
两个数比较大小:
int a,b;
printf("please input a and b\n");
scanf("%d %d",&a,&b);
printf("a=%d,b=%d\n",a,b);
if(a>b)
{
printf("the max is a, the min is b\n");
}else
{
printf("the max is b, the min is a\n");
}
int *p,*p1,*p2;
p1=&a;
p2=&b;
if(a<b)
{
p=p1; p1=p2; p2=p; //把大的值给指针p1
}
printf("max = %d, min = %d\n",*p1, *p2);
指针变量的加减运算:p +/- n = ADDR +/- n*sizeof(ptype) //ADDR为p当前的值
指针变量的关系运算:p1 == p2, p1与p2指向同一地址单元,p1 > p2, p1处于高地址
数组的指针:数组名就是一个指向这个数组的指针:a[k] = k; 等价于 *(a+k) = k;
指向数组的指针:int a[10], *p=a; 或 (*p = &a[0];) 或 int a[10], *p; p=a; 或 (p=&a[0];)
对数组的赋值:
#include <stdio.h>
void main()
{
char str[10];
int k;
for (k = 0; k < 10; k++)
{
str[k] = 'A' + k; //method 1
printf("%c", str[k]);
}
printf("\n");
char* p;
p = str;
for (k = 0; k < 10; k++)
{
//p[k] = 'A' + k; //method 2
*p++ = 'A' + k; //method 3
//printf("%c", p[k]);
}
p = str; //给数组的首地址给p,防止p指针造成数组越界
for (k = 0; k < 10; k++)
printf("%c", *(p + k)); //输出str数组
printf("\n");
}
访问多维数组:a[i][j] 等价于 *(a[i]+j) 等价于 *(*(a+i)+j) 等价于 (*(a+i))[j]
行指针:int (*p)[4]; p=a;或(p=&a[0]) p[i][j] 等价于 *(p[i]+j) 等价于 *(*(p+i)+j) 等价于 (*(p+i))[j]
列指针:int *p; p=a[0];或(&a[0][0])
#include <stdio.h>
int main() {
short int a[2][3]={{1,2,3},{4,5,6}};
short int i,j,*p;
p=&a[0][0]; //列指针,指向第一行的第一列
for(i=0;i<2;i++)
{
for(j=0;j<3;j++)
printf("%d",*(p+i*3+j));
printf("\n");
}
return 0;
}
指针数组:里面的每个元素都是指针 int *p[20],p是数组名,不是指针变量,不可赋值
#include <stdio.h>
int main() {
int i,j,t;
int a,b,c,d,e;
int *p[5]={&a,&b,&c,&d,&e};
scanf("%d,%d,%d,%d,%d",p[0],p[1],p[2],p[3],p[4]);
/*冒泡法排序*/
for(i=0;i<4;i++)
for(j=i+1;j<5;j++)
if(*p[i]>*p[j])
{
t=*p[i];
*p[i]=*p[j];
*p[j]=t;
}
for(i=0;i<5;i++)
printf("%d ",*p[i]);
return 0;
}
指针与字符串:字符串本身就是以‘\0’为结尾的字符型数组
指针变量只有与内存建立联系后才可以使用,若一个指针没有指向一个有效的内存就被引用,称这个指针为野指针,容易导致程序崩溃。
char str[]="I love China!";
//char *pstr="I love China!";
char *pstr;
pstr="I love China!"; //或 pstr=str;
for(;*pstr != '\0';pstr++) //逐个字符引用
printf("%c",*pstr);
pstr="I love China!"; //将该字符串的首地址给pstr
printf("\n");
printf("%s",pstr); //整体引用
利用字符串数组对一组城市名进行升序排列:
本质是操作字符指针,改变下标即可改变指针,即可改变字符串数组
int i,j,k;
char *pcity[]={"Wuhan","Guangzou","Beijing","Shanghai","Tianjing",""};
char *ptemp;
for(i=0;strcmp(pcity[i],"")!=0;i++) //从第一个字符串到最后,冒泡法排序
{
k=i; //假设最小下标为k,初始为i
for(j=i+1;strcmp(pcity[j],"")!=0;j++) //此刻的字符串与之后的字符串进行比较
if(strcmp(pcity[k],pcity[j])>0) //如果前面的字符串比后面的大
k=j; //更新最小字符串下标
if(k!=i) //交换字符串位置
{
ptemp=pcity[i];
pcity[i]=pcity[k];
pcity[k]=ptemp;
}
}
for(i=0; strcmp(pcity[i],"")!=0;i++)
printf("%s ",pcity[i]);
printf("\n");
指针与动态内存分配:
静态内存分配:根据变量或数组的类型及其大小分配相应的内存单元。
动态内存分配:通过分配需要的大小合适的内存单元,指针指向内存分配首地址,完成动态分配各类型单元的数据。
函数 malloc():分配若干字节的内存空间:void *malloc(unsigned int size);
在函数内部对指针变量分配内存,形参必须是二重指针。
int n, *pscore;
scanf("%d",&n);
pscore=(int *)malloc(n*sizeof(int)); //分配n个连续的整数单元,首地址赋给pscore
if(pscore==NULL)
{
printf("Insufficient memory available!");
exit(0);
}
... //可对pscore所指单元进行其他处理
函数 calloc():给若干同一类型的数据项分配连续的内存空间:void *calloc(unsigned int num, unsigned int size);
int n, *pscore;
scanf("%d",&n);
pscore-(int *)calloc(n,sizeof(int));
if(pscore==NULL)
{
printf("Insufficient memory available!");
exit(0);
}
... //对内存单元处理
函数 realloc():改变原来分配的存储空间大小:void *realloc(void *p, unsigned int size);
char i,*p;
p=(char *)malloc(4*sizeof(char)); //p所指内存为4个int字节
for(i=0;i<4;i++)
*(p+i)=i+1;
p=(char *)realloc(p,6*sizeof(char)); //修改后p所指内存为6个int字节
函数 free():void free(void *block);
free(pscore);
多级指针:指向一级指针,存放一级指针的地址。
定义格式:[存储类型] 数据类型符 **变量名;
int a=3;
int *p1;
int **p2;
p1=&a; //p1指向a
p2=&p1; //p2指向p1
**p2=5; //改变a的值为5
指针作为函数参数:传地址
函数alltrim(char *psstr, char *pdstr), 去除psstr所指字符串的前导空格和后续空格
#include "stdio.h"
#include "string.h"
void alltrim(char *psstr, char *pdstr);
void main()
{
char *pstr, str[20];
pstr=" Good Bye! ";
printf("before alltrim:%s\n",pstr);
alltrim(pstr,str);
printf("after alltrim:%s\n",str);
}
void alltrim(char *psstr,char *pdstr)
{
char *pstart, *pend;
pstart=psstr;
while(*pstart==' ')
pstart++;
pend = pstart+ strlen(pstart) -1;
while(pend>pstart && *pend == ' ')
pend--;
//将pstart所指字符至pend所指字符复制到pdstr中
while(pstart<=pend)
*pdstr++ = *pstart++;
*pdstr='\0';
}
求某矩阵各行元素之和
#include "stdio.h"
#include "stdlib.h"
#include "malloc.h"
int GetSumRow(int *p, int num);
int GetMaxRow(int **p,int row,int col);
void main()
{
int row,col;
int i,j,**p,maxrow;
printf("input row=");
scanf("%d",&row);
printf("input col=");
scanf("%d",&col);
p=(int **) malloc(row * sizeof (int *)); //申请创建行指针的空间
for(i=0;i<row;i++) //申请创建列指针的空间
p[i]=(int *) malloc(col * sizeof(int));
printf("input the number:\n");
for(i=0;i<row;i++)
for(j=0;j<col;j++)
scanf("%d",p[i]+j);
maxrow= GetMaxRow(p,row,col);
printf("---------------------\n");
printf("maxrow=%d\n",maxrow);
for(i=0;i<row;i++) //释放每一行的空间
free(p[i]);
free(p); //释放二重指针空间
}
int GetMaxRow(int **p, int row, int col)
{
int i,max,t;
max= GetSumRow(p[0],col);
for(i=1;i<row;i++)
{
t= GetSumRow(p[i],col);
if(t>max)
max=t;
}
return (max);
}
int GetSumRow(int *p, int num)
{
int i,sum=0;
for (int i = 0;i<num;i++)
sum += p[i];
return (sum);
}
指针函数:返回指针类型的函数
定义格式:函数类型 *函数名([形参1,形参2,...,形参n])
#include <malloc.h>
#include "stdio.h"
#include "string.h"
char *alltrim(char *psstr);
void main()
{
char *pstr;
pstr=" Good Bye! ";
printf("before alltrim:%s\n",pstr);
printf("after alltrim:%s\n",alltrim(pstr));
free(alltrim(pstr)); //释放分配的指针内存
}
char *alltrim(char *psstr)
{
char *p,*pstart, *pend;
int i,size;
pstart=psstr;
while(*pstart==' ')
pstart++;
pend = pstart+ strlen(pstart) -1;
while(pend>pstart && *pend == ' ')
pend--;
//根据字符个数动态分配内存单元
size=pend>=pstart ? pend - pstart+2:1;
p=(char *)malloc(size*sizeof(char));
//将pstart所指字符至pend所指字符复制到pdstr中
for(i=0;pstart<=pend;i++)
p[i] = *pstart++;
p[i]='\0';
return (p);
}
函数指针:指向函数的指针
定义格式:函数类型 (*指针变量) ([形参类型1,形参类型2,...,形参类型n])
如:int (*p)(int, int); 定义了一个可指向带两个int型的形参,其返回值为int型指针。
赋值:函数指针 = [&]函数名; [&]表示&可选
int max(int a, int b)
{
return (a>b? a:b);
}
int (*p)(int, int); //定义函数指针p
p = max; //将函数对应的内存首地址(函数名max)赋给函数指针p
调用:函数指针变量([实参1,实参2,...,实参n]); 或 (*函数指针变量) ([实参1,实参2,...,实参n]);
p(2,3); or (*p)(2,3); //equal to max(2,3);
函数指针的应用:整合多个函数功能
#include "stdio.h"
#include "stdlib.h"
#include "malloc.h"
int max(int,int);
int min(int,int);
int add(int,int);
void process(int,int,int (*fun)(int,int));
void main()
{
int a,b;
scanf("%d%d",&a,&b);
process(a,b,max);
process(a,b,min);
process(a,b,add);
}
int max(int x,int y)
{
printf("max=");
return x>y?x:y;
}
int min(int x,int y)
{
printf("min=");
return x<y?x:y;
}
int add(int x,int y)
{
printf("sum=");
return (x+y);
}
void process(int x,int y,int (*fun)(int,int))
{
int result;
result=(*fun)(x,y);
printf("%d\n",result);
}
带参数的mian函数
#include "stdio.h"
#include "stdlib.h"
#include "malloc.h"
#include "math.h"
int GetMonth(char *str);
char *month_str[]={
"January","February","March","April","May","June","July","August",
"September","October","November","December"
};
void main(int argc, char *argv[]) //argc为参数个数n+1,*argv[]存放命令字符串
{
int k;
if(argc!=2)
{
printf("Use this program like this: main 3");
exit(0);
}
k= GetMonth(argv[1]);
if(k==1)
{
printf("Command line argument must be in [1-12]\n");
exit(0);
}
printf("%s\n",month_str[k-1]);
}
int GetMonth(char *str)
{
int k,i=0;
while (str[i]!=0) //判断是否为合法数字串
{
if(str[i]<'0' || str[i]>'9')
return -1;
else
i++;
}
k=atoi(str);
if(k<0 || k>12)
return -1;
return k;
}
预处理命令
宏定义 #define undef
不带参数的宏定义:
#define 标识符 单词串 //标识符一般大写,字符串要带双引号,若太长需要使用\续行
#undef 标识符
注意事项:
可以嵌套,不可递归
采用双引号中的标识符不能进行宏替换
宏定义遇到换行结束,不需要 ;
宏可以被重复定义
若宏是一个表达式需要用()括起来
带参数的宏定义:
#define 标识符(参数列表) 单词串 //参数列表无类型,调用时,将宏参数替换为实参文本,类似于函数,本质是文本替换,不同的是编译的区别
带参数的宏定义:
文件包括 #include
可包括.c和.h文件
条件编译 #if #ifdef #else #elif #endif
与defined 宏名搭配使用,形成#if defined(宏名),即#ifdef (宏名)
或#ifndef 宏名
程序段1
#else
程序段2
#endif
其他 #line #error #pragma
复杂数据类型
结构体
struct [结构体类型名] //结构体类型名可省略,结构体属于类型标识符,类似int,char
{
数据类型名1 成员名1;
数据类型名2 成员名2;
...
数据类型名n 成员名n;
};
struct 结构体类型名 变量名列表; //定义结构体变量
struct [结构体类型名] //结构体类型名可省略,结构体属于类型标识符,类似int,char
{
数据类型名1 成员名1;
数据类型名2 成员名2;
...
数据类型名n 成员名n;
}变量名列表; //定义结构体变量
注意事项:结构体可以嵌套,但不能是自身类型的变量,可以包含自身类型的指针。
结构体变量的引用:
结构体变量名.成员名
结构体指针->成员名 或 (*结构体指针).成员名 //"."与”->“是优先级最高的运算符
和[]和()同一个优先级
结构体变量的赋值:
struct 结构体类型名
{...};
struct 结构体类型名 变量名={成员1的值,成员2的值,...,成员n的值};
struct 结构体类型名
{...} 变量名={成员1的值,成员2的值,...,成员n的值};
程序中赋值:
#include "stdio.h"
#include "string.h"
struct Date
{
int year,month,day;
};
struct Stu_Info
{
char no[9]; //学号
char name[20]; //姓名
char sex; //性别
struct Date birthday; //生日
unsigned int classno; //班级
float grade; //成绩
unsigned int age; //年纪
};
void Print(struct Stu_Info *p) //输出结构体里数据
{
printf("name=%s,no=%s,sex=%c,age=%d,classno=%d,grade=%d\n",\
p->name,p->no,p->sex,p->age,p->classno,p->grade);
}
void main()
{
struct Stu_Info stu;
strcpy(stu.no,"22000218");
strcpy(stu.name,"zhangshan");
stu.sex='M';
stu.age=18;
stu.classno=2;
stu.grade=100;
struct Stu_Info stu1,stu2;
stu1=stu; //直接复制stu到stu
memcpy(&stu2,&stu,sizeof(struct Stu_Info)); //使用函数复制
Print(&stu1);
Print(&stu2);
}
简化结构体类型名:typedef
格式:typedef 类型名 类型名的别名;
#include "stdio.h"
#include "string.h"
typedef int INTEGER;
typedef char* STRING;
struct teacher_info
{
char name[20], sex, unit[30];
unsigned int age, workyears;
float salary;
};
typedef struct teacher_info TEACHER;
INTEGER a;
STRING str;
TEACHER t;
void main()
{
a=9;
str="nihao";
printf("%s",str);
}
结构体数组:相当于一张二维表,表中每一列相当于该结构体的成员,每一行信息对应各数组元素的各成员的具体值。
线性链表:
结点的结构体格式:
struct 节点结构体类型名
{
数据成员定义;
struct 节点结构体类型名 *指针变量名;
};
#include <malloc.h>
#include "stdio.h"
#include "stdlib.h"
#include "string.h"
struct Grade_Info
{
int score;
struct Grade_Info *next;
};
typedef struct Grade_Info NODE;
NODE *Greate_LinkList() //返回类型位NODE指针类型
{
NODE *head,*tail,*pnew;
int score;
head=(NODE *)malloc(sizeof(NODE));
if(head==NULL)
{
printf("no enough memory!\n");
return (NULL);
}
head->next=NULL;
tail=head;
printf("input the score of students:\n");
while (1)
{
scanf("%d",&score);
if(score<0)
break;
pnew=(NODE *) malloc(sizeof(NODE));
if(pnew==NULL)
{
printf("no enough memory!\n");
return (NULL);
}
//对新结点赋值
pnew->score=score;
pnew->next=NULL;
//尾结点移向当前结点
tail->next=pnew;
tail=pnew;
}
return (head); //返回头指针
}
//在第i个结点插入结点
void Insert_LinkList(NODE *head,NODE *pnew, int i)
{
NODE *p; //做索引用
int j;
p=head; //p指向头结点
//p向后移动i个位置,此刻的位置为i-1,即指向要插入的结点前面
for(j=0;j<i && p!=NULL;j++)
p=p->next;
if(p==NULL)
{
printf("the %d node not found!\n",i);
return;
}
//新结点的前一个指向新结点,新结点指向前一个结点的后一个结点
pnew->next=p->next;
p->next=pnew;
}
void Delete_LinkList(NODE *head, int i)
{
NODE *p,*q;
int j;
if(i==0)
return;
p=head;
for(j=1;j<i && p->next !=NULL;j++)
p=p->next; //p的指向要插入的结点的前驱结点
if(p->next==NULL)
{
printf("the %d node not found!\n",i);
return;
}
q=p->next; //q指向要删除的结点
p->next=q->next; //p的next指向q的下一个
free(q);
}
void Free_LinkList(NODE *head)
{
NODE *p,*q;
p=head;
while (p->next!=NULL)
{
q=p->next;
p->next=q->next;
free(q);
}
free(head);
}
void Display_LinkList(NODE *head)
{
NODE *p;
for(p=head->next;p!=NULL;p=p->next)
printf("%d ",p->score);
printf("\n");
}
void main()
{
NODE *head, *pnew;
head=Greate_LinkList();
if(head==NULL)
return;
printf("after create:");
Display_LinkList(head);
pnew = (NODE *) malloc(sizeof(NODE));
if(pnew==NULL)
{
printf("no enough memory!\n");
return;
}
pnew->score=88;
Insert_LinkList(head,pnew,3);
printf("after insert:");
Display_LinkList(head);
Delete_LinkList(head,3);
printf("after delete:");
Display_LinkList(head);
Free_LinkList(head);
}
联合体
定义与引用:类似结构体
赋值:联合体变量的各成员共享同一地址单元,共用体成员间相互影响
#include "stdio.h"
void main()
{
union
{
long L;
short a;
char ch;
}d={0xFFF11241};
printf("d.ch=%c d.a=%X d.L=%X\n",d.ch,d.a,d.L);
d.a++;
printf("d.ch=%c d.a=%X d.L=%X\n",d.ch,d.a,d.L);
}
位域
将结构体或联合体中的成员定义为位域成员,位域成员只占有限几位的结构体或联合体成员。
枚举类型
效果:文章替换变量
用途:对于一个变量有几种可能的值
#include"stdio.h"
enum color{red,white,blue};
void main()
{
static enum color flag[20] = {
white,red,red,blue,white,
red,blue,red,blue,white,
red,blue,white,red,blue,
white,red,white,red,blue
};
enum color temp;
int rr, lb, nx, i;
rr = -1;
lb = 20;
nx = 0;
while (nx != lb)
{
switch (flag[nx])
{
case red: //如果当下是红色的,交换当下和左边的值
rr++;
temp = flag[nx];
flag[nx] = flag[rr];
flag[rr] = temp;
nx++;
break;
case white: //如果是白色的,不动
nx++;
break;
case blue: //如果是蓝色的,交换当下和右边的值
lb--;
temp = flag[nx];
flag[nx] = flag[lb];
flag[lb] = temp;
break;
default:
break;
}
for(i=0;i<20;i++)
switch (flag[i])
{
case red: putchar( 'r' );
break;
case white: putchar('w');
break;
case blue: putchar('b');
break;
default:
break;
}
printf("\n");
}
}
综合应用:
#include"stdio.h"
#include"stdlib.h"
enum SEX //定义性别属性
{
man,
female
};
struct Student_Info //定义学生属性
{
char no[9];
char name[20];
enum SEX sex;
unsigned int age;
unsigned int classno;
float grade;
};
typedef struct Student_Info STUDENT;
STUDENT* GetStuInfo(int i); //获取学生信息,给每一个结构体赋值
void SortStuInfo(STUDENT **pstu, int num); //给结构体数组排序
void FreeMemory(STUDENT **pstu, int num); //释放结构体数组内存
void main()
{
STUDENT** pstu;
int i, num;
printf("the number of the students: ");
scanf("%d", &num);
if (num <= 0)
return;
pstu = (STUDENT**)malloc(num * sizeof(STUDENT*));
if (pstu == NULL)
{
printf("no enough memory!\n");
return;
}
for (i = 0; i < num; i++)
{
pstu[i] = GetStuInfo(i);
if (pstu[i] == NULL)
{
printf("not enough memory!\n");
FreeMemory(pstu, i);
return;
}
}
SortStuInfo(pstu, num);
printf("\n-------------sort result--------------\n");
for (i = 0; i < num; i++)
printf("%12s%20s%9s%5d%5d%8.1f\n", pstu[i]->no, pstu[i]->name,
(pstu[i]->sex == man) ? "man" : "female", pstu[i]->age, pstu[i]->classno, pstu[i]->grade);
FreeMemory(pstu, num);
}
STUDENT* GetStuInfo(int i)
{
STUDENT* p;
char sex;
p = (STUDENT*)malloc(sizeof(STUDENT));
if (p == NULL)
return NULL;
printf("\n-------input %dth student's information-------\n", i + 1);
printf("no: ");
scanf("%s", p->no);
printf("name:");
scanf("%s",p->name);
fflush(stdin);
while (1)
{
printf("sex(M,F): ");
scanf("%c", &sex);
if (sex == 'M' || sex == 'F')
break;
fflush(stdin);
}
p->sex = (sex == 'M') ? man : female;
printf("age: ");
scanf("%d", &p->age);
printf("classno: ");
scanf("%d", &p->classno);
printf("grade: ");
scanf("%f", &p->grade);
return p;
}
void SortStuInfo(STUDENT** pstu, int num)
{
STUDENT* p;
int i, j, k;
for (i = 0; i < num - 1; i++)
{
k = i;
for (j = i + 1; j < num; j++)
if (pstu[j]->grade > pstu[k]->grade)
k = j;
if(k!=i)
{
p = pstu[i];
pstu[i] = pstu[k];
pstu[k] = p;
}
}
}
void FreeMemory(STUDENT** pstu, int num)
{
int i;
for (i = 0; i < num; i++)
free(pstu[i]);
free(pstu);
}
文件
文件结构体FILE
typedf struct
{
int level;
unsigned flags;
char fd;
unsigned char hold;
int bsize;
unsigned char _FAR *buffer;
unsigned char _FAR *curp;
unsigned istemp;
short token;
} FILE;
文件类型指针
FILE *fp; //结构体指针,指向FILE结构体
打开文件
FILE *fopen(char *filename, char *mode);
filename是一个字符串,表示要打开的文件名,一般为文件的路径
mode:
r | 打开文件,只能读不能写 |
w | 创建新文件,只能写不能读 |
a | 打开文件,在尾部追加数据,若不存在,创建后追加 |
r+ | 打开文件,读写 |
w+ | 创建文件,读写 |
a+ | 等价a,但可以读 |
t | 打开文本文件 |
b | 打开二进制文件 |
关闭文件:
int *fclose(FILE *filepointer);
FILE *fp;
fp=fopen("wang.txt","r");
if(fp==NULL)
{
printf("the file: wang.txt not found!");
exit(-1);
}
...
fclose(fp);
文件的读写:
字符读函数:fgetc,fputc:每次读或写一个字符到文件
int fgetc(FILE *filepointer);
int fputc(int c, FILE *filepointer);
判断文件是否结束:feof
int feof(FILE *filepointer);
字符串读写:fgets,fputs
char *fgets(char *s, int n, FILE *filepointer);
int fputs(char *s, FILE *filepointer);
文件读写位置指针用以指示文件内部的当前的读写位置,每读写一次,该指针均向后移动,不需要定义和说明,是由系统自动设置的。
数据块读写函数:fread,fwrite:一般用于二进制文件的输入和输出
unsigned fread(void *ptr, unsigned size, unsigned n, FILE *filepointer);
//从filepointer所指的文件中读取n个数据项,每个数据项的大小是size个字节,将这些数据放到ptr所指的内存中
unsigned fwrite(void *ptr, unsigned size, unsigend n, FILE *filepointer);
格式化读写函数fscanf,fprintf:针对标准输入(stdin)输出(stdout)文件,只能操作文本文件
int fscanf(FILE *filepointer, const char *format[,address,...]); //输入格式化变量到变量中
int fprintf(FILE *filepointer, const char *format[,address,...]); //输出格式化数据到文件
文件定位的读写:rewind,fseek,ftell
void rewind(FILE *filepointer); //将filepointer所指文件的读写位置指针重置为文件首部
int fseek(FILE *filepointer, long offset, int whence)
offset为正值,新位置在whence后面,offset为负值,新位置在whence前面
SEEK_SET | 0 | 文件开始处 |
SEEK_CUR | 1 | 文件位置指针当前位置 |
SEEK_END | 2 | 文件末尾 |
/*获取文件内容并显示,以空格为界读取字符串和数据*/
#include"stdio.h"
#include"stdlib.h"
#define MAXSIZE 100
struct Book {
char id[MAXSIZE];//ISBN
char name[MAXSIZE];//书名
double price;//定价
} book[MAXSIZE];
typedef struct {
Book* elem; //存储空间的基地址
int length; //当前长度
} SqList;
void main()
{
SqList L;
int i=0,p=1;
char buf[256],*s;
FILE* fp;
fp = fopen("book.txt", "r+");
if (fp == NULL) {
printf("错误!未找到文件!\n");
exit(0);
}
L.elem = (Book *)malloc(MAXSIZE* sizeof(Book));
while (!feof(fp))
{
fgets(buf, 256, fp);
//if ('\n' == buf[0]) continue; //空行继续
s = buf;
sscanf(s, "%s%s%lf", &L.elem[i].id,&L.elem[i].name,&L.elem[i].price);
i++;
}
fclose(fp);
do
{
printf("%-15s %-30s %-lf\n", L.elem[p].id, L.elem[p].name, L.elem[p].price);
p++;
} while (p < i);
}