c语言的一个简单回顾

来源:互联网 发布:imap smtp 端口 编辑:程序博客网 时间:2024/06/05 14:07

这里先来说一下cpu的运行原理。

cpu我们知道是计算机的核心,就相当于是说,这就你相当于是人的大脑一样。你没有大脑,人又怎么思考,怎么去做一些平常看似简单的事情。

cpu的根本任务就是去执行指令。

它的内部可以划分为二个重要部件。

第一个是运算器,第二个是控制器。

这里就来说的简单一点。控制器主要是负责从内存中读取数据,载入寄存器中。然后通过内部分析应该执行什么操作。

运算器,主要是负责把控制器传过来控制信号,来进行操作,然后得出我们想要的结果,比如算术运算什么的。

而内存与运算器的桥梁就是控制器。它是整个CPU的指挥中心。

好了,上面就算是简单的说了一下。

 

 

下面我们来说一下,变量为什么必须初始化。

这里就是说,当我们打开了一个应用程序,比如qq这个应用程序。当它退出,也就是我们关闭之后,内存里面会残留很多的垃圾数据。当我们新申请一个变量占据这个内存空间的时候,如果没有初始化,就会默认带上这些垃圾数据。

 

 

下面来说一下c语言常量的问题。

c语言定义一个常量有两种方法。

第一种方法是:define常量名大写 常量值

第二种方法是: const数据类型 常量名=

其实,在c语言中用const定义的常量是一个伪常量。它是可以修改的,存放在栈区。而define定义的常量才是一个真正的常量,是不可以修改的。下面我们来看一下代码;

int main()

{

const int num1=38;

int* p=&num1;

printf("num1=%d\n",num1);//38

*p=50;

printf("num1=%d\n",*p);//50

printf("num1=%d\n",num1);//50

system("pause");

return 0;

}

我们就可以通过地址来进行数据修改

 

下面来简单说一下c语言的数据类型。

四个大类:构造类型(数组,结构体,共用体)

基本数据类型:字符型(char)数值类型(整型(short(2) int(4) long(4) long long(8))与实型(float(4) double(8)))枚举类型(enum)

指针类型

空类型(void)

c语言数据类型默认是有符号的,如果想表示无符号数据类型,在前面加上unsigned

 

这里,我来说一下,字符是可以参与运算的,字符参与运算就是按照他的ascii码进行运算。

下面来写一个简单的程序,大小写字母转换,看你输入什么,就输出相应的大小写。

int main()

