- C语言程序的三种基本结构:顺序结构、选择结构、循环结构
- C语言可处理的文件类型是 :文本文件和二进制文件
- C语言中最简单的数据类型包括:整型、实型、字符型
- sizeof是运算符,参数可以是数组、指针、函数、类型、对象等 ; 例如sizeof(数组):返回分配的数组空间大小; sizeof(指针) :返回存储该指针所用的空间大小(储存该指针的地址的长度,是长整型,应该为4);
- strlen是函数,参数只能是字符型指针,功能是返回字符串的长度 : 从代表该字符串的第一个地址开始遍历,直到遇到第一个结束符NULL('\')为止
- 标识符:在程序中使用的变量名、函数名、标号等统称为标识符。除库函数的函数名由系统定义外,其余都由用户自定义。C 规定,标识符只能是字母(A~Z,a~z)、数字(0~9)、下划线()组成的字符串,并且其第一个字符必须是字母或下划线。
- %为求余运算符,该运算符只能对整型数据进行运算,且符号与被模数相同
- \ 为求商运算符,该运算符能对整型、字符型、浮点型等数据类型进行运算
- 左移运算符 << :比如讲0X01左移5位,即0X0100000,得32
- 右移运算符 >>
- 在C语言中,关系表达式和逻辑表达式的值为 0或1
- 绝对地址赋值 :(unsigned int*)0*100000 = 0*1234;
- 让程序跳转到绝对地址是0x100000去执行 : *((void(*)())0*100000)();
-
//这是原来的函数,使用的是值传递方式 void GetMemory(char *p){ p=(char *)malloc(100); } //这是正确的内存分配函数,使用的是双重指针,传递进来的是str的指针地址,*p就是原来的str void GetMemory2(char **p) { *p=(char*)malloc(100); } //这是使用引用的内存分配函数 void GetMemory3(char* &p) { p=(char*)malloc(100); } void Test(void){ char *str=NULL; GetMemory(str); // 该函数做的工作是 p=str, p=malloc(100); 和str一点关系都没有,str=NULL; //GetMemory2(&str); //该函数做的工作是 char**p=&str, str=*p=malloc(100),内存分配成功 //GetMemory3(str); // 传递的是str的引用,函数内的 p还是str,分配成功 strcpy(str, "Hello World!"); //现在可以 知道,第一个函数分配内存错误,strcpy也会失败 printf("%s\n", str); //输出只能是空 }
- char *Str;Str="abcdef"; Str指向常量区,指向的内容不能改了char Str[]="abcdef"; 在栈区,可以改变首先第一个指针形式的str指向一个字符串,这样指向以后就不能通过str对abcdef这个字符串修改了,但是你可以给str用别的字符串再赋值,这样他就指向了别的字符串,但是一旦指向某个字符串,就不能通过这个指针对字符串修改了。 第二种数组形式的str,其实数组名就是指针常量,也就是说一旦他初始化了,它指向的地址就固定了,就不能再用别的字符串赋值了(这就不同于指针形式的str),但是这个地址里放什么是可以改变的(指针形式的就不能改变了),比如说初始化之后在通过cin>>str改变数组的内容还是没有问题的char Str[]="abcdef"; 这种写法是没有问题的,这样不写数组大小但必须要对其初始化,如果没初始化就必须声明数组大小。
- int (*s[10])(int):返回一个int型的函数指针,该指针指向一个内有10个元素的数组
- 指针本质上也是一个变量,即指针变量,其用来保存其他变量的地址!
- 一维数组的数组名代表的是数组首元素的地址;对一维数组的数组名进行取地址操作,求得是数组的地址
- 二维数组的数组名代表的是第一个一维数组的地址;对二维数组名进行取地址操作,求得是二维数组的地址
- 指针和字符串的联系
-
char str[] = "hello"; //hello保存在字符数组 char *p = "hello"; //字符串指针指向字符串常量;用p来保存字符串常量的首地址 //计算字符串长度用strlen函数 strlen(str) = 5 //计算数组所占字节大小用sizeof 它即是关键字,又是运算符
- 字符串常量保存在.text段,不能被改变
- static:静态变量作用: 修饰局部变量的时候,可以延长局部变量的生命周期! 修饰全局变量或者函数的时候,可以起到“隐藏”的功能,进而达到只能够在本文件中使用的目的,不能在别的文件中使用; register: 寄存器变量修饰局部变量,不能够修饰全局变量和函数; register int num = 10; 请求编译器,将register修饰的变量保存到CPU内部寄存器上!而不是保存到内存中进而提高读取效率!
- 内存管理 栈空间: 1、自动管理:有系统自动给函数形参,局部变量和自动变量分配空间;
执行结束之后,系统自动释放,不用程序员自己去管;
2、反复使用:栈空间比较小;
3、栈空间是脏的;int num;printf("num");
4、临时性; 堆空间: 用malloc 、realloc、calloc来申请空间 1、灵活;
2、内存空间比较大;
3、需要手动申请和释放;
4、堆空间是脏的;
5、临时性; - 内存四驱
栈区(stack) 由编译器自动分配释放,存放函数的参数值,局部变量值等 堆区(heap) 一般由程序员分配与释放(动态内存申请与释放),若程序员不释放,程序结束后可能由操作系统进行回收 全局区(静态区)(static)
常量区
=======》》》》数据区
字符串常量和其他常量的存储位置,程序结束后由操作系统进行释放 程序代码区 存放函数体的二进制代码 - 宏定义函数和自定义函数的区别:
1、宏定义是在调用的地方原地展开;
自定义函数是跳转到函数定义的地方调用,调用结束之后再返回到函数调用的地方;
===》带来的实质区别是:
===》宏定义函数没有调用开销,而自定义函数是需要有调用开销的(需要保存现场,有入栈
和出栈的操作)
===》宏定义函数不需要进行参数类型的检查,也没有返回值类型;但是自定义有参数类型的
===》检查和返回值类型的检查!这个检查是由编译器完成的,如果调用的时候传入的实参和
===》函数的形参不一样的话,要么报警告要么报错!【会提示你发生了错误】 - 什么时候用宏定义函数,什么时候用自定义函数?
1、函数体比较简单的时候,不需要进行参数类型检查的时候,可以用宏定义函数!
2、函数体比较复杂,需要进行参数类型检查和返回值类型判断的时候,用自定义函数! - 枚举类型:一个一个地列举;
总结:
1、枚举里面是整型常量;
2、第一个枚举常量,默认为0,后面依次加1;
3、如果对于里面某个常量初始化为非0,后面的继续在此基础上加1,前面的不变; - 枚举(enum)和宏定义(define)有什么区别:
1、当有大量宏定义需要定义的时候,可以用枚举来代替;
2、枚举是数据类型,宏定义不是;
3、枚举只能表示常量;
4、宏定义是在预处理阶段处理,枚举是在编译阶段处理的! - 大小端:
int :byte0 byte1 byte2 byte3
A--->B:
存储领域的大小端模式:
一个字节有一个地址!存储类似int型变量的时候,会带来一个问题:
低字节放在低地址,高字节放在高地址,还是低字节放在高地址,高字节放在低地址?
低字节放在低地址,高字节放在高地址:小端模式!
低字节放在高地址,高字节放在低地址:大端模式! -
地址总线的宽度,随可寻址的内存元件大小而变,决定有多少的内存可以被存取。
-
32位系统上,对任意指针(无论那种类型的指针)求sizeof,得到的结果都是4;
-
同理64位系统上,对任意指针(无论那种类型的指针)求sizeof,结果是8。
-
数组的首地址和数组首元素的地址之间的区别:
*(a+1);a和&a的值一样的,但意思不一样;
a是数组首元素的的首地址,也就是a[0]的首地址,&a是数组的首地址;
a + 1是数组下一个元素的首地址,即a[1]的首地址,&a+1是下一个数组的首地址。