C语言关键字及运算符操作
32个关键字
杂项:sizeof、return
#include<stdio.h>
int main()
{
int a;
printf("the a is %d\n", sizeof(a));
return 0;//输出为4
}
sizeof:编译器给我们查看内存空间容量的一个工具
return:返回值
数据类型:int, char, long, short, unsigned, signed, float, double, void
c语言操作对象:内存
c语言通过数据类型关键字描述内存大小
short、int、long、char、float、double 这六个关键字代表C 语言里的六种基本数据类型。
在不同的系统或编译器上,这些类型占据的字节长度是不同的:
8bit = 1B
在32 位的系统上:
short 占据的内存大小是2 个byte;
int占据的内存大小是4个byte;
long占据的内存大小是4个byte;
float占据的内存大小是4个byte;
double占据的内存大小是8个byte;
char占据的内存大小是1个byte;//硬件处理的最小单位8bit ,1个字节
具体可以用sizeof测试
int根据具体硬件决定
编译器最优的处理大小:系统一个周期,所能接受最大处理单位
32位系统 32bit 4B int
16位系统 16bit 2B int
2B数的表示范围为65536
进制表示
十进制、八进制、十六进制、二进制
3bit——8进制 111——0x7 1000——0x8
int a = 010 //a为8
4bit——16进制 int a = 0x10//a为16
unsigned无符号:多表示数据
signed有符号:多表示数字
char a = -1; //0xff 补码
a>>1//a右移 符号位仍然存在
unsigned char b = -1
b>>1//b右移,可以变成0x00
浮点运算float、double
float 4B double 8B
浮点型常量
1.0 1.1 double
1.0f float
格式化输出语句
void 语义符
自定义数据类型:struct、union、enum、typedef
自定义 = 基本元素集合
结构体struct 元素之间的和
struct myabc{
unsigned int a;
unsigned int b;
unsigned int c;
unsigned int d;
}; //告诉编译器自己定义一个内存组织,四个unsigned int的集合
struct myabc mybuf;//对结构体变量化,实例一个变量
结构体顺序有要求;
共用体 union 共用起始地址的一段内容,技巧型代码
union myabc{
char a;
int b;
}; //共用体声明
union myabc abc;//使用
enum 枚举 被命名的整形常数的集合
定义常数例如:
#define mon 0
#define tue 1
#define wed 2
enum abc{mon, tue, wed};//效果同上,不定义的话默认为0,1,2
enum 枚举名称{常量列表};
例如
enum abc{MOD = 100, TUE, WED};
int main()
{
enum abc a1;
printf("the a1 is %lu\n",sizeof(a1));
printf("the %d\n",WED);
return 0;
}
typedef 数据类型的别名
可以自己定义数据类型的名字,提高代码可读性
例如:int a = 100; //a是一个int类型的变量
typedef int a; //a是一个int类型的外号
typedef usigned int u16;
通常定义为xxx_t;
逻辑结构关键字:
两大结构:分支和循环
if、else 条件
if(条件表达式)
xxx;
else
xxx;
switch、case、default 多分支
switch(整型数字)
switch(a)
{
case 1:
break;
case 2:
break;
}
do、while、for 循环
for:次数
for(表达式1;表达式2;表达式3)
{
执行代码块
}
执行表达式1,对循环变量做初始化;
判断表达式2,若其值为真(非0),则执行for循环体中执行代码块,然后向下执行;若其值为假(0),则结束循环;执行表达式3,(i++)等对于循环变量进行操作的语句;执行for循环中执行代码块后执行第二步;第一步初始化只会执行一次。
循环结束,程序继续向下执行。
while:条件,当值为真(非0)时, 执行循环体代码块。
while(表达式)
{
执行代码块
}
do:先执行再判断条件,它先执行循环中的执行代码块,然后再判断while中表达式是否为真,如果为真则继续循环;如果为假,则终止循环。因此,do-while循环至少要执行一次循环语句。
do{
执行代码块
}while(表达式);
continue、break、goto
goto只能在同一函数下跳转
对于资源属性中位置的限定关键词auto、register、static、const、extern、volatile
C语言根据变量的生存周期来划分,可以分为静态存储方式和动态存储方式。
静态存储方式:是指在程序运行期间分配固定的存储空间的方式。静态存储区中存放了在整个程序执行过程中都存在的变量,如全局变量。
动态存储方式:是指在程序运行期间根据需要进行动态的分配存储空间的方式。动态存储区中存放的变量是根据程序运行的需要而建立和释放的,通常包括:函数形式参数;自动变量;函数调用时的现场保护和返回地址等。
auto 默认情况,可省略,属于动态存储方式,可读可写
register 定义一些快速访问的变量,编译器会尽量安排CPU的寄存器去存放变量,若寄存器不足,变量还是放在存储器中
register int a;// 限制变量定义在寄存器上的修饰符
&取地址这个符号对register不起作用
int main()
{
register int a;
a = 0x10;
printf("the a is %d\n",a);
return 0;
}
static 静态
应用场景:修饰3种数据
1)函数内部变量
普通局部变量的值在初始时是不确定的,除非对其显式赋值。普通局部变量存储于进程栈空间,使用完毕会立即释放。
静态局部变量使用static修饰符定义,即使在声明时未赋初值,编译器也会把它初始化为0。且静态局部变量存储于进程的全局数据区,即使函数返回,它的值也会保持不变。变量在全局数据区分配内存空间;编译器自动对其初始化
其作用域为局部作用域,当定义它的函数结束时,其作用域随之结束
#include <stdio.h>
void fn(void)
{
int n = 10;
printf("n=%d\n", n);
n++;
printf("n++=%d\n", n);
}
void fn_static(void)
{
static int n = 10;
printf("static n=%d\n", n);
n++;
printf("n++=%d\n", n);
}
int main(void)
{
fn();
printf("--------------------\n");
fn_static();
printf("--------------------\n");
fn();
printf("--------------------\n");
fn_static();
return 0;
}
static定义的变量在全局数据区分配内存空间,只有定义它的函数结束作用域才结束,不会自己释放,普通变量位于栈区,由系统自动分配内存,使用完毕立即释放
2)函数外部的变量
全局变量定义在函数体外部,在全局数据区分配存储空间,且编译器会自动对其初始化。
普通全局变量对整个工程可见,其他文件可以使用extern外部声明后直接使用。也就是说其他文件不能再定义一个与其相同名字的变量了(否则编译器会认为它们是同一个变量)。
静态全局变量仅对当前文件可见,其他文件不可访问,其他文件可以定义与其同名的变量,两者互不影响。
file1.h如下:
#include <stdio.h>
void printStr();
在file1.c中定义一个静态全局变量hello, 供file1.c中的函数printStr访问
#include "file1.h"
static char* hello = "hello cobing!";
void printStr()
{
printf("%s\n", hello);
}
file2.c是我们的主程序所在文件,file2.c中如果引用hello会编译出错
#include "file1.h"
int main()
{
printStr();
return 0;
}
上面的例子中,file1.c中的hello就是一个静态全局变量,它可以被同一文件中的printStr调用,但是不能被不同源文件中的file2.c调用。只在定义它的源文件内有效,其他源文件无法访问它
3)函数的修饰符
函数的使用方式与全局变量类似,在函数的返回类型前加上static,就是静态函数。其特性如下:
静态函数只能在声明它的文件中可见,其他文件不能引用该函数,不同的文件可以使用相同名字的静态函数,互不影响
/* file1.c */
#include <stdio.h>
static void fun(void)
{
printf("hello from fun.\n");
}
int main(void)
{
fun();
fun1(); //无法访问fun1
return 0;
}
/* file2.c */
#include <stdio.h>
static void fun1(void)
{
printf("hello from static fun1.\n");
}
static函数可以很好地解决不同原文件中函数同名的问题,因为一个源文件对于其他源文件中的static函数是不可见的。
extern:在一个文件中引用另一个文件中定义的变量或者函数
1)引用同一个文件中的变量
#include<stdio.h>
int func();
int main()
{
func(); //变量num在func函数中是可以正常使用,因为func对num的调用是发生在num的声明和初始化之后
extern int num;// 需要使用extern告诉编译器变量存在
printf("%d",num); //2
return 0;
}
int num = 3;
int func()
{
printf("%d\n",num);
}
2)引用另一个文件中的变量
extern这个关键字的真正的作用是引用不在同一个文件中的变量或者函数
main.c
#include<stdio.h>
int main()
{
extern int num;
printf("%d",num);
return 0;
}
b.c
#include<stdio.h>
int num = 5;
void func()
{
printf("fun in a.c");
}
b.c中定义了一个变量num,如果main.c中想要引用这个变量,那么可以使用extern这个关键字,注意这里能成功引用的原因是,num这个关键字在b.c中是一个全局变量,也就是说只有当一个变量是一个全局变量时,extern变量才会起作用
3)引用另一个文件中的函数
extern除了引用另一个文件中的变量外,还可以引用另一个文件中的函数,引用方法和引用变量相似
const:只读的变量
1)修饰局部变量
const int n=5;
int const n=5;
都表示变量n的值不能被改变,需要注意的是,用const修饰变量时,一定要给变量初始化,否则之后就不能再进行赋值了。
2)常量指针与指针常量
常量指针是指针指向的内容是常量,可以有一下两种定义方式
const int * n;
int const * n;
需要注意的是一下两点:
1、常量指针说的是不能通过这个指针改变变量的值,但是还是可以通过其他的引用来改变变量的值的。
int a = 5;
const int *n = &a;
a=6;
2、常量指针指向的值不能改变,但是这并不是意味着指针本身不能改变,常量指针可以指向其他的地址。
int a=5;
int b=6;
const int* n=&a;
n=&b;
指针常量是指指针本身是个常量,不能在指向其他的地址
int *const n;
需要注意的是,指针常量指向的地址不能改变,但是地址中保存的数值是可以改变的,可以通过其他指向改地址的指针来修改。
int a=5;
int *p = &a;
int* const n = &a;
*p=8;
3)修饰函数的参数
1、防止修改指针指向的内容
2、防止修改指针指向的地址
volatile:告知编译器编译方法的关键字,不优化编译
volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改。volatile 提醒编译器它后面所定义的变量随时都有可能改变,因此编译后的程序每次需要存储或读取这个变量的时候,都会直接从变量地址中读取数据。如果没有 volatile 关键字,则编译器可能优化读取和存储,可能暂时使用寄存器中的值,如果这个变量由别的程序更新了的话,将出现不一致的现象。所以遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。
一般说来,volatile用在如下的几个地方:
- 中断服务程序中修改的供其它程序检测的变量需要加volatile;
- 多任务环境下各任务间共享的标志应该加volatile;
- 存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;