一、c基础
1.1 一个函数遇到return语句就终止了
1.2 system系统调用:用命令打开计算器、记事本等,windows和linux下命令不同,需要头文件(stdlib.h)
1.3 gcc -o a a.c :-o为指定输出程序后缀,如果不加-o,gcc a.c windows下生成a.exe,linux下生成a.out
1.4 编译过程
1.5 -E预编译:gcc -E -o a.e a.c 预编译a.c文件,生成的目标文件名为a.e,预编译实际是把头文件解析,把include包含的文件替换,同时删除注释;
1.6 -S反汇编:gcc -S -o a.s a.e 把预编译后的a.e文件编译成汇编语言文件a.s
1.7 -c编译:将代码编译成二进制的机器指令
1.8 gcc没有任何参数,代表就是链接
1.9 用户模式:应用程序都是运行在用户区域;内核模式:操作系统的内核,设备驱动程序在内核模式下运行
1.10 RISC:精简指令集 CISC:复杂指令,一般x86构架的CPU都是复杂指令集
二、数据类型与运算符
2.1 #define MAX 10 定义一个宏常量,值为10,define定义的常量类型要大写
2.2 %x:代表输出16进制整数,%u用大写字母方式输出16进制数 ,%o:代表输出八进制整数,%p:代表输出内存的地址,%c代表输出一个字符
2.3 short:短整数,在32位系统下是两个字节,16个比特;
long:长整数,在32为系统下是4个字节,64为系统下windows还是4个字节,unix是8个字节;
int:不管32,64位或windows、unix都是四个字节
long long:是64位,8个字节,对于32位的操作系统,cpu寄存器是32位,所以计算long long类型的数据,效率很低
unsigned int:unsigned是关键字,代表无符号数的意思
2.4 计算一个整数的时候超过整数能够容纳的最大单位后,整数会溢出,溢出的结果是舍弃最高位
当一个小的整数赋值给一个大的整数,符号位不会丢失,会继承
2.5 对于arm、intel这种x86的构架的复杂指令cpu,整数在内存中是倒着存放的,低地址放低位,高地址放高位,叫小端对齐,
对于unix服务器的cpu,更多的是采用大端对齐方式存放整数
2.6 char的本质就是一个整数,一个只有1个字节大小的整数
2.7 char c; c=c+32; //将大写字母转化为小写字母的算法
c=c-'0'; //将字符转化为整数的算法
2.8 不可打印的char转义符:\a:报警;\b:退格;\n:换行;\r:回车;\t:制表符;\\:斜杠
2.9 char取值范围为-128到127,unsigned char取值范围为0到255
2.10 float在32位系统下是4个字节,double在32位下是8个字节
2.11 当一个浮点数赋值给一个整数时,小数点后的位数会被舍弃,如果想要四舍五入,可以把浮点数+0.5
2.12 volatile:告诉编译器不要优化
register int i;变量i不是在内存里面,而是在寄存器里面
2.13 qutchar是输出一个字符的函数
2.14 getchar();得到用户键盘的按键,一次只能接收一个,跟scanf相似
2.15 一个字节有8个bit
三.数组、字符串
3.1 数组在内存中是一段连续的空间,每个元素的类型都是一样的
3.2 如果scanf能容纳的长度小于用户在键盘输入的长度,那么scanf就会缓冲区溢出,导致程序崩溃
3.3 gets();认为回车是输入结束的标识,空格不是输入结束标识,scanf();认为空格和回车键都是输入结束标识;都存在缓冲区溢出问题。
——gets();不能用类似%s,%d之类的转义字符,只能接受字符串的输入
3.4 atoi(); 将字符串转换为int类型;atof();将字符串转化为float类型;atol();将字符串转化为long类型,需要头文件<stdlib.h>
3.5 fgets(s , 100 , stdin); 第一个参数是char数组,第二个参数是大小,单位:字节;第三个参数stdin代表标准输入;
——fgets是安全的,只要保证第二个参数小于等于数组的大小,就不存在缓冲区溢出,如果有溢出自动截取
3.6 puts(s);输出字符串,会在输出完成后打印回车
3.7 strlen(s); 得到字符串长度,返回不包含字符串结尾/0的字符串长度
3.8 strcat(s, s1);往字符串s后面追加s1字符串;也存在缓冲区溢出问题,要尽量保证s字符串容量
3.9 strncat(s , s1 ,6);往字符串s后面追加字符串s1的前6个字符
3.10 strcmp(s1,s2);比较s1和s2是否相等,如果返回值为0,代表两个字符串中内容相等
3.11 strncmp(s1,s2,5);比较s1和s2的前面5个字符是否相等,如果相等返回0
3.12 strcpy(s1,s2);将s2的内容拷贝到s1里面并覆盖s1
3.13 strncpy(s1,s2, 3);将s2的前三个字符拷贝到s1,并覆盖s1前三个字符
3.14 sprintf(s,"%d",i);将格式化(此例用%d格式化)后的字符串输出第一个参数指定的字符串中,print是将格式化结果输出到屏幕,sprintf是将格式化结果输出到字符串
3.15 sscanf();scanf是 ,sscanf是从指定格式化字符串读取输入
——char s[100]="abc=100";
——int i=0;
——sscanf(s, "abc=%d" , &i);
——printf("%d\n",i); 输出结果为100
sscanf()应用典型实例:
3.16 char * strchr(char* _Str , int _Ch);在参数_str中查找参数_ch指定字符,找到返回_ch在_str中所在位置,没找到返回null;
3.17 char * strstr(char * _str , const char * _Substr);在参数_str中查找参数_Substr指定子串,找到返回子串在_str中位置,没有找到返回null
3.18 strtok(s , “_”);字符串s中遇到_就分割成子串,需用循环将分割的字符串打印出来
——strcpy(s , "abc_123");
——buf = strtok(s , "_");
——while(buf)
——{
—— printf("%s\n",buf);
—— buf=strtok(NULL,"_");
——}
四.函数
1.如果一个函数没有标明返回值,那么返回的函数类型就是int
2 size_t 是一种变量类型:unsigned int;有些函数如果确定返回值不会小于0,就可以使用size_t
3 形参必须加数据类型;
4 要在main函数中调用一个函数,需要先声明函数;
五.指针
1.指针变量的值,一般不能直接赋值一个整数,而是通过取变量地址的方式赋值
2.*p是指针指向地址的内容
3. p是指针指向地址的内存地址
4. void *p;//无类型指针,意思是这只是一个指针变量,而不指向任何具体的数据类型
5. &取地址运算符,可以取得一个变量在内存中的地址
6. NULL在c语言中的定义为(void *)0
7. 指向NULL的指针叫空指针,没有指向任何变量地址的指针叫野指针
8. 只要是指针,就是固定大小,32位下4个字节,64位下8个字节
9. 指针之间赋值比普通类型检查严格,原则上不可把double *类型赋值给int*类型,不能把一种类型的指针指向另一种类型,如有指针指向其他类型,从低地址开始取值
10. 一个变量在内存中存放时变量前面的值在内存高地址,后面的值在内存低地址,指针指向从低地址开始,字符数组相同,指针首先指向数组的首端,与其他变量一样从低地址开始指向(具体可看c基础 指针与数组的关系)
11.不能把int型的指针指向float
12.const int *p 这个指针只能指向一个常量,例:int a=10;p可指向a,但不能改a的值,p也可以指向其他常量,区别 int const *p
13.int *const p 常量指针,一旦初始化后就不可以改变指向,但可以更改所指常量的值
14.*P指针与数组对应,如果遍历数组时一定要注意数组长度与p大小对应,不能让p指空
15. 指针的运算是指针指向的数据类型在内存中占用字节数做为倍数加减,
——char *p ;p++;移动了sizeof(char) 个字节,一个字节
——int *p;p++;移动了sizeof(int)个字节,四个字节
16. int buf[10];int *p1=&buf[1]; int *p2=&buf[3];
—— p2 - p1 =2; p1和p2都是int型相减得到的是两个数组元素在内存中的距离(8),因为是int型的(4个字节),所以结果为2
——char *p1=&buf[1]; char *p2= &buf[3] ; p1和p2是char型相减得到的也是两个数组元素在内存中的距离(8),因为是char型的(1个字节),所以结果为8
——long long *p1=&buf[1] ; long long *p2=&buf[3] ; p1和p2是long long型相减得到的也是两个数组元素在内存中的距离(8),因为是long long型的(8个字节),所以结果为1
——short *p1=&buf[1]; short *p2= &buf[3] ; p1和p2是short型相减得到的也是两个数组元素在内存中的距离(8),因为是short型的(1个字节),所以结果为4
—— (int)p1 - (int)p2=8; 类型转化后相减得到内存中两个指针的距离,int字节为4,相减为8
——(int)p1+3; 加了int类型后加3是指针在内存地址的加法,单纯的加3,假如原先内存为:320680,加3后为320683
——p1+=3;不加类型是指针指向的数据类型在内存中的加法,p1为int型,加3后指针在内存中移动了12个字节
17.关闭安全检查:#pragma warning (disable:4700);
18.下面例子说明当一个指针指向一个整数后,从低地址位开始取,指针的运算让指针指向整数的不同位置
int main(){
int a = 0x1234;
char *p = &a;
char *p1 = p++;
printf("%x\n", *p); //34
printf("%x\n", *p1); //12
return 0;
}
19.指针指向数组时,也可以这样写:
int main(){
int buf[10] = { 1,3,56,7,7,8,4,5,4,6};
int *p = buf;
int *p1 = buf + 2;
printf("%d\n", *p); //1
printf("%d\n", *p1); //56
return 0;
}
20.下面两种指针写法效果一样:
int main(){
int buf[10] = { 1,3,56,7,7,8,4,5,4,6};
char *p = &buf;
printf("%d,%d,%d,%d\n", *p,*(p+1),*(p+2),*(p+3));
printf("%d,%d,%d,%d\n", p, p[1], p[2], p[3]); //c语言允许指针通过数组下标的方法访问数组成员
return 0;
}
21.注意p[ n ] 修改的是谁的值
int *p = buf[1];
P++;
P++;
p[2] = 100; //经过两轮++后p已经指向buf[3],此时p[2]是在p指向buf[3]的基础上+2,因此指向了buf[5],修改的是buf[5]的值
22. 定义指针数组不管什么类型的,占用字节大小都相等,int *a[10];char *b[10];
——sizeof(a)=40,sizeof(b)=40,都等于40,sizeof(a[1])=4,sizeof(b[1])=4
23.二级指针
int main(){
int a = 10;
int *p = &a;
int **pp = &p; //定义了一个二级指针,指向一个一级指针
**pp = 100; //通过二级指针修改内存的值,将a改成了100
*pp = 10; //相当于将p指向了编号为10的这块内存,pp还是正常的指针,但p被修改成了野指针
printf("%d\n", a); //100
return 0;
}
24.多级指针指向原理
25.多级指针对应内存图解:***ppp代表了int a所在的内存快,**pp代表了int *p所在的内存快,*ppp代表了int **pp所在的内存块,ppp代表了***ppp所在的内存块
26.指向二维数组的指针
1.1.1 int a[3][5]
a | 二维数组名称,数组首地址 |
a[0], *(a + 0), *a | 0行,0列元素地址 |
a + 1 | 第1行首地址 |
a[1], *(a + 1) | 第1行,0列元素地址 |
a[1] + 2, *(a + 1) + 2, &a[1][2] | 第1行,2列元素地址 |
*(a[1] + 2), *(*(a + 1) + 2), a[1][2] | 第1行,2列元素的值 |
27.数组指针:
int main(){
int buf[2][3] = { {1,2,3},{4,5,6} };
//int *p[3]; //指针数组
int (*p)[3]; //定义了一个指针,指向int[3]这种数据类型,指向二维数组的指针
p = buf; //p指向了二维数组中的第一行
p++; //指向了第二行,在内存中位移了1*sizeof(int [3])=12个字节
int i; int j;
for(i=0;i<2;i++){
for(j=0;j<3;j++){
printf("%d\n", *(*(p + i) + j));
printf("%d\n", p[i][j]); //两种写法相同,都能输出相同的结果
}
}
return 0;
}
28.指针变量作为函数的参数:当一个实参传递给一个函数作为形参用时,这个函数不会改变实参的值,但是如果这个函数的形参接收的是指针类型,函数就会改变形参的值
void change(int a, int b) {
int temp = a;
a = b;
b = temp;
}
void s_change(int *a, int *b) {
int temp = *a;
*a = *b;
*b = temp;
}
int main(){
int a = 10;
int b = 20;
change(a, b);
printf("%d,%d\n", a, b); //输出值为10,20,调用change函数并未改变a,b的值
s_change(&a, &b);
printf("%d,%d\n", a, b); //输出值为20,10,值改变,调用函数时传递的是地址,传递时用&
return 0;
}
29.一维数组名作为函数参数,c语言将函数名解释为指针,下例可以看出被调用函数接收的参数只有4个字节大小,表明传递的是地址,参数的数组名也代表数组的第一个参数,代表数组的首地址,数组名也就是一个指针变量,可以进行++--操作
void array(int *buf){
printf("%d\n", sizeof(buf)); //输出结果为4
}
int main(){
int buf[10] = {};
array(buf);
printf("%d\n", sizeof(buf)); //输出结果为40
return 0;
}
30.一维数组名作为函数参数,这个函数的第一个参数是数组名,第二个参数可以是数组的维度,告诉这个函数这个数组长度
31.将二维数组作为参数传递给一个函数:
void two_level(int (*p)[3],int a,int b) //第一个参数为二维数组,第二三个参数为数组维度
{
int i;
int j;
for(i=0;i<a;i++){
for(j=0;j<b;j++){
printf("p[%d][%d]=%d\n", i, j, p[i][j]);
}
}
}
32.当数组作为参数传递不知道数组维度时,一维数组可以用:sizeof(buf)/sizeof(int)得到维度,二维数组可以用:sizeof(buf)/sizeof(buf[0])得到第一个维度,sizeof(buf[0])/sizeof(int)得到第二个维度
33.二维数组作为参数时可以不指定第一个参数,所以也可以写成:
void two_level(int [][3],int a,int b){};
与这种方式等效:
void two_level(int (*p)[3],int a,int b){};
34.一维数组作为参数传递写法有:int buf[];int buf;int *buf;效果相同
35.数组作为函数的参数,当不需要改变数组的内容时,可以加const修饰,const int buf;
36.void *p(int , char *); //声明了一个函数,函数的名字叫p,函数的返回值是void *,函数的参数分别是:int 和char *
void (*p)(int ,char *); //定义了一个指向参数为int和char *,返回值为void的函数指针
int *(*p)(int*); //定义了一个参数为int *,返回值为int *的指向函数的指针
void(*p)(); //定义一个指向没有参数,没有返回值的指向函数的指针
37.三个函数:
memset(buf, 0, sizeof(buf)); 第一个参数是要设置的内存地址,第二个是要设置的值,第三个是内存大小(将一块内存初始化为0最常用的方法
memcpy(buf2, buf1, sizeof(buf1)); 将buf1的内存内容全部拷贝到buf2,,拷贝大小为第三个参数
memmove(buf2, buf1, sizeof(buf1)); 将buf1的内存内容全部拷贝到buf2,,拷贝大小为第三个参数
38.指针小结
定义 | 说明 |
Int i | 定义整形变量 |
int *p | 定义一个指向int的指针变量 |
Int a[10] | 定义一个int数组 |
Int *p[10] | 定义一个指针数组,其中每个数组元素指向一个int型变量的地址 |
Int (*p)[3] | 定义一个数组指针,数组名为p,指向int [3]这种数据类型,数组指针大多用在指向一个二维数组 int buf[2][3] ; 数组指针的长度取决于第二个维度(后面一个) |
Int *func() | 定义一个函数,返回值为int *型 |
Int (*p)() | 定义一个指向函数的指针,函数的原型为无参数,返回值为int |
Int **p | 定义一个指向int的指针的指针,二级指针 |
39.指针数组做为main函数的参数:int main(int argc,char *args[]){}
argc代表程序执行的时候有几个参数,程序名本身就是一个参数,所以argc最小为1
args[] 是一个指针数组,其中每个成员的类型是char *,argc就是告诉函数这个数组有多长
运用示例:当给args传递参数如:/c时,程序就能输出c盘下目录
int main(int argc, char *args[]){
char buf[100] = "dir";
int i;
for(i=0;i<argc;i++){
strcat(buf, " ");
strcut(buf, args[i]);
}
system(buf);
return 0;
}
运用示例:用户输入两个数返回其和
int main(int argc, char *args[]) {
if(argc<3){
printf("参数不足,程序退出");
}
int a = atoi(args[1]); //从1之后的参数都是用户输入的参数,第一个参数是main程序名本身
int b = atoi(args[2]);
printf("%d\n", a + b);
return 0;
}
40.指针函数:
void test(){
}
int main(){
void(*p)(int *); //定义了一个指向函数的的指针
p = test;
int a = 10;
p(&a); //通过指针间接的调用了test函数
return 0;
}
41.指针总结:
int array[100]={1,2,3,4,5,6}; int *p=&array和int *p=array相同,所以可以不用&
*p =100; 将100赋值给p所指向的地址;
p=100;将100赋值给变量p,相当于将内存+100,这种写法是错误的
p++;移动了int个字节的内存大小
int i=300;p=&i;*p=200;整数赋值加&,地址赋给地址,值赋给值,p是地址,*p是值
野指针:int *p;*p=10;错误,不能给野指针赋值,指针应该指向一个内存地址
指针数组与数组指针:int *s[10];指针数组; int(*s1)[10]; 数组指针
sizeof(s)=40; sizeof(s1)=4; 由此可以看出s是一个数组,s1是一个指针类型的变量,指向int [10]这么大的一种数据类型
int *p=array;这种类型的指针适合指向一维数组
int (*p)[10]=array; 这种类型的指针适合指向二维数组
int array[3][4]={};sizeof(array[2])=16; array[2]代表了第二行所以元素,,四个元素16个字节
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)