数据类型和变量
数据类型:固定内存大小的别名(本质)
变量:
一段实际连续存储空间的别名
程序中通过变量来申请并命名存储空间
通过变量的名字可以使用存储空间
eg:
int i;//告诉编译器需要4个字节大小的内存,并把这段内存命名为i,编译器就会分配一段内存空间。
#include <stdio.h>
int main()
{
char c = 0;
short s = 0;
int i = 0;
//数据类型:固定内存的别名
//char :1个字节的别名
//short:2个字节的别名
//int :4个字节的别名
//sizeof(char):去查找char是几个字节的别名
printf("%d, %d\n", sizeof(char), sizeof(c));
printf("%d, %d\n", sizeof(short), sizeof(s));
printf("%d, %d\n", sizeof(int), sizeof(i));
return 0;
}
运行结果:
1,1
2,2
4,4
#include<stdio.h>
typedef int INT;
typedef unsigned char BYTE ;
typedef struct _demo
(
short a;
BYTE b1;
BYTE b2;
INT 3;
)DEMO:
int main()
{
INT32 i32;
BYTE byte;
DEMO d;
printf("%d,%d\n",sizeof(INT32),sizeof(i32));
printf("%d,%d\n",sizeof(BYTE),sizeof(byte));
printf("%d,%d\n",sizeof(DEMO),sizeof(d));
return 0;
}
auto、register、static关键字
auto:
c语言中局部变量的默认属性
编译器默认所有局部变量都是auto类型的
register:
指明变量存储于寄存器中(访问速度快)(局部)
没有地址可以访问到(C语言规定:地址是内存中的空间)
static:
静态变量(只被初始化一次)
在程序的静态区分配空间
文件作用域限定符:限定在所定义此变量函数的文件中使用(static所定义的全局变量,函数),只有从一个文件访问static变量、static函数。
auto:变量存储于程序的栈中,默认属性
static:变量存储在程序静态去中
register:变量请求存储器CPU寄存器中(适用于实时系统中)
if、switch、do-while、while、for关键字
分支语句:
if
- else匹配之前离他最近的if语句
if语句中零值比较注意点
bool型变量应直接出现于条件中,不要直接进行比较
C语言中用0表示假,非零表示真(不能想当然认为bool变量1就是真!!!)普通变量和 零 值 比较,0放左边,避免手误导致bug(难找)
- float型的变量不能直接进行0值比较,需要定义精度
#define EPSINON 0.000000000000001
float f = 5.0;
if((5-EPSINON<=f)&&(f<=5+EPSINON))
{
**********;
}
switch
- case语句中的值只能是整形或字符型
case语句排列顺序分析
- 按字母或数字顺序排雷各条语句
- 正常情况放前面,异常置后
- default语句只用于真正的默认情况
if :适用于范围分支结构(程序清晰,可读性强)
switch:各个离散值判断(简洁,可读性强)
循环语句
- 通过条件表达式判定是否执行循环体
- 条件表达式遵循if语句表达式的原则
do:先执行,后判断。循环体至少执行一次。
while:先判断,后执行。循环体可能不执行。
for:先判断,后执行 。比while简洁,可读性高。
break:终止循环(跳出一个块{ })
continue:终止本次循环,进入下一次循环
5.const和volatile
const修饰变量
- 在C语言中const修饰的变量是只读的,其本质还是变量
- const修饰的变量会在内存占用空间
- 本质上const只对编译器有用,在运行时无用
const int a = 1;
左值:报错
右值:
1.int b = a;
2.int *p=(int *) & a ;//(取地址符,到内存里面取值)
int main()
{
const int a = 1;
int *p = (int*)&a;//
printf("%d\n",a);//到内存取值
*p = 3;//利用指针改变内存中a的值
printf("%d\n",a);
}
const修饰数组
- 在C语言中const修饰的数组是只读的
- const修饰的数组空间不可被改变(针对现代c编译器,不是所有c编译器都这样)
const int A[5] = {1,2,3};
int* p = (int*)A;
int i = 0;
for(i=0,i<5,i++)
{
p[i]=5-i;//ERROR
}
const修饰指针
- const int* p; //p指向的地址可变,p指向的内容不可变
- int const* p; //p指向的地址可变,p指向的内容不可变
- int* const p; //p指向的地址不可变,p指向的内容可变
const int* const p; //p和p指向的内容都不可变
口诀:左数右指
有*的时候
以*为界,左边数(指针指向的内容)不可改变,右边指针指向的地址不可改变- 当const出现在*号左边时指针指向的数据为常量
- 当const出现在*后右边时指针本身为常量
int main()
{
int i =0;
const int* const p = &i;
*p = 3;//不可改变指针指向地址的内容const int*(左数)
p = NULL;//不可改变指针int* const p(右指)
return 0;
}
const修饰函数参数和返回值
const修饰函数参数表示在函数体内不希望改变参数的值
const修饰函数返回值表示返回值不可改变,多用于返回指针的情形
const int* fun()//函数返回const 类型的指针
{
static int count = 0;
count ++;
return &count;
}
int main()
{
int i = 0;
const int * p = fun();//指针类型要和接收的类型一样
printf("%d\n",*p);
}
volatile
volatile可理解为“编译器警告指示字”
volatile用于告诉编译器必须每次去内存中取变量值
volatile主要修饰可能被多个线程访问的变量
volatile也可以修饰可能被未知因数更改的变量
int obj = 10;
int a = 0;
int b = 0;
a= obj;
sleep(100);
b=obj;
//编译器在编译的时候发现obj没有被当成左值使用,因此会“聪明”的直接将obj替换成10,而把a和b都赋值为10。
//此时加上vilatile int obj = 10;
//要求编译器每次从内存中取obj的值
问题:
const和volatile是否可以同时修饰一个变量?
const volatile int i = 0; 这个时候i具有什么属性?编译器如何处理这个变量?
6.struct和union分析
空结构体占多少内存?
struct D
{
};
int main()
{
struct D d1;
struct D d2;
printf("%d\n",sizeof(struct D));
printf("%d,%0x\n",sizeof(d1),&d1);
printf("%d,%0x\n",sizeof(d2),&d2);
return 0;
}
取地址: %p 或者 %0x(以16进制数显示)
灰色地带,空结构体占一个字节大小的内存,
由结构体产生柔性数组
柔性数组即数组大小待定的数组
C语言中结构体的最后一个元素可以是大小未知的数组
- C语言中可以由结构体产生柔性数组
#include <stdio.h>
#include <malloc.h>
typedef struct _soft_array
{
int len;
int array[];
}SoftArray;
int main()
{
int i = 0;
SoftArray* sa = (SoftArray*)malloc(sizeof(SoftArray) + sizeof(int) * 10);
sa->len = 10;
for(i=0; i<sa->len; i++)
{
sa->array[i] = i + 1;
}
for(i=0; i<sa->len; i++)
{
printf("%d\n", sa->array[i]);
}
free(sa);
return 0;
}
union和struct的区别
- struct中的每个域在内存中都独立分配空间
- union只分配最大域的空间,所有域共享这个空间