程序执行四个步骤:预处理;编译;汇编;链接;宏:(如:#define N 6)发生在预处理阶段的单纯的字符串的替换;解决反复使用到的常量;用宏定义 方便修改;在宏中没有变量常量、语法,只是单纯的替换;宏的传参,是单纯的替换;一定要把后边定义的变量都加上括号();
malloc():帮助我们开辟内存空间;传进要开多少字节的空间;开的空间会存在堆段;(main函数在栈空间;)其返回值为void,表示泛型指针;泛型指针可以返回给任何类型的指针变量;size_t:usigd long;无符号长整型;堆空间使用完毕,需要释放;用free()函数;如果不释放,程序没有结束的话,堆空间里的函数始终不会消失;必须要释放;free();释放函数;exit()函数是终止程序进程;perror("报错")//报错栈空间:静态内存分配;//编译时决定的;堆空间:动态内存分配;//运行时决定的;动态内存:大小 运行时决定;程序员分配的;堆段;手动释放;静态内存:大小 编译时决定;编译器分配的;栈段;只读数据段;自动释放
枚举:enum;用枚举中的成员代表0,1,2。。。。。可以给枚举赋一个初始值;赋值的这个成员后边的成员都会加1;前边那个成员值不会改变;
结构体:不同类型变量的集合;(数组:相同类型变量的结合)struct user{char username[20];char password[20];long long userID;//结构体成员;};struct user user1;//相当于int a;结构体指针:指向结构体变量的指针;结构体可以存储向学生信息这样复杂的内容;
【查看a的某一位】如果想求a的第n位(0位起)是1还是0?那么a&(1<<n)==1,则说明第n位是1,如果a&(1<<n)==0,这说明第n位是0;【将a的某位置成1】a = a|(1<<n);或者a|=1<<n;【将a的某位置成0】a& = ~(1<<n);【将a的某位取反】a^=1<<n;
位运算:针对一个变量的某一位的操作,称之为位操作;int a;4字节;32位;如:int a =5;0000 0000 0000 0000 0000 0000 0000 0101最低位是第0为;最高位是第31位;如果想把 第3为的0改成1,那么这就是位运算;为运算符:& 按位与 |按位或 ^按位异或 ~按位取反 <<左移 >>右移与运算:每一位进行与,一假即假5&3;0000 0000 0000 0000 0000 0000 0000 0101 5; 0000 0000 0000 0000 0000 0000 0000 0011 3;结果为:0000 0000 0000 0000 0000 0000 0000 0001 1;5&3 == 1;与的时候,用的是补码;-1的补码是全1;5&-1=5;或运算:每一位进行或运算;一真即真;5|3 == 7;异或运算:a^b;相同为假,不同为真;相同为0,不同为1;5^3 ==6; 按位取反:0为1,1为0;一个数的取反等于他的相反数减一;左移动 :每一位都向左移动一位,超出的舍去,末位补零;左移一位相当于乘以2;左移动2位,相当于乘以4;左移动n位相当于乘以2的n次方;右移:不同的系统右移动的结果不同;移除的部分舍去,正数首位补0;负数补1;mac5>>1 == 2正数右移动一位相当于除以2;右移动n位,相当于除以2的n次方;-1右移几位都是-1;
#include <string.h>strtok:(字符串分割函数)传入一个空间,传入一个字符串;只能分割可变的字符串;第一个 是要分割的字符串{前提是这个字符串一定是可变的,可更改的字符串;空间是可变的};第二个参数是分隔符;char *p=" hell wo de";//无法分割;char p[64]="i am so bad man";char *ret =strtok(p," ");//分隔符为空格;printf("%s",ret);//打印出 i//如果想继续分割后边的字符串 如 am//则再次调用strtok;//只不过传参要传空ret = strtok(NULL," ");//传空 表示 继续分割之前的字符串//传的不是空,表示分割新的字符串//原理是将分隔符换成'\0';如果被分割的不为可变,那么分隔符,将不能替换成尾零,程序会报错;完全分割;分割字符串中所有的字符;#include <stdio.h>#include <string.h>int main(){char but[] ="hello,welcome to qianfeng!";char *p=buf;while(1){p= strtok(p,", ");if(!p)break;printf("%s",p);p=NULL;}return 0;}//这样,它会以每一个逗号或者空格作为分隔符;//分割出来的字符串,不仅仅是可以打印出来,它也可保存到其他字符串数组中,可以再声明一个字符串数组: char * jie[5]={};int i;jie++ = p;
需要引入头文件:#include<string.h>strcpy:(完成一个字符串的拷贝)传参传入两个字符串;将后边的参数,拷贝到前边的参数中;(前边参数的空间必须足够大,否则不能全部拷贝进去)char *p= "helloworld"; char buf[64]={}; strcpy(buf,p);printf("%s",buf);//打印出:helloworld未初始化的指针不能赋值;(未指向空间)strcat:(表示字符串的拼接);char buf[64] = "hello";char *p="world!";strcat(buf,p);printf("%s\n",buf);//buf的空间必须足够容纳拼接之后的字符串;字符串拼接时,前边那一段的\0会去掉,中间不会有尾0;//打印出 helloworldstrstr(在一个字符串中,查找另一个字符串)传入两个参数,在前边参数中,查找后边参数;查到该位置后,输出后边的字符串;如果查找中有两个想查字符串,查找到的是第一次出现的位置;如果没有要查字符串;则返回空,什么也不打印;char *p="welcome to qianfeng!";char *q="to";ret = strstr(p,q);printf("%s",ret);//打印出 to qianfeng!
atoi 函数;传参 字符的地址(字符串首字符地址)碰到一个不是数字的字符就停下来,如果遇到负数,能识别出第一个负号,不能识别后边的符号;strcmp 字符串比较函数 ;比较字符串:传入两个字符串,参数1比较大,返回正数;参数2比较大 返回负数;相等则返回0;strcmp(参数1,参数2);比较的方法:比较首字符的ascll码;如果相同,则比较第二个字符......
当我们写"hello"时,就是告诉编译器,在只读数据段存在6个字符;只读数据段:在内存中调一个函数时,会存在一个栈内,但是字符串是存在于一个离栈很远的只读数据段中,专门存储只读数据;它和栈是并列关系;"hello"表达式的值,表示字符串第一个字符的地址;write(i,j,k)这个函数:代表将j,打印到终端上,打印k个字节;"hello",在只读数据段中,存储了6个字节的6个字符地址为h的一个字符串;只读数据段的数据不可修改;有效字符:不包括/0的字符串;查看有效字符用 strlen()这个函数来计算,打印用 %ld 来打印;严格上来讲应该用%u来打!strlen遇到/0就结束计算了;
复杂类型:由已知的拼凑出的类型;int *p[10];printf("%ld,%ld",sizeof(p),sizeof(int *p[10])); //80 , 80int *p[10]; //*和[]平级,又结合,从优先级上看,p离[]最近,所以说这是一个数组;它有10个元素,每个元素是指向整型变量的指针;int a,b,c;p[0]=&a;p[1]=&b;.......*p[0]=-1;p[0]就是指向a的指针,取*之后,就是a的值; int (*p)[10]printf("%ld,%ld\n",sizeof(p),sizeof(int (*)[10])); //8 ,8这是一个指针;指针都是8字节;它指向10个整型元素的数组;int **p;二级指针;这是一个指针,它指向另一种指针,该指针指向整型;二级指针可以改变指针的指向;int *p(int);这是一个函数,它的参数是一个整型,返回值是一个指向整型的指针;int (*p)(int);这是一个指针,它指向参数是int返回值是int的函数;这种指针称之为:函数指针;int((*p[10])(int))(int);这是一个数组,10个元素;每个元素是某种指针,这种指针指向函数,该函数参数,参数一个int,返回值是另一种指针,该指针指向参数int 返回值int的函数;这样一个数组(应该有80个字节;)因为它有10个元素;每个元素是指针;
a[5]={1,2,3,4,5};数组名a就代表a[0]的地址;传参数时,如果看到数组名a,那就代表a[0]的地址;a[0]==*(a+0)==*a;a[i]==*(a+i);取*之后,是可以改变main函数的值的;只要加上const便不可修改值;
要修改一个变量,传参传变量的地址;分清值传递和址传递;传参如同赋值;
const关键字修饰指针变量;char a='A';const char *p=&a;char const *q=&a;1.*星前const:表示p可以改变,*p不可以改变,因为const修饰*p;char b='B';p=&b;这时可以编译,完全没问题,p可以改变,但是*p不可以改变;如果再把*p='C';这时会报错;read-only只读;但是b还是可以改变的;b = 'C'//这样是完全可以的;a='D'//这样也是完全可以的;2.*后const;char a = 'W';char b;char * const p=&a;*p='X';这样是可以的;p = &b;这样就不行了;const char * const q = &a;这样无论是*q还是q均不可改变;修饰哪个哪个不可改变;修饰*P那么*P不可改变;修饰p,那么p不可改变;
指针通常和数组一起使用;//单独的数组名,如a相当于第一个元素的地址,即&a[0];数组名是数组的首元素地址。指针变量+1,其结果相当于加1个其指向的字节数;int *p=(int *)5;p++;最后p==9;//int类型占4个字节;char *q=(char *)5;q++;p==6; int a[10]={};int *p = a;int i;for(i=0;i<10;i++){scanf("%d",a[i]);//这里的a[i]可以换成p+i;因为p+1的地址就是a[1]的地址... 换成p+i,只是把每个元素的地址取出来,如果要取元素,就要写成*(p+i);}return 0;每次都用*(p+i);取值!特别麻烦,这时人们就就发明了一个代替+i取* 的运算符;就是中括号运算符;*(p+3)=0;代表p[3]=0;*(p+n)==p[n]; 数组中也一样适用:a[3]=9; 相当于*[&(a[0])+3]=9;即*(p+3)=9; int main(){int a[4]={1,3,4,5};printf("%d",a[3]);printf("%d",3[a]);//两者相等;因为中括号运算符;//a[3]==*(a+3);//3[a]==*(3+a);}指针是地址变量;数组名是地址常量;它就代表着a[0]的地址,这也就解释了,数组为什么不能整体赋值;int main(){int a[10]={1,2,3,4,5};int *p=a;a[1]=2;*(a+1)=2;*(p+1)=2;p[1]=2;}上边四个式子都一样,因为 方括号运算符本身就是+i取*
指针(pointer)是变量;int *p;p代表指针变量名;在64位系统下占8个字节;指针是用来装地址的;int a;int *p=&a;&是取地址符;把a的地址放到p里面;*p代表a; *代表地址的取值符;内存中的地址是以十六进制写的;这只是地址的编号,实际上要加地址偏移量;十六进制编号是为了方便阅读和编写;地址是常量,指针是变量;只有变量有地址; 常量不是空间,所以没有地址;
数组完全初始化:a[3]={1,2,3}没有被初始化的变量元素为零;数组部分初始化:a[10]={1,2,3}; int a[5]; 其中a是数组,不能说a[5]是数组;可以说,a是int 5类型的数组;数组不能整体赋值;int a[5];a[0]=1,a[1]=2;//这样写对int b[5];b=a;//这样写是错的,数组不能整体赋值;a[4],内存中是这样存储的:a[0]占4位地址,a[1]占4位地址,a[2]占4位地址,a[3]占4位地址!!!当我们访问数组某个元素的时候,内存会自动找到相应的4位,如果要打印,会把首位地址打印出来;首位地址代表该元素的地址;
最怪的循环:for循环;break;跳出;continue;在这里continue是跳过本次循环,进入下一次循环;这样的说法不严密,只是针对一种情况而言的; for(语句1;表达式;语句3){ 语句2;}语句1,判断表达式为真,执行语句2,然后执行语句3,判断表达式,执行语句2,执行语句3,判断表达式......在for循环 语句1不用的话,可以省略;如果语句3不用,也可省掉;如果把表达式省掉,它就恒为真,进入死循环;
goto 语句:跳转语句;int main(){pritnf("1");goto:ABC;pritnf("2");pritnf("3");pritnf("4");:ABC;pritnf("5");}switch语句;//逻辑选择语句switch(表达式){case 1: 语句1; break;case 2: 语句2; break;case 3: 语句3; break;default:printf(“表达式不匹配则打印到此”);}
在if中,如果if大括号里只有一个语句,那么这个大括号就可以省略;