这里先来说一下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’字符A用sizeof检测出来是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得位,乘10得10
#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的数据。
下面来说一下不常见的realloc与calloc的使用。
calloc(“要分配多少个数据”,”每个数据占用多大的内存空间”),返回的也是void *类型的指针,注意数据类型转换。
realloc()当我们已经分配好了这一篇内存之后,我们突然,分配的这一片内存不够用了。这个时候,我们就要用到realloc,它会返回被分配内存的指针。返回的这个指针的指向有可能与原数据一样,也有可能不一样,因为我们开始分配的内存后面可能残有其他应用程序的垃圾数据,所以我们不可能就在这个下面进行分配内存。
来说一下realloc的参数
realloc(“原地址指针可能是用malloc分配的内存地址指针”,要增加截个数据,每一个数据大小按照原来数据类型解析)
下面我们看一段代码:
如果写代码的时候,出现没有声明表示符,那么就是说明你没有把变量定义在块语句的开头,重新定义一下就好了。
//这里就是说一下realloc与malloc分配内存的问题
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位的差别以及debug与release的区别。
32位 地址是32位的
64位 地址是64位的
服务器编程都是64位的
数据类型的长度往往与编译器有关。
debug:调试程序,文件很大,附加了很多的调试信息,所以文件大。
release:代码都是经过优化的。去掉了很多调试信息,所以文件较小。
简单来说一下运算符优先级,运算符只有与变量连接在一起的时候,才会有优先级。
java是没有办法写外挂的,没有办法与cpu进行直接交互。
edu 教育机构
gov 政府
mil 美国军事网
net 网络
org 其他组织机构