C语言基础
原码、反码、补码(只跟负数相关)
正数的原码、反码、补码都相同(也就是说原码、反码、补码只跟负数相关) 原码:把数字转换成二进制 反码:除最高位,其他位依次取反 补码:反码+1注意:计算机中使用补码来表示数据的!!!(由于正数的原码、反码、补码相同,所以在计算机中显示的二进制(补码)和我们通过计算把数字转码成二进制(原码)相同)
java基本数据类型
byte:1 short:2 int:4 long:8 float:4 double:8 char:2 boolean:1c基本数据类型(可通过sizeof函数查看大小)
short:2 int:4 long:4 float:4 double:8 char:1 注:long long 占8个字节,但是并不属于基本数据类型有符号(signed)、无符号(unsigned)
int类型,占4个字节(一个字节由八个二进制组成),32位 int类型能表示 2^32 个数字 无符号int类型,取值范围是:0 ~ (2^32 - 1)所有32位都表示数值位 有符号int类型,取值范围是:-2^31 ~ (2^31 - 1)
最高位为符号位,剩下31位作为数值位 当最高位为0时,剩下的31位都表示一个正数 当最高位为1时,剩下的31位都表示一个负数 所以int能表示的最大值是 (2^32 -1) signed:有符号,最高位表示符号位 unsigned:无符号,最高位表示数值位
在中没有字符串,只能使用字符数组来表示字符串;每个字符数组最后面都有一个结束符\0,所以字符数组的长度都+1;
char c[] = "你好啊hello";
输入输出函数
%d - int %ld – long int %lld - long long %hd – 短整型 %c - char %f - float %lf – double %u – 无符号数 %x – 十六进制输出 int 或者long int 或者short int %o - 八进制输出 %s – 字符串
c语言中数组不检测越界
指针
内存地址
是用十六进制的数字来表示内存地址的 内存中的每一个字节都会有一个地址,地址相当于内存的门牌号
32位的地址总线长度是32位的,也就是说系统能分配给内存地址的数量是 2的32次方 个
为什么32位系统最大只支持4g内存?
因为:系统最多能分配给内存地址的数量是:2^32 个,而每个地址对应一个字节,也就是总共 2^32 b(字节),2^32/1024/1024/1024 = 4g
内存修改器:找到变量所在的地址,然后修改地址上的值
指针变量是用来存储地址的
一级指针
int* p;// 定义一个 int* 型的指针变量,这个变量只能保存地址,而且保存的地址上存放的数据必须是一个int型(因为:如果你存放其他数据类型,那么你取那个指针存放的的值时,会取不到值) int i = 3; p = &i;// 将整数3的地址存放到指针p中 *p:引用这个指针所指向的地址上保存的数据
二级指针
int** q;// 定义一个二级指针,二级指针保存的是一级指针的地址
指针如果没有赋值,那么就不能使用,此时指针指向的地址是一个随机值,这种指针叫野指针
值传递和引用传递
引用传递,本质上也是值传递,只不过传递的这个值是一个地址值
一个函数返回多个值
由于c语言是面向过程的,java是面向对象的,在java中,如果想要让一个函数返回多个值的话,可以将函数中要返回的数据封装成对象,然后就可以返回多个值了。 而在c语言中,一个函数只能返回一个值,
#include #include void function(int* p,int* q){ *p += 5; *q += 5; } main(){ int i = 3; int j = 5; function(&i,&j); printf("%d\n",i);// 8 直接修改内存地址保存的数据值 printf("%d\n",j);// 10 system("pause"); }
子函数获取主函数变量的地址
#include #include void function(int* p){ printf("%#x\n",p);// 0x28ff44 } main(){ int i = 3; function(&i); printf("%#x\n",&i);// 0x28ff44 system("pause"); }
主函数获取子函数变量的地址
#include #include void function(int** p){ int i = 3; *p = &i; printf("i的地址为%#x\n",&i);// 0x28ff44 } main(){ int* mainp; function(&mainp); printf("主函数获取到i的地址为:%#x\n",mainp);// 0x28ff14,这里打印的是mainp的值,而mainp是一个指针,所以它的值是一个地址 printf("主函数获取到i的值为:%d\n",*mainp);//这里获取不到正确的值,因为函数中的变量,在函数结束之后会被销毁。但是当注释掉前面两个打印语句之后,可以打印出3这个值,这是因为那个函数中的变量还没来得及销毁,所以你取出的值是一个"幻值",这种做法是不正确的 system("pause"); }
int i = 3; 那么i的值就是3;int* p = &i; p的值是一个地址值(i的地址)
数组
数组每一个元素的地址都是连续的,数组是一个连续的内存空间 数组名保存的其实是第0个元素的地址 通过首地址的运算,就可以拿到每一个数组中每一个元素
指针的运算
int i[] ={1,2,3}; int* p = &i; *p // 1 *(p+1) // 2 char* q = &i; *q // 1 *(q+4) // 2 注意:这里如果写成*(q+1)是错误的!!!因为数组中存储的是int型,每个元素占4个字节,指针应该向右位移4个字节才能拿到下一个元素
+1表示向右位移一个单位,一个单位是多少个字节,看指针类型而定;如果指针式int型的,那么位移一个单位是4个字节,而如果指针类型是char型的,那么位移一个单位是1个字节!
指针的长度
32位操作系统 指针长度4个字节,跟类型无关 64位操作系统 指针长度8个字节,跟类型无关 int* p和double* q的指针长度相同
堆和栈的区别:
(1) 在栈上分配的内存,叫静态内存
(2) 在堆上分配的内存,叫动态内存
区别:
申请方式
栈: 由系统自动分配,后进先出,由系统释放这个空间 堆: 需要程序员自己申请,并指明大小,在c中用malloc函数
如char* p1 = (char*) malloc(10); //14byte (虽然程序员申请了10个字节,但是由于p1是一个指针,指针的长度是4个字节,也就是占用4个字节,指针是保存在栈内存中,所以那句语句总共占用14个字节的空间)
但是注意p1本身是在栈中的
系统申请后的反应
栈:只要栈的剩余空间大于所申请空间,系统将为程序提供内存,否则将报异常提示栈溢出。 堆:首先应该知道操作系统有一个记录空闲内存地址的链表,当系统收到程序的申请时,会遍历该链表,寻找第一个空间大于所申请空间的堆结点,然后将该结点从空闲结点链表中删除,并将该结点的空间分配给程序,另外,对于大多数系统,会在这块内存空间中的首地址处记录本次分配的大小,这样,代码中的delete语句才能正确的释放本内存空间。另外,由于找到的堆结点的大小不一定正好等于申请的大小,系统会自动的将多余的那部分重新放入空闲链表中。
申请大小的限制
栈:在windows下,栈是向低地址扩展的数据结构,是一块连续的内存的区域。栈顶的地址和栈的最大容量是系统预先规定好的,在windows下,栈的大小是2m,如果申请的空间超过栈的剩余空间时,将提示overflow。因此,能从栈获得的空间较小。
堆:堆是向高地址扩展的数据结构,是不连续的内存区域。大小跟硬件有关
申请效率的比较
栈:由系统自动分配,速度较快。但程序员是无法控制的。 堆:由malloc/new分配的内存,一般速度比较慢,而且容易产生内存碎片,不过用起来最方便.
堆和栈中的存储内容
栈:函数的各个参数、局部变量等。注意静态变量是不入栈的。 堆:堆中的具体内容有程序员安排。
内存的回收
栈上分配的内存,编译器会自动收回 堆上分配的内存,要通过free来显式地收回,否则会造成内存泄漏。
堆内存申请
使用malloc函数申请堆内存
int* p = malloc(4 * 10);// 申请了40个字节大小的空间,并且申请的这40个空间是连续的,返回的是堆内存地址(用一个指针p接收)
释放申请的堆内存
free(p);
realloc函数的使用
#include #include main(){ printf("请输入班级人数:"); int count; scanf("%d", &count); int* p = malloc(count * sizeof(int)); int i; for(i = 1; i <= count; i++){ printf("请输入第%d个学生的学号:", i); scanf("%d", p + i - 1); } printf("请输入插班人数:"); int newcount; scanf("%d", &newcount); //现在原来旧的堆内存后面,扩展新的空间 //如果旧堆内存后面的空间已经被别的程序占用了,那么就无法扩展 //如果不能扩展,就会寻找一个块足够大的内存区域,申请新的堆内存 ,并且会把旧堆内存的数据复制到新的堆内存中 //释放旧的堆内存 p = realloc(p , (count + newcount) * sizeof(int)); for(i = count + 1; i <= count + newcount; i++){ printf("请输入第%d个学生的学号:", i); scanf("%d", p + i - 1); } for(i = 1; i <= count + newcount; i++){ printf("第%d个学生的学号为%d\n", i, *(p + i - 1)); } system("pause"); }
结构体
struce 关键字 定义结构体
第一种
struct student { int age; float score; char sex; }
第二种
struct student2 { int age; float score; char sex; } st2;
第三种
struct { int age; float score; char sex; } st3
结构体中不能定义函数,但是可以定义函数指针 函数也是保存在栈内存中,就像定义变量一样 结构体长度不固定,具体长度通过sizeof()得到
#include #include void study(){ printf("天天打怪兽\n"); } // 定义结构体 // 结构体中不能定义函数,但是可以定义函数指针 struct student{ int age; int height; char sex; void(*studyp)();//studyp 函数指针名 }; main(){ struct student std = {20,180,'f',study};// std 结构体变量 printf("芳龄%d\n",std.age); std.studyp();// 第一种方法 struct student* stdp = &std; (*stdp).studyp(); // 第二种方法 // ->左边必须是结构体的一级指针,右边是函数指针名字 stdp->studyp();// 第三种方法(开发中一般使用这种 ) system("pause"); }
联合体
联合体作用就是:联合体变量可以被赋值联合体定义的任意类型
#include #include main(){ union{int i; char c; short s} un; un.i = 30; un.s = 20; printf("%d\n",un.i); // 20 联合体同时只能有一个值,后面的值会把前面赋给联合体变量的值覆盖掉 printf("%d\n",sizeof(un)); // 4个字节 联合体长度取变量中的最大值 int system("pause"); }
枚举
#include enum weekday { monday,tuesday,wednesday,thursday,friday,saturday,sunday }; int main(void) { //int day; enum weekday day = sunday;// 只能从定义中的内容取,否则报错 printf("%d\n",day); // 6 system("pause"); return 0; }
自定义类型
typedef newname oldtype; #include #include // hehe这个时候跟int的作用一样,相当于给int起了个别名 typedef int hehe; main(){ hehe i = 3; printf("%d\n", i);// 3 system("pause"); }
上一篇: Ajax的学习