一、概览
一.常量
1.字面常量 2.const修饰的常变量不可被改变 定义数组的大小不能用变量。int arr[n] (?) n如果被const修饰,本质上还是一个变量,不能用来指定数组大小。 3.#define定义的标识常量 是预处理指令,不是关键字 #define MAX 100;MAX不可改 4.枚举常量
二、字符串和字符
1.双引号内的是字符串,单引号内的是字符。字符串末尾隐藏一个\0,是字符串结束的标志。 2.用单引号引起的一个字符实际上代表一个整数,整数值对应于该字符在编译器采用的字符集中的序列值。而一般我们的编译器采用的都是ASCII字符集。因此’s’的含义其实和十进制数115的含义是一致的。 而用双引号引起的字符串,代表的是一个指向无名数组起始字符的指针。 3.在计算字符串长度时\0是结束标志,不算作字符串内容。 4.库函数strlen - string length - 求字符串长度。<string.h> 5.字符格式printf(“%c\n”,’a’) 字符串格式printf(“%s\n”,”a”) 6.printf(“%c\n”,’\x51’); 51是十六进制数,打印出转换成十进制的ascii码。printf(“%c\n”,’162’);162是八进制数。。。 7.0是数字0。\0是一个转译字符,ascii值是0。 ‘0’是字符0,ascii值是48, ”abc0de”中的0也是字符0。 例:printf(“%d\n”,strlen(“c:\test\328\test.c”)); //14. \t和\32是转译字符,8不属于八进制数所以不是。
三.数组
1.int arr[10]={1,2,3,4,5};//不完全初始化,剩下默认初始化为0 2.int i=0.循环.arr[i]是数组元素,i是变量 3.arr是数组名,arr[10]表示下标为10的元素 4.计算数组元素的个数: int sz=sizeof(arr) / sizeof(arr[0]); sizeof(arr)计算的是数组的总大小,单位是字节 - 48. sizeof()计算括号内所占空间的大小(单位为字节)
四.操作符
- / 取商 % 取模(余)
2.a<<1 左移操作符. 左移一位 (移动的是二进制位) 00000000000000000000000000001100👉 00000000000000000000000000011000 3.&按位与 |按位或 ^按位异或 ~ 按位取反 4.float score=0.0; score=95.4;//直接写出来的浮点数默认是double类型的。95.4f 指定为float类型 计算平均成绩并保留一位小数: #include<stdio.h> int main() { int a,b,c,d,e; float f; scanf("%d %d %d %d %d\n",&a,&b,&c,&d,&e); f=(a+b+c+d+e)/5.0; printf("%.1f\n",f); return 0; } 5.sizeof是操作符,不是函数 6.整数在二进制中存放的是补码 int a=0; printf(“%d\n”,~a); //-1 7.int a=10; int b=a++; //后置++,先使用,再++ 8.&&逻辑与 ||逻辑或 条件操作符 exp1?exp2:exp3 max=(a>b)?(a):(b) 逗号表达式:从左向右依次计算,整个表达式的结果使最后一个表达式的结果
五.关键字
1.typedef 给类型重命名 extern int g_val 声明外部变量 函数 2.static-静态:(1).修饰局部变量:使得局部变量出了自己的范围也不销毁,其实是改变了局部变量的生命周期。但作用域还是局部的。 (2).修饰全局变量:全局变量本身是具有外部链接属性,但是static修饰全局变量,会使得全局变量失去外部链接属性,变成内部链接属性。所以static修饰的全局变量,只能在自己所在的.c文件中使用。 (3).修饰函数:与(2).相似 3.宏–#define可以定义宏 #define ADD(X,Y) ((X)+(Y)) 宏是替换式的工作,替换在预编译阶段完成。
六. 指针
1.内存.1个内存单元大小是1个字节. 在计算机中:32位-32根地址线 地址线-1/0 2.%p - 地址 - 地址的16进制表示形式 int a=10;//&a时,取出的是a所占内存中4个字节中第一个字节的地址 int* pa=&a; //pa是指针变量,存放a的地址 *pa=20; //*解引用操作符,通过地址找到内存空间。a被改成了20 3. 32位环境下,指针大小是4个字节(因为地址所占空间大小是4个字节)。64位环境下,指针大小是8个字节。
七、结构体
二、分支语句和循环语句
一.if 1.else和离得最近的可以匹配的if匹配 二.switch 1.switch(整型表达式) case 整型常量表达式: break 嵌套中,只跳出一个循环 default 三.while循环 1.break:停止后期的所有循环 continue:终止本次循环,continue后边的代码不执行,直接跳转到while的判断部分。进行下一次循环的入口判断。 四.for循环 k=0为假,不循环
五.do while循环 1.至少能被循环一次 2.break continue与前两个循环作用相同 3.Sleep(1000) <windows.h> system(“pause”);//执行系统命令的-清空屏幕 4.数组名本来就是地址,在scanf里不用取地址& 5.两个字符串的比较不能用==,用strcmp() if(strcmp(password,“123456”)==0) 5.获得时间戳:time( ); <time.h> srand((unsigned int)time(NULL)); 设置一个随机的起点 <stdlib.h> int ret=rand( )%100; //生成0~99间的随机数 6.cmd 命令窗口 倒计时关机 system(“shutdown -s -t 60”); 取消关机system(“shutdown -a”);
三、函数
1.嵌套调用:函数可以嵌套调用,不能嵌套定义
2.链式访问: printf函数返回的是打印在屏幕上的字符的个数。如果发生错误,将返回负数。 printf(“%d”,43);//打印43,返回2 printf(“%d”,printf(“%d”,43))=printf(“%d”,2); //432
3.让头文件只被包含一次: (1)#ifndef #define
#endif (2)#program
4.递归 函数递归的几个经典题目: (1)汉诺塔问题 (2)青蛙跳台阶问题
5.字符指针+1 - 向后跳1个字节 char *p ; p+1;向后跳1个字节 整型指针+1 - 向后跳4个字节 int *p ; p+1;向后跳4个字节
6.putchar读取一个字符 getchar是读取字符(只读取一个字符) 返回类型是int 1.getchar既然返回的是字符,其实返回的是字符的ascii码值(整数) 2.getchar在读取结束或失败时,会返回EOF EOF -end of file -> 返回-1 getchar清除缓冲区中的\n 但scanf读到空格就不读了,也不读\n,getchar清除缓冲区内空格之后的内容(直到读到\n),也可用变量存储getchar读取的内容 gets()函数直接读取一行
多组输入: while(scanf(%d %d %d”,&a,&b,&c)!=EOF) while(~scanf(%d %d %d”,&a,&b,&c))也可 因为scanf返回EOF, EOF=-1,在二进制中为 11111111111111111111111111111111 按位取反~ 00000000000000000000000000000000 就是0
函数必须保证先声明后使用,声明后函数的定义可以放在随意位置
四、数组
数组名单独放在sizeof内部的时候,如sizeof(arr),这里的arr表示整个数组,不是首元素的地址。但arr传参传给函数时,会降级变成数组首元素地址,所以sz如果放在函数内求会求出1
数组名就是首元素的地址 有两个例外:1.sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小 2.&数组名,这里的数组名表示整个数组,取出的是数组的地址
&arr[0]和&arr虽然结果相同,但&arr[0]+1是跳过一个元素,&arr+1是跳过整个数组
字符3减去字符0就是数字3
arr[4] —> *(arr+4) —> *(4+arr) —> 4[arr] 方块只是操作符 printf(“%d\n”,arr[4]); printf(“%d\n”,4[arr]);
strlen返回的是无符号整型 unsigned int printf(“%u\n”,strlen(“abc”));
字符串拷贝 - strcpy - 库函数 - string.h strcpy(b.name,”数据结构”);
五、操作符
1.对于除号,两边都是整数,执行的整数除法 操作数中有浮点数,才执行浮点数除法 2.整数在内存中存储的是二进制的补码 3.左移操作符:左边丢弃,右边补0,移动补码,打印的是原码。 右移操作符:逻辑右移:右边丢弃,左边补0 算数右移:右边丢弃,左边补原符号位。 大部分编译器为算数右移 不能移位负数值(int a>>-1),标准未定义行为 不适用于浮点数(float a>>1) 负数的反码:符号位不变其余取反 异或^:相同为0,相异为1 4.算一个数二进制中有几个1 int a=15; a&1;//按位与 a=a>>1; 循环 5.不创建临时产变量交换a,b的值 a=a+b; b=a-b; a=a-b; 但有可能溢出
异或:a=a^b; b=a^b; a=a^b;可读性不好,且只适用于整型 //b=abb=a^0=a 异或是支持交换律的 aab=b;aba=b aa=0;0a=a 左值是地址所指向的空间,右值是空间里的内容
6.sizeof是操作符,不是函数 计算变量或者类型创建变量的大小,和内存中存放什么数据没有关系 char arr[10]=“abc”; printf(“%d\n”,sizeof(arr));//10 printf(“%d\n”,strlen(arr));//3 字符串的长度,关注的内存中是否有\0.计算的是\0之前出现的字符个数 sizeof内部表达式不参与运算 7.~:把一个数的二进制位按位取反 8.逻辑与&& 若左边为假,右边不需要算 逻辑或|| 若左边为真,右边不用算
9.隐式类型转换 C的整型算数运算总是至少以缺省整型类型的精度来进行的。 为了获得这个精度,表达式中的字符和短整型操作数在使用之前被转换为普通整型,这种转换被称为整型提升。 整型提升是按照变量的数据类型的符号来提升的 负数的整型提升: char c1=-1; 变量c1的二进制为(补码)中只有8个比特位:11111111 因为char为有符号的char,整型提升时,高位补充符号位,即为1,所以提升后的结果是: 11111111111111111111111111111111 无符号整型提升,高位补0
10.算数转换
六、结构体
1.结构体变量创建好后才占内存空间 2.结构体传参 首选print2函数 函数传参时,参数是需要压栈的。如果传递一个结构体对象的时候,结构体过大,参数压栈的系统开销比较大,所以会导致性能的下降 结构体传参时,要传结构体的地址
七、指针
1.地址指向了一个确定的内存空间,所以地址被形象地称为指针。 指针就是变量,用来存放地址的变量 一个小的单元是一个字节byte
2.指针类型的意义 (1)指针类型决定了指针解引用的时候,一次访问几个字节(访问内存的大小)。 char* 指针解引用访问1个字节 int* 指针解引用访问4个字节 (2)指针类型决定了指针±整数的时候的步长(指针±整数的时候,跳过几个字节)
3.野指针:就是指针指向的位置是不可知的 (1)指针未初始化—里边放的是随机值 通过p中存的随机值作为地址,找到一个空间,这个空间不属于我们当前的程序,就造成了非法访问,就是野指针 (2)指针越界访问 (3)指针指向的空间释放 避免野指针: 1.初始化指针 不知道一个指针当前指向哪里时,可以初始化为NULL 2.小心指针越界 3.指针指向空间释放即时置NULL 4.避免返回局部变量的地址 5.指针使用之前检查有效性 或用assert
4.指针的运算: (1).指针±整数 (2).指针-指针.前提是两个指针指向同一个区域(如同一个数组) (3).指针的关系运算.标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但不允许与指向第一个元素之前的那个内存位置的指针进行比较。
5.数组名表示数组首元素的地址 但有两个例外: 1.sizeof(数组名) — 这里的数组名不是首元素的地址,是表示整个数组的,这里计算的是整个数组的大小,单位还是字节 2.&数组名—这里的数组名不是首元素的地址,是表示整个数组的,拿到的是这个数组的地址。
6.例题: (1)
#include<stdio.h>
int i;
int main()
{
i--;
if (i > sizeof(i))
{
printf(">\n");
}
else
{
printf("<\n");
}
return 0;
}
C语言中,0为假,非0即为真。全局变量没有给初始值时,编译器会默认将其初始化为0.i的初始值为0,i–结果为-1;i为整型,sizeof(i)求i类型大小是4,但sizeof的返回值类型实际为无符号整型,因此编译器会自动将左侧i转换为无符号整型的数据,-1对应的无符号整型是一个非常大的数字,应输出>
八、实用调试
Debug:调试版本,可以调试 Release:发布版本,用户可以使用的,不能调试。往往进行了各种优化,使得程序在代码大小和运行速度上都是最优的。
F5:启动调试,和F9配合使用 F9:打断点。右击红点,点条件,可以在指定条件断点。 如果打两个断点,想从第一个断点跳到第二个断点,只能取消第一个断点。否则继续执行,遇到第一个断点还是会停下。 F10:逐过程。一个过程可以是一次函数调用,或是一条语句。 F11:逐语句,每次都执行一条语句,但可以使我们的执行逻辑进入函数内部 Ctrl+F5:开始执行不调试
该代码死循环: i变量在后创建时,不会死循环但会报错,因为i的地址会在低地址处常见的coding技巧: 1.使用assert 2.尽量使用const 3.养成良好的编码风格 4.添加必要的注释 5.避免编码的陷阱 const int num=10;//num是常变量 num=20;会报错
const修饰指针: 1.const放在*p的左边:修饰的是指针指向的内容(p),指针指向的内容不能通过指针来改变了。但是指针变量本身§是可以改变的。const int p=# 或int const p=# 针对的是p p=20;会报错 2.const放在p的右边:int * const p=&num;针对的是p p=&n;会报错
|