{

char c;

 

c=getchar();

//判断一下时大写字母还是小写字母

if(c>=65 && c<=90)

{

c=c+32;

printf("%c\n",c);

}

else if(c>=97 && c<=122)

{

c=c-32;

printf("%c\n",c);

}

else

{

printf("你输入的有错误,请重新输入\n");

Sleep(5000);

exit(0);

}

我来说一下,。这里比较容易出问题的就是,c是不能处理这样的判断语句的65<=c<=90

必须要用上面的逻辑处理才可以。

 

下面我们也会接触到c语言数据类型转换的问题。

一个数据,单纯就是拿一个数据来说,比如1000它其实是一个int类型的数据。

比如100.3285其实本身就是一个double类型的数据。当我们用赋值号把这些数据交给一些变量的时候,数据就会自动转换成相应的数据类型。这里还要注意一个问题,‘A’字符Asizeof检测出来是4个字节。也就是它是按asiicc码来保存数据的。

c语言还是会涉及到数据类型转换的问题。

还是数据会造成缺失的问题。

我们所以,需要对数据进行强制类型的转换。

c语言中整数除以整数,得到的结果依然会是整数。

整除除以浮点数,得到的结果也会是浮点数。

总之就是说,两个数参与运算,以大的数结果为基准。

我们可以通过<limits.h>来查看这些数据类型的极大值,极小值。

比如SHRT_MAX SHRT_MIN有符号short的极大值与极小值

USHRT_MAX 无符号char的最大值

判断浮点数的极值与位数需要再加一个头文件<float.h>

 

下面来说一下数据的输入与输出的一些格式符。

%u 无符号十进制输出

%e 以标准的指数形式输出单双精度浮点数

%g 选用输出宽度较小的格式输出实数

%o以无符号八进制整数输出

linux还有一个%n,统计输入了多少个字符,只有在Linux才会有效。

上面格式符简单介绍:

比如%8d,数据宽度八个位。如果实际数小于8,则按实际数输出,如果实际位数小于8,则左边补0

08d%,如果实际位数小于8位,就用0或者空额来补,如果有0,则用0来补位。

%-08d “-”左对齐,右边补0

 

下面再来说一道题目:

整数逆置的问题:就是说输入一个数,比如1234,我们就把它倒过来4321

10得位,乘1010

#define _CRT_SECURE_NO_WARNINGS

#include <stdio.h>

#include <stdlib.h>

 

//这个是返回的个数的函数

int get10n(int n)

{

//0->1 1->10 2->100 3->1000

int res=1,i;

 

for(i=0;i<n;i++)

{

res*=10;

}

 

return res;

}

 

int main()

{

int num;//需要逆转的数

int count=0;//这个是位数

int i;//这个是循环变量

int lastResult=0;//这个变量是用来存放最后结果的

int numCopy;//定义一个保存变量的副本

 

scanf("%d",&num);//我们输入一个数

numCopy=num;

//先来判断一下多少位

for(;num;num/=10)

{

//这里执行完了之后,num的值是会变的

//c语言是条件等于就停止

count++;

}

 

//下面就来逆置我们的数据,从一个数的后面往前面取

for(i=0;i<count;i++)

{

int numNow=0;

//上面的意思是说,我们要取几次这个数据

numNow=numCopy/get10n(i)%10;//取位数

lastResult+=numNow*get10n(count-1-i);//这个是相乘

}

 

printf("%d\n",lastResult);

system("pause");

return 0;

}

下面再来做一道题,斐波拉契数列

先来说一下,什么是斐波拉契数列

前面两个数是1,后面两个数是前面两个数之和

1 1 2 3 5 8...

这里我们来打印前40个数就好了。

 

int main()

{

int f1=1,f2=1,f3=0,i;//这是定义三个数据

//因为斐波拉契数列属于一种递归性质往下面走

printf("%d %d\n",f1,f2);

 

for(i=1;i<=38;i++)

{

//这里是打印后面个数

f3=f1+f2;

printf("%d\t",f3);

f1=f2;

f2=f3;

}

system("pause");

return 0;

}

 

下面我们来说一下基本数据类型里面的枚举数据类型:

枚举里面的值,其实就是索引值。

0开始的索引值

enum color {.........}

也可以用枚举类型定义变量

enum color bgcolor=red

.........

 

 

c语言函数传参默认是按值进行传递的。就相当于是新开了一个副本去接收传递的变量。

与原来的就没有关系。所以,需要地址或者按引用传递。

 

 

下面我们来说一下c语言数组的问题。c语言定义数组,要么让定义的数组知道自己长度,要么我们去指定数组的长度,要么让数组自己可以计算出长度。如果是数值类型的数组,没有初始化的值会自动赋上数值0,若是字符类型,会自动赋上’\0’

字符数组只能在定义的时候初始化双引号字符串。不能先定义了,在后面初始化。但是,我们可以通过字符串函数复制进去。我们必须记住,数组的名字就是一个指针。数组名字是一个常量指针不可以被修改。

这里我还想说一点,指针指指到数组什么位置,就可以把那个位置当成以指针名为数组名的首地址处理。也就是说方括号此时可以用指针来取值。在c语言中我们经常用指针来操作数组,原因就是它比较快。

 

下面我们来谈及一下,对c语言输入输出流和缓冲区的一个理解。

缓冲区称为缓存,这些存储空间用来缓冲输入与输出的数据。这部分预留空间叫做缓冲区。

缓冲区根据是输入设备还是输出设备,分为输入缓冲区与输出缓冲区。

从缓冲区读取数据有一个好处就是快。

缓冲区分为三种类型:

全缓冲,行缓冲和不带缓冲

全缓冲:填满了才进行实际操作。

行缓冲:当遇到换行符就执行相应的操作。

不带缓冲:标准错误stderr就是典型代表。这使得错误信息可以直接展现出来。

 

既然说到缓冲是一段空间,那么是不是它也是有大小的。

一个缓冲区通常的大小是512byte大小。

缓冲区的大小由stdio.h头文件BUFSIZ定义。

 

下面来说一下,缓冲区的刷新,刷新就是即时输出这些信息。

第一个,满了自动刷新

第二个行遇到回车刷新

第三个使用特定函数来刷新,比如fflush(文件指针),当然,还有文件关闭的时候。

 

下面再来看一道题,自己写一个在字符串中查找字符的函数,找到了,我们就直接返回这个字符的地址。没有找到,我们就返回NULL

char* FindStr(char* string1,char str1)

{

while(*string1!='\0')

{

if(*string1==str1)

{

return string1;//直接返回这个指针

}

string1++;//指针不断往前面加

}

return NULL;//没有找到返回NULL

}

 

int main()

{

char *string1="love";

char str1='j';

 

char* result=FindStr(string1,str1);

if(NULL!=result)

{

printf("%c\n",*result);

}

else

{

printf("不好意思,灭有找到\n");

}

 

system("pause");

return 0;

}

下面来说一个数组必须要注意的一点;

不管是使用strcat函数,还是使用strcpy函数,我们都要考虑数组的范围,不然活报错的。

 

这里说了一下strchr的用法:strrchr刚好相反字符最后一次出现位置的指针

 

int main()

{

char *str="love tangtian";

char str1='t';

char* result=strchr(str,str1);

if(result!=NULL)

{

printf("%c\n",result[0]);//打印一个t

}

else

{

printf("not Found");

}

 

system("pause");

return 0;

}

 

下面来说一下strspn的用法。这个函数返回的是指定字符的下标,而不是一个指针。

参数都是两个字符串,返回一个int

在第一个参数字符串中寻找第二个字符串中出现的字符。

strspn返回的是第一个不在指定字符串中出现的字符位置

strcspn返回的是第一个在指定字符中出现的字符位置(注意是索引位置)

int main()

{

char *str1="love";

char *str2="ljik";

int i=strspn(str1,str2);

char *p=str1;

p+=i;//1

printf("%c\n",*p);//o

system("pause");

return 0;

}

 

结构体在这里我不得不说一下,可以用typedef来定义结构一体,或者定义一种数据类型,给它起一个其他的名字。typedef不能用来定义变量。

typedef struct

{

char name[5];

int age;

char sex;

}student1;

 

int main()

{

student1 s1={"张三",25,''};

student1* s2=&s1;

printf("%s",s2->name);

 

system("pause");

return 0;

}

 

下面就具体来说一下,关于指针的问题。

指针永远是4个字节。

一个单纯的地址。我们必须指明它是什么类型的指针,一片空间也是一样。

保存指针的地址,我们必须用二级指针。

同一个类型的指针可以直接赋值。

 

 

下面我们来说一下,指向元素的指针与指向数组的指针。

int main()

{

int a[5]={1,2,3,4,5};

printf("%d,%d\n",a,&a);//数组名字本身就是一个首地址

printf("%d,%d\n",sizeof(*a),sizeof(*(&a)));//4 20

system("pause");

return 0;

}

当数组名加上&标示的时候,再用*去取的时候,指向的就是整个数组,字节大小就是数组有多大,它就有多大。

下面来说一下,指针引用二维数组的问题。二维数组可以看成行指针与列指针。

比如a[3][4]

int *p[4];//这个就可以代表一个二维组指针。

a代表的就是行指针

&a 就是同一维数组一样,指向整个二维数组的指针,一共有多少个元素,元素共有多少个字节,一下你就可以看明白

a+1 第二行

a+2 第三行

每一行可以看成一维数组处理。

a[i][j]=*(*(a+i)+j);//与这个是等价的

下面我们再来看一道题。以指针的方法,输出二维数组中任意一行与列的值。

int main()

{

int a[2][3]={1,2,3,4,5,6};

int (*p)[3]=a;

int i=0,j=0;

scanf("%d,%d",&i,&j);

printf("%d",*(*(p+i)+j));

system("pause");

return 0;

}

 

下面我们再来看一道题,就是将数组中的数据按相反顺序存放,注意是数组中的数据。

数组没有副本机制,等价于是直接操作原数组。

数组这个东西本身就是直逼内存地址的。

 

//做一个调转函数

void ChangePosition(int *p,int n)

{

int i;

int temp;

for(i=0;i<n/2;i++)

{

temp=*(p+i);

*(p+i)=*(p+n-1-i);

*(p+n-1-i)=temp;

}

}

int main()

{

int a[5]={1,2,3,4,5};

int i=0;

 

ChangePosition(a,5);

 

for(;i<5;i++)

{

printf("%d\n",a[i]);

}

 

system("pause");

return 0;

}

下面再来做一题:

用指针方法对10个整数由小到大排序,这个也就是说,用冒泡排序法对一个数组排序。

每次排序找出一个最大数放到上面。

void BubbleSort(int *p,int n)

{

int i,j,temp;

for(i=0;i<n-1;i++)

{

for(j=0;j<n-1-i;j++)

{

//前一个数大于后一个数

if(*(p+j)>*(p+j+1))

{

temp=*(p+j);

*(p+j)=*(p+j+1);

*(p+j+1)=temp;

}

}

}

}

 

下面简单来说一下指针。void *可以保存任何类型的指针地址。但是当你取的时候,必须把地址的类型转换一下才可以。

下面来说一下双指针,也就是指向指针的指针。用来保存指针的地址。指针本身也会在内存空间里面开辟一个空间。也就是我们常说的二级指针。看一下下面的代码应该就豁然开朗了。

double num;

double* pn=#

double** ppn=&pn;

**ppn这样也可以取出来num的数据。

 

下面来说一下不常见的realloccalloc的使用。

calloc(“要分配多少个数据”,”每个数据占用多大的内存空间”),返回的也是void *类型的指针,注意数据类型转换。

realloc()当我们已经分配好了这一篇内存之后,我们突然,分配的这一片内存不够用了。这个时候,我们就要用到realloc,它会返回被分配内存的指针。返回的这个指针的指向有可能与原数据一样,也有可能不一样,因为我们开始分配的内存后面可能残有其他应用程序的垃圾数据,所以我们不可能就在这个下面进行分配内存。

来说一下realloc的参数

realloc(“原地址指针可能是用malloc分配的内存地址指针”,要增加截个数据,每一个数据大小按照原来数据类型解析)

下面我们看一段代码:

如果写代码的时候,出现没有声明表示符,那么就是说明你没有把变量定义在块语句的开头,重新定义一下就好了。

//这里就是说一下reallocmalloc分配内存的问题

int main()

{

int num;//我们的一开始分配的内存有多少个数据

int newNum;//这个的意思是重新分配多大的内存空间

int i;

int* p;

int* newp;

scanf("%d",&num);

p=(int *)malloc(sizeof(int)*num);//分配相应数据类型的内存空间

//赋值并且初始化这一片内存空间

for(i=0;i<num;i++)

{

*(p+i)=i+1;

printf("%d,%x\n",*(p+i),p+i);

}

printf("---------------\n");

//现在我们发现上面的空间不够用了,用realloc来增加

scanf("%d",&newNum);//我们输入增加多少

newp=(int *)realloc(p,newNum);//分配的空间都要重新来定义它的数据类型

if(NULL==newp)

{

printf("内存分配失败\n");

}

else

{

printf("内存分配成功,地址是:%x\n",newp);

}

system("pause");

return 0;

}

 

注意上面分配了内存,我们必须要用free来释放这片内存空间,下面来说一下free注意的一点。

注意一片堆上的内存只可以释放一次,只有NULL指针才可以被释放两次。

下面,我们再说一下这个问题。

内存释放以后,指针的值不会发生变化。也就是说取指针的值一样可以取出来。

软件工程规范,内存释放以后,必须让指针的值等于NULL,也就是说,释放了内存,就让这个指针变成空指针。如果指针在内存中,没有及时的得到释放,那么就会造成内存泄露的问题。所以,分配了内存就要及时释放。

 

下面来说一个小知识,32位与64位的差别以及debugrelease的区别。

32位 地址是32位的

64位 地址是64位的

服务器编程都是64位的

数据类型的长度往往与编译器有关。

debug:调试程序,文件很大,附加了很多的调试信息,所以文件大。

release:代码都是经过优化的。去掉了很多调试信息,所以文件较小。

 

简单来说一下运算符优先级,运算符只有与变量连接在一起的时候,才会有优先级。

java是没有办法写外挂的,没有办法与cpu进行直接交互。

edu 教育机构

gov 政府

mil 美国军事网

net 网络

org 其他组织机构

0 0
原创粉丝点击