a++ 先赋值再自增
++a 先自增后赋值
使用以下代码区分两者的区别:
int main()
{
//可以理解成 ++a 是先自增后使用
int a = 8;
int b = ++a;
printf("a = %d b = %d\n", a, b);//9 9
// a++ 是先使用后自增
a = 10;
b = a++;
printf("a = %d b = %d\n", a, b);//9 8
return 0;
}
在刚开始学习c语言中会经常犯的错误就是 在if判断语句中只写了一个等号,然后导致程序跑完后现象跟你所想的不一致而且编译器还不会报错,
此时可以将数字写到等号前面 如果是一个等号编译器就会识别报错,而且不会影响正常的使用。
break可以跳出循环的控制语句和switch的语句,切记不能跳出 if 的判断语句,不然也会出现比较隐蔽的错误。
a) static修饰局部变量会延长局部变量的生命周期,并且只在第一次调用函数时对这个局部变量进行初始化,后续在调用这个函数都不在进行初始化的操作。
b) 如果static修饰的局部变量没有初始化,存在.bss段,默认初始化为0
c) 如果使用static修饰的局部变量初始化,存在.data段
d) static修饰的局部变量只是延长了生命周期,访问依然只能在函数内进行访问。
static修饰全局变量或者函数表示在外部的源文件中不可以访问,只能在本文将中进行访问。
全局变量在静态存储区,局部变量在堆栈区
1,寄存器数量有限使用频率不高,
2,寄存器变量不可以取地址因为没有地址
3,只用于快速访问的变量,如计数器。
const 用来修饰指针,有下面四种写法
要看 const 在 * 的左边还是右边。如果是在左边,表示修饰的是 *p ,表示不能通过指针修改指向的内容。如果是在右边,表示修饰的是 p, 表示指针的指向不能修改
const int *p;
int const *p; 前两种写法是一样的。
int * const p;
const int * const p;
修饰全局变量或者函数,表示这个函数或者变量在其他文件进行定义,通常用于两个或者多个文件共享的全局变量或者函数时的场景。
可以引用头文件的方式,也可以使用extern关键字,,如引用头文件的方式,假如全局变量写错了在编译期间就会报错,而用extern关键字 在编译不会出错在链接部分会出错。
static 声明的全局变量 与 普通的全局变量 |
相同点:都是全局变量都存储在静态区,存储方式相同, 不同点:普通全局变量作用域是整个源程序,当一个源程序是由多个源文件组成时,普通全局变量在各个源文件都是有效的。而经过static声明后变成静态全局变量后限制了作用域,只在定义变量的源文件有效,在同一个源程序下不同的源文件不可以使用,只能成为本源文件的全局变量即使外部声明extern也不可以并且只初始化一次,正因如此考虑数据安全性可以避免在其他源文件中引起的错误。总得来说就是作用域不同 |
static 的局部变量 与 普通局部变量 |
static 的局部变量增加了生命周期,改变了存储方式,直到程序运行结束才会被释放,并且下一次会依据上一次的值。 而普通的局部变量在程序结束或者函数结束后就已经释放掉了 。 |
static 的函数 与 普通函数 |
被static修饰后就变成只在本源文件使用的内部函数,并且在该源文件内说明和定义,不能被其他文件调用 |
整体分为4个过程:预处理-->编译-->汇编-->链接
预处理 |
头文件的展开、宏定义的替换、注释的删除 在Linux-Ubuntu中可以使用如下命令生成后缀名为 .i的文件进行查看 gcc -E hello.c -o hello.i |
||
编译 |
词法分析、语法分析,是用来查错的 如果无误,会生成对应的汇编文件,如果有误的话无法进入下一个阶段,会在命令行是会打印出错误信息。gcc -S hello.i -o hello.s 会生成后缀名为 .s的文件为汇编文件。 |
||
汇编 |
将汇编文件生成计算机能识别的二进制文件 通过 gcc -c hello.s -o hello.o |
||
链接 |
链接库文件,生成对应的可执行文件 通过 gcc hello.o -o hello(可以是自定义名字) |
#define:
首先凡是以 # 号开头的都是预处理命令,当然#define 与常用的#include一样也不例外,#define 定义一个标识符来表示一个常量,这个标识符遵循标识符命名规则。作为宏定义使用。在预处理时进行替换被称为宏定义。
#include
#define aaa 1
#define bbb 2
int main()
{
printf( "aaa 的值: %d\n", aaa);
printf( "bbb 的值: %d\n", bbb);
return 0;
}
typedef:
重定义简单来说就是按照定义变量的方式,把变量名换上新类型名,并在最前面加上 typedef 就声明了新类型名代表原来的类型。并且typedef只对已存在的类型指定一个新的类型名,没有创造新的类型名,并且用来声明数组类型、指针类型、结构体、共用体类型时变得十分方便。
typedef int NUM[5];
NUM a;//就相当于定义了 int a[5]
两者的区分与注意项:
两者在表面上看似相同,但是实质上是不同的,#define是在预处理阶段处理的,只是简单的字符串替换操作,而typedef是在编译阶段处理的,是采用定义一个变量的方式去定义变量。宏定义后面是不能加分号的,而重定义typedef是必须加的。
使用typedef有利于程序的通用与移植,有时程序会依赖硬件特性,此时体现出便捷性。有的计算机系统 int占用两个字节,但有的是占用4个字节,如果将一个c程序从4字节的移植到2字节的时候,一般的办法是将int改成long,如果有多处定义就需要一处一处都需要改变,此时可以使用typedef int A 替代int ,在移植的时候只需要改变typedef long A 就可以了。---参考了大神们的解释
typedef:
1> 将变量的定义前边加一个typedef, 此时变量名等价于原来的数据类型。
2> 结尾必须加“;”
3> 新的类型起别名时一般以_t结尾
4> typedef起别名时参与编译过程的
在使用时需注意:
#define intp_t int *
intp_t p, q; // p是指针 q是整型变量 只简单替换了两者。
==> int *p, q;
typedef int * intp_t
intp_t p, q; // p和q都是指针
strlen |
计算字符串长度,不包括 '\0'。在打印的时候用%ld |
sizeof |
计算数据在内存的所占空间的大小,与定义时的数据类型有关,计量单位是字节,int 类型为4个字节、char类型为1个字节等。sizeof他是一个关键字,不是一个函数,可以用来计算类型或者变量占用的内存空间的大小,返回值结果是以 字节 为单位的。32为系统,返回的是 int %d 64为系统,返回的是 long %ld |
头文件内容以 led.h 为例,如果宏没有定义以下内容就进行定义,如果已经包含了就不再重复定义包含。
#ifndef __LED_H__
#define __LED_H__
//内部头文件代码
#endif
结构体变量所占内存内存长度是各个成员内存长度之和,每个成员分别占有自己的内存单元。而共用体(联合体)变量所占的内存长度等于最长的成员长度。
在以下代码内 a,b,c各占四个字节,如果换成结构结构体4+1+4。相比之下后者更加节省空间,空间使用效率更高
union{
int i;
char j;
float k;
}a,b,c;