C语言基础整理
2016-02-24 16:28
141 查看
机器语言-->>汇编语言-->>高级语言 一个地址对应一个字节,一个字节8位 变量的定义:变量就是内存中已命名的存储空间,程序通过引用变量就可以引用保存在该存储空间中的数据。 void 无值类型,不能定义变量,但可以定义指针 变量在没有赋初始值的时候是有数据的(其他程序释放内存后留下的数据),通常叫做垃圾值。 初始化:定义变量的同时进行赋值才叫变量的初始化。 查看变量占多少个字节 sizeof(变量名) 查看变量地址 printf("&a=%p\n",&a) 输出无符号整型一般用%u %e 为科学计数法方式 例如:1.23e+3 为1.23*10的3次方 地址打印为%p printf("%p",&a); 后面必须是一个地址 char *p="sab"; 字符传输出%s printf("%s",p); 后面必须是一个地址,会从第一个字符开始输出,接着输出后面的字符 单个字符输出%c print("%c",*p); 会输出第一个字符,后面是一个具体的值 if里面的判断条件只有0和1两种 1为成立,0为不成立,如果条件得0也为不成立 find -name 需要查找的文件 查找文件 find -name limits.h 查看类型大小的文件 取随机数的一个函数 #include<stdio.h> #include<stdlib.h> int main(){ int i=0; int a=0; srand(time(NULL)); printf("time=%d\n",time(NULL)); for(i=0;i<5;i++){ a=rand()%51+50; printf("a=%d\n",a); } } 位运算 计算机里只有补码 正数的补码是本身 负数的反码为按位取反符号位不变,负数的补码是按位取反再加1 以下操作都是补码: >>右移运算: 正数 整体右移 高位补符号位(最高位补位是根据机器的不同而不同,有的机器一律补0) 负数 整体右移 高位补符号位(最高位补位是根据机器的不同而不同,有的机器一律补0) >>左移运算: 正数 整体左移 低位补0 负数 整体左移 低位补0 (有的机器把符号位移出,有的不移出) &按位与 : 正数 每一位都相与 全1为1 其它都是0 |按位或 : 正数 每一位都或运算 全0为0 其它都是1 ~按位取反 : 正数 每一位都取反 ^按位异或 : 正数 相同为0 不相同为1 当使用<math.h>包中的sqrt()函数时,编译的时候后面要加上-lm 例如:gcc t6.c -lm const int b=1000; 常量 常量必须初始化 1234 科学计数法 1.234e+3 break 结束循环体 continue 结束本次循环继续下一次循环 goto 只能在一个函数中使用 指针 指针:指针就是变量的地址,存放指针的变量就是指针变量。在指针变量中不是存放具体的数据,而是存放其他变量的地址即存放其他变量的指针。 指针==地址 指针可以进行运算 int *p=NULL;(这里的*号是类型说明符) p=&a; p+1 就是p指向的地址加4 比原来地址大4 如果是char 就每次偏移1个大小 偏移大小根据指针类型偏移 p是指针本身 是一个地址 4个字节 *p是指针所指向的那块内存的内容,跟据类型来确定 如果是double的就是8个字节,这里的*号是一个运算过程 我们可以用指针类型来限定指针处理字节的大小,一般用同种类型的指针来处理对应类型的变量。 不管什么类型的指针都只占4个字节,这也是根据机器操作系统来确定,但是在每个系统中指针所占大小相同 一个地址对应一个字节,一个字节8位 int* ,char*,double* 表示每种类型的指针 指针常量 int a=10; int * counst p=&a; 指针就是常量, p的指向不能改变,p必须初始化 p是常量 常量指针 int a=10; const int *p=&a; a,p都不是常量 *p是常量 就是定义了一个只读的指针 多级指针 二级指针:int **pp=&p; 这里的**是二级指针标识 存放的是指针的地址 pp就是指针p的地址 pp=&p *pp=p 寻址一次 是p所存放的地址(内容) **pp=a 寻址两次 是p所有指向的内容 三级指针 int ***ppp=&pp 这里***是三级指针的标识 存放的是指针pp的地址 ppp=&pp *ppp=pp=&p **ppp=*pp=p=&a ***ppp=**pp=*p=a 数组 一组有相同数据类型的数据的有序集合,需要连续的空间存储 数组的初始化操作: int a[5]={1,2,3,4,5}; int b[5]={0}; int a[]={1,2,3}; 数组名存放的是数组第一个元素的地址 可以把数组名看作成一个指针 a[0]=*(a+0) a[1]=*(a+1) int *p=NULL p=a p[i]=a[i] //等价 a=b 是错的 ,a里面的地址不能修改,是一个常量 sizeof(a)==20;整个数组的大小 sizeof(p)==4; 指针 size0f(a)/sizeof(a[0]) 可以查看数组一共有多少个元素 二维数组 int a[3][4]={1,2,3,4,5,6,7,8,9,0,1,2} int b[2][2]={{1,1},{1,1}} int a[3][4]={0} int c[][2]={{1,1},{1,1}} int b[3][4]; int **pp; pp=b 是错误的 a是一个二维数组 sizeof(a) 所有元 15377 素所占字节数 sizeof(a[0]) 一行所有元素所占字节数 确定行 for(i=0;i<sizeof(a)/sizeof(a[0]);i++){ 确定每行有多少个元素 for(j=0;j<sizeof(a[i])/sizeof(a[i][0]);j++){ printf("%d",a[i][j]): } } 三维数组 int a[2][3][4]={ { {1,2,3,4},{5,6,7,8},{9,10,11,12} }, { {13,14,15,16},{17,18,19,20},{21,22,23,24} }, } 字符数组 字符数组的定义要比使用空间至少多一个 char a[6]={'a','b','c','d','e','\0'}; 这种写法只能用于初始化 char a[]={'a','b','c','d','\0'}; 这种写法只能用于初始化 char a[6]={'a','b','c','d','e'}; 这种写法只能用于初始化 char a[]="abcdefgh"; 这种方法会自动初始化并加尾0 这种写法只能用于初始化 sizeof(a) 测出a数组整个大小 strlen(a) 测出a数组\0之前大小 printf("a=%s",a); 字符数组的打印 字符的二维数组 char a[3][20]={"abc","efg","hij"}; #include<stdlib.h> 包含字符函数 #include<string.h> 包含字符函数 strcpy(b,a); 字符串拷贝函数,把a复制给b,是根据首地址一个字符一个字符的复制 strcmp(a,b); 比较两个字符数组是否相等,如果返回值是0表示不相等,1表示相等 复制二维数组 char e[3][4] char f[3][4] for(i=0;i<3;i++){ strcpy(f[i],e[i]); } 指针数组 数组元素都是指针 int *p[3]; int a[20]="abcd"; //a数组在栈区 所以内容可以改变 char *p="jlmn"; "jlmn" 会放在常数区(都是长串),最后一个字节同样会放\0,p会指向j的地址也就是这个串的首地址, 字符串一旦放在了常数区就不可以更改 因为p里面只能存地址,没有给jlmn在栈区分内存,只能存常数区 printf("p=%s\n",p); char *p2="jlmn"; p2所指向的地址与p所指向的地址相同,是一块内存 char *p3="jlm"; p3所指向的地址与p,p2不同,会重新分配一块内存 堆区内存的操作 堆区的操作靠地址 #include<stdlib.h> 头文件中包含下面三个函数 void *malloc(sizeof(int))函数 分内存 返回一个指针 int *p=NULL; double *pp=NULL; p=malloc(sizeof(int)); pp=malloc(sizeof(double)); 一个在堆区做二维数组的例子 .............................................................................................. #include <stdio.h> #include <stdlib.h> int main() { int **p=NULL; int n=0; int m=0; int i=0; int j=0; printf("输入学生数量:"); scanf("%d",&n); printf("输入考试科目数量:"); scanf("%d",&m); p=(int**)malloc(sizeof(int*)*n); for(i=0;i<n;i++) { p[i]=malloc(sizeof(int)*m); } for(i=0;i<n;i++) { printf("输入第%d个学生成绩:\n",i+1); for(j=0;j<m;j++) { printf("输入第%d个科目分数:",j+1); scanf("%d",&p[i][j]); } } for(i=0;i<n;i++) { printf("输出第%d个学生成绩:\n",i+1); for(j=0;j<m;j++) { printf("输出第%d个科目分数:",j+1); printf("%d\n",p[i][j]); } } for(i=0;i<n;i++) { free(p[i]); } free(p); return 0; } .............................................................................................. void free(void *p)函数 释放内存 free(p); 释放p控制的内存 void *realloc(void *ptr int size)函数 对已经分配的内存在从新分配 int *p=NULL; p=malloc(sizeof(int)*5); p=realloc(p,sizeof(int)*8); 对p重新分配内存.不会覆盖原来存储的数据 这种分法需要下面有空闲内存,如果没有 空闲内存则会重新寻找可以分配的内存空间,找到后会重新存储,释放掉原来的内存 内存泄漏 p=malloc(sizeof(int)*5); p=malloc(sizeof(int)*10); 这样会造成内存泄漏 50个字节的内存泄漏 内存操作错误问题 迷途指针 p=malloc(sizeof(int)*5); free(p); 内存释放后 p指向的地址不改变 仍然控制着上面分配的50个字节 p=NULL; 用这种方法来解决上面的问题 p[1]=1000; 同一个指针不能释放两次 内存 ........................................................................ 栈区: 定义的一些变量 int a; 处理一些运算频率比较高的数据 1MB~2MB左右 自动管理 使用完后自动释放 堆区: 很大,其它区使用剩余的都分给它 大数据存储 使用者自己管理 最难控制 全局区: 存放全局变量,只能初始化变量,不能执行赋值语句 常数区: 存放常数 生命周期:和程序的生命周期是一致的 代码区: 存放代码 函数 是一段用函数规范形式表示并能够完成一定功能的代码 指针函数:返回类型为指针的函数为指针函数 函数指针:函数类型的指针 就是函数指针 函数名就是指针 函数调用靠函数名(里面放函数代码首地址,在代码区)找到代码,以此执行代码 函数名指针所指向的地址不能改变 类似数组名 函数里面的代码是放在代码区 函数名也存在代码区,函数名在一个列表中,调用函数先要到函数名列表中找到函数名指针,在查找代码 声明函数类型的指针(需要和需要指向函数的类型一致): void printf_word(char *str); void (*p)(char *str); //指向上面函数的指针 如果函数没有形式参数 void(*p)(); p=printf_word; //这样就指向函数了,用这个指针就可以调用函数了 p("abc"); //可以实现和函数一样的功能 参数类型的函数指针:函数指针做参数 一个例子 #include <stdio.h> int add(int a,int b) { return a+b; } int sub(int a,int b) { return a-b; } int C(int a,int b) { return a*b; } int Math(int a,int b,int (*p)(int x,int y)) { return p(a,b); } int main() { int m=100; int n=200; int q=0; q=Math(m,n,add); printf("q=%d\n",q); q=Math(m,n,sub); printf("q=%d\n",q); q=Math(m,n,C); printf("q=%d\n",q); return 0; } 函数指针数组 int (*p[]) (int a,int b)={add,sub,mul}; //定义函数指针数组 int z; z=p[0](2,3); 结构体 结构体是不同数据类型的集合,它本身也是一种数据类型,自定义数据类型 结构体的大小是内部所有变量大小之和,他们在内存中顺序排列 定义结构类型的格式: struct student { 结构体成员部分 int id; char a[40]; }; int main(){ struct student stu1={101,"张三"}; //定义并初始化 struct student stu2=stu1; //可以这样赋值 printf("stu1.id=%d",stu1.id); } 堆区不能初始化 struct student *p=NULL; p=malloc(sizeof(struct student)); p->id=100; 这句话等价于 (*p).id=100; strcpy(p->name,"张三"); 给结构体中的字符数组赋值需要通过字符数组复制 共用体(联合) 自定义数据类型,同一时间只能存放一个变量,当我们无法确定数据类型不确定的时候,就可以使用共用体 union A{ int a; short b; float c; double e; }; int main(){ union A x; return 0; } 枚举类型 默认第一个为0,然后以此类推,枚举值一般不能更改,其实就是一些常量值的集合,一个枚举变量同样不能重复 enum meiju{ a, b, c, d }; enum meiju2{ a, //这样是错误的,和上面的a重复了 e }; 变量的分类 全局变量(外部变量) 与程序生命周期一致 全局变量存在全局区 使用外部文件中的外部变量(跨文件使用变量):需要先声明 extern int x; 外部变量不作初始化系统会默认为0 内部变量,块级变量则为垃圾值 局部变量(内部变量) 与函数生命周期一致 如果程序中局部变量和外部变量同名,则函数会根据就近原则使用局部变量 块级变量 例如for循环 就是一个语句块,for循环里面定义的变量就是一个块级变量 静态变量 静态外部变量 static int y=0;系统默认初始化为0,存在全局区 只能在本文件中使用,其他文件无法使用 静态内部变量 static int x; 虽然函数多次调用,但是定义这句话只执行一次 如果不初始化,系统默认为0 静态变量存放在全局区中 只在本函数体内有效,生命周期和程序的生命周期相同 寄存器变量 register int i; 定义的时候前面加上register(建议关键字) 会提高变量的读取速度,适合变量运算频率特别高的变量 C语言会对寄存器变量进行优化 文件 写文件 内存->硬盘 读文件 硬盘->内存 文本文件操作 写文件 一个例子 #include<stdio.h> int main(){ FILE *fp=NULL; fp=fopen("a.txt","w"); int i=0; int id=0; char name[40]; if(fp==NULL){ printf("打开文件失败!!!\n"); return 0; }else{ for(i=0;i<3;i++){ printf("请输入ID\n"); scanf("%d",&id); printf("请输入名字\n"); scanf("%s",name); fprintf(fp,"%d\t%s\n",id,name); } fprintf(fp,"文件写入结束\n"); fclose(fp); } return 0; } 读文件一个例子 #include<stdio.h> int main(){ FILE *fp=NULL; int i=0; int id=0; char a[40]; fp=fopen("a.txt","r"); if(fp==NULL){ printf("读取文件失败\n"); return 0; }else{ for(i=0;i<2;i++){ fscanf(fp,"%d\t%s\n",&id,a); printf("%d\t%s\n",id,a); } fclose(fp); } return 0; } 二进制文件操作 写文件: #include <stdio.h> int main() { FILE *pf=NULL; int a[]={1,2,3,4,5,6,7,8,9}; if((pf=fopen("b.bin","wb"))==NULL) { printf("err\n"); return 0; } else { fwrite(a,sizeof(int),9,pf); fclose(pf); } return 0; } fwrite(内存首地址,每次存放大小,一共存放那个几次,放在哪个文件中); 读文件 #include <stdio.h> int main() { FILE *pf=NULL; int a[9]; int i=0; if((pf=fopen("b.bin","rb"))==NULL) { printf("err"); return 0; } else { fread(a,sizeof(int),9,pf); for(i=0;i<9;i++) { printf("a[%d]=%d\n",i,a[i]); } fclose(pf); } return 0; } fread(存放的内存首地址,每次读取多少个字节,读取的次数,从哪个文件中读取); 把文件写入堆区 #include <stdio.h> #include <stdlib.h> struct stu { char name[40]; char addr[40]; char tel[40]; }; int main() { struct stu *p=NULL; p=malloc(sizeof(struct stu)*3); int i=0; for(i=0;i<3;i++) { printf("name="); scanf("%s",p[i].name); printf("addr="); scanf("%s",p[i].addr); printf("tel="); scanf("%s",p[i].tel); } FILE *pf=NULL; if((pf=fopen("stu.bin","ab"))==NULL) { printf("err"); return 0; } else { fwrite(p,sizeof(struct stu),3,pf); fclose(pf); } free(p); } 把文件读出来存在堆区 #include <stdio.h> #include <stdlib.h> struct stu { char name[40]; char addr[40]; char tel[40]; }; int main() { struct stu *p; p=malloc(sizeof(struct stu)*6); int i=0; FILE *pf=NULL; if((pf=fopen("stu.bin","rb"))==NULL) { printf("err"); return 0; } else { fread(p,sizeof(struct stu),6,pf); for(i=0;i<6;i++) { printf("name=%s\n",p[i].name); printf("addr=%s\n",p[i].addr); printf("tel=%s\n",p[i].tel); } fclose(pf); } free(p); } 文本文件: r 只读模式 w 文件不存在,创建文件,写入文件并覆盖 a 文件不存在,产生新文件,文件存在,在原文件尾部增加数据 r+ 读取或修改文件 w+ 文件不存在,产生新文件,读取或写入文件并覆盖 a+ 与a相同 二进制 rb 只读模式 wb 文件不存在,创建文件,有同名原文件丢掉,产生新文件 ab 文件不存在,产生新文件,文件存在,在原文件尾部增加数据 r+b 读取或修改文件 w+b 文件不存在,产生新文件,读取或写入文件,存在同名文件,原文件丢弃,产生新文件 a+b 与a相同 宏 #号 开头的语句 就是宏 宏就是一种替换 代码很短小的适合写成宏函数 #include 是一种包含操作 #denfine A 0 定义A=0 #if #elif #else #endif 宏函数 #define MAX(A,B) A>B?A:B 遇到前面的函数会替换成后面的语句 #isndef 宏名 避免重复导入宏 #denfine 宏名 #endif #denfine A 10 定义宏 #undef A 取消宏 内联函数 inline int max(int a,int b){} 内联函数定义 如果函数调用max会将max代码复制到调用函数体内 没有了调用函数开销和保存,恢复开销 头文件 可以 声明函数,声明全局变量(定义还需在原文件中定义) static void f() {} 静态函数 只能在本文件中使用 头文件写法 函数 先声明后使用 #include "自己写的.h" 会到当前路径中找,当前路径没有会到系统中找 #include <系统的.h> 会到系统文件中找 GDB 调试 gcc -g 文件名 -o 编译文件名 可以进行调试的编译、 gdb j 进行调试 list 查看代码 break 行号或者函数名 。。 设置断点 info break 查看断点信息 run 运行程序 print 变量名 可以看到变量的值 p是print的所写 next 单条语句执行 n是next的所写 finish 函数运行结束,继续下一个函数 continue 继续运行程序 quit 退出gdb gcc -c 文件名 会生成一个.o文件 .o文件再生成可执行文件 gcc a.o b.o main.o -o edit(可执行文件名) makefile 见三个升级版文件 命令行参数 int main(int argc,char *argv[]){ int i=0; for(i=0;i<argc;i++){ printf("%s\n",agrv[i]); } } argc 一共传进来几个参数 文件名是第一个参数 argv 指针数组 每个参数的地址 ./a.out hello hello1 hello2 是4个参数 排序 冒泡排序 针对冒泡排序的一个优化 #include<stdio.h> int main(void){ int arr[9]={1,2,3,9,5,6,7,8,4}; int n=0; int i,j,temp; for(i=0;i<9-1;i++){ n=0; for(j=0;j<9-1-i;j++){ if(arr[j]>arr[j+1]){ temp=arr[j]; arr[j]=arr[j+1]; arr[j+1]=temp; n=1; } } if(n==0) break; } for(i=0;i<9;i++){ printf("%d",arr[i]); } printf("\n"); return 0; } 选择排序 #include <stdio.h> int main() { int i,j,k; int temp; int s[10] = {9,8,7,6,5,4,3,2,1,0}; for(i=0;i<9;i++) { k = i; for(j=i+1;j<=9;j++) { if(s[k] > s[j]) k = j; } if(k != i) { temp = s[k]; s[k] = s[i]; s[i] = temp; } } for(i=0;i<10;i++) printf("%3d",s[i]); printf("\n"); return 0; } 插入排序 #include <stdio.h> #define max 10 int main() { printf("请输入10个数\n"); int a[max+1]; int i,j; for(i=1;i<=max;i++) { scanf("%d",&a[i]); } for(i=2;i<=max;++i) { if(a[i]<a[i-1]) { a[0]=a[i]; for(j=i-1;a[0]<a[j];--j) { a[j+1]=a[j]; } a[j+1]=a[0]; } } for(i=1;i<=max;i++) { printf("%d\n",a[i]); } return 0; } 第二个 易懂的 #include<stdio.h> int main(){ int i=0; int j=0; int a[10]; int insertVal=0; int index=0; srand(time(NULL)); for(i=0;i<10;i++){ a[i]=rand()%101; printf("%d ",a[i]); } printf("\n"); for(i=1;i<10;i++){ insertVal=a[i]; index=i-1; while(index>=0 && insertVal<a[index] ){ a[index+1]=a[index]; index--; } a[index+1]=insertVal; } for(i=0;i<10;i++){ printf("a[%d]=%d\n",i,a[i]); } return 0; }
相关文章推荐
- 如何组织构建多文件 C 语言程序(二)
- 如何写好 C main 函数
- Lua和C语言的交互详解
- 关于C语言中参数的传值问题
- 简要对比C语言中三个用于退出进程的函数
- 深入C++中API的问题详解
- 基于C语言string函数的详解
- C语言中fchdir()函数和rewinddir()函数的使用详解
- C语言内存对齐实例详解
- C语言编程中统计输入的行数以及单词个数的方法
- C语言自动生成enum值和名字映射代码
- 使用C语言判断英文字符大小写的方法
- c语言实现的带通配符匹配算法
- C语言实现顺序表基本操作汇总
- C语言中计算正弦的相关函数总结
- 使用C语言详解霍夫曼树数据结构
- 探讨C语言的那些小秘密之断言
- C语言实现BMP转换JPG的方法
- 深入探讨C语言中局部变量与全局变量在内存中的存放位置
- C语言查找数组里数字重复次数的方法