函数 不写参数表示可以接受任意参数,不写返回类型则返回int. fun( ){ }
不带参数,无返回值 则应该这样写 void fun(void)
不存在void变量,但是可以定义void类型的指针
void* 做为作为左值可以接受任意类型的指针
void* 作为右值使用时 需要强制转换
const 并不是定义真正意义上的常量,只是使变量具有只读属性
const 修饰的局部变量在栈上分配空间,修饰的全局变量在全局数据区分配空间
const 只在编译器有用,在运行期无用
(const 修饰的变量只是告诉编译器该变量不能出现在赋值符号的左边(可以通过指针修改它的值)const int c = 1; int *p =(int *)&c; *p = 2;)
标准C编译器不会将const修饰的全局变量存储在只读存储区,而是存在可修改的全局数据区,其值可改。
标准C编译器 —> const变量:只读,值可变
const
const全局变量:只读,值不可变
现代C编译器
static局部变量:只读,值不可改变
const局部变量
普通局部变量: 只读,值可以改变
const 将具有全局声明周期的变量存储在只读存储区
字符串字面量存储于只读存储区,在程序中需要用const char* 指针
volatile 可以理解为 编译器警告指示字
volatile 告诉编译器必须每次去内存取变量值
volatile 主要修饰可能被多个线程访问的变量,或者被中断处理程序改变的值
volatile 修饰可能被未知因数更改的变量
现代编译器 ,空结构体 的内存大小为0,标准C编译器 会报错
柔性数组
结构体最后一个元素可是是大小可变的数组
struct SoftArray
{
int len;
int array[];
};
// sizeof(struct SoftArray) == 4;
struct SoftArray* sa = NULL;
sa = (struct SoftArray *)malloc(sizeof(SoftArray)+ sizeof(int)* 5);
sa->len = 5;
联合体
union 只分配最大成员的空间,所有成员共享这个空间,其使用受系统大小端影响
int i=1;
小端模式0x00000001
大端模式0x01000000
枚举 enum
第一个定义的值默认为0;enum值默认是前一个值加1
enum类型的变量只能取定义时的离散值
enum中定义的值是C语言中真正意义上的常量
enum //无名枚举 真正意义上的常量
{
a = 10,
};
int array[a] = {0};
sizeof 是编译器内置的指示符,在编译期间就确定了类型或者变量的大小了
用于类型 sizeof(type)
用于变量 sizeof(var) 或者 sizeof var
int var = 0;
int size = sizeof (var++);
printf("%d\n", var);
// 此时size == 4;var ==0; sizeof在编译期间就确定了,不会在程序运行时计算;
int f()
{
.....
}
size = sizeof(f());
// size == 4
size并不是函数,在编译期间就计算出了,所以函数f()并不会被执行,只是计算出size==4;
typedef
用于给一个已经存在的数据类型重命名
typedef重命名的类型 可以在typedef语句之后被定义
新名字 是不能被 unsigned 和signed 修饰的
typedef int Int32;
struct _tag_point
{
int x;
int y;
};
typedef struct _tag_point Point;
typedef struct
{
int length;
int array[];
} SoftArray;
typedef struct _tag_list_node ListNode;
struct _tag_list_node
{
ListNode* next;
};
注释
编译器在编译过程中使用空格代替整个注释
字符串字面量中的的注释符号 // /* */ 不代表注释符号
/* */型注释不能被嵌套
// is it \
?
注释符号 可使用换行符,上面注释两行;
接续符(\)
编译器会将接续符提出,跟在接续符后面内容接续到前一行
在接续单词是,反斜杠之后不能有空格,反斜杠的下一行也不能有空格
接续符适合在宏定义代码块使用
转义符(\)
无回显字符,普通字符
反斜杠(\)做为接续符使用可直接出现在程序中,作为转义符使用需要出现在单引号和双引号之间;
单引号括起来的单个字符代表整数 ‘a’表示字符字面量 ‘a’+1 表示‘a’的ASCII码加1,结果为‘b’
双引号括起来的字符代表字符指针 "a"表示字符串字面量,在内存中占两个字节,“a”+1表示指针运算,结果指向“a”结束符‘\0’
逻辑运算符
|| 从左向右开始计算: 当遇到为真的条件时停止计算,整个表达式为真
&& 从左向右开始计算: 当遇到为假的条件时停止计算,整个表达式为假
&&和||混合运算时,整个表达式被看做||表达式,从左向右先计算&&表达式最后计算||表达式 : a&&b||c&&d --> (a&&b)||(c&&d)
! !n n = 0 则返回1;n != 0则返回0
C语言里面 只有0才表示假,非0为真。
位运算符
左移右移的 左操作数必须是整数类型,char和short被隐式转换成int后进行位移操作
右操作数的范围必须是0 - 31
左移运算符<< 高位丢弃,低位补0
右移运算符>> 高位补符号位,低位丢弃
左移n位相当于乘以2的n次方,但效率比数学运算符高
右移n位相当于除以2的n次方,但效率比数学运算符高
a = a^b;
b = a^b; // b = a^b^b;
a = a^b; // a = a^b^a;
运算优先级 : 四则运算 > 位运算 > 逻辑运算
++ --
C语言只规定了++和--对应的相对执行次序,其对应的汇编指令不一定连续运行,在混合运算中可能被打断;
自增自减运算符 不要和其他操作符一起混合运算;
r = (i++) + (i++) + (i++);
r = (++i) + (++i) + (++i);//不要这么写
空格可以作为C语言中一个完整符号的休止符,编译器读入空格后立即对之前的符号进行处理
++a+++b --> ++a + ++b
三目运算符
三目运算符(a?b:c)的返回类型: 三目运算符返回变量的值,不是变量本身,通过隐式类型转换返回b和c的较高类型,当b和c不能隐式转换到同一类型则编译出错
int main()
{
char c = 0;
short s = 0;
int i = 0;
double d = 0;
char* p = "str";
printf( "%d\n", sizeof(c ? c : s) );//4 int
printf( "%d\n", sizeof(i ? i : d) );//8
printf( "%d\n", sizeof(d ? d : p) );//error
return 0;
}
逗号表达式
#include <stdio.h>
#include <assert.h>
int strlen(const char* s)
{
return assert(s), (*s ? strlen(s + 1) + 1 : 0);
}
int main()
{
printf("len = %d\n", strlen("Delphi"));
printf("len = %d\n", strlen(NULL));
return 0;
}