您的位置:首页 > 编程语言 > C语言/C++

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语言