一、 变量
-
变量依据其定义的类型,分为整型变量、字符型变量、浮点型变量和指针变量等。
-
每个变量必须要有一个名字和它所在内存空间绑定。
-
变量的值是变量所对应的内存区域存放的二进制序列。变量的值不会因为变量的类型发生了改变而改 变,当变量被转换为对应的类型时,内存区域的二进制序列以该类型的形式翻译出来。这是强制类型转换能够成立的原因。
-
局部变量:在函数内进行定义说明,其作用域仅限于函数内。
(1)在主函数中定义的变量也只能在主函数中使用,不能在其他函数中使用。
(2)形参变量是属于被调函数的局部变量,实参变量是属于主调函数的局部变量。
(3)允许在不同的函数中使用相同的变量名,它们代表不同的对象,分配不同的单元,互不干扰,也不会发生混淆。 -
全局变量:在函数外部定义的变量,不属于哪一个函数,它属于一整个源程序文件,作用域为整个源程序。
全局变量的说明符为extern,但在一个函数之前定义的全局变量,在该函数内使用时可不再添加。
二、常量
- 常量的值在其作用域内不会发生改变,也不能再被赋值,在其出现时就被当作一个立即数来使用。只能被访问、被读取,而不能被写,被赋值。
类似于const关键字。
三、基本内置类型
1、整型(6种):带符号整型(int),带符号短整型(short int),带符号长整型(long int或long),无符号整型(unsigned int),无符号短整型(unsigned short int),无符号长整型(unsigned long)。
2、实型(3种):单精度(float),双精度(double),长双精度(long double)
3、构造类型
4、指针类型
5、枚举类型
6、void类型
- 数据类型及其大小
- 陷阱——有符号vs无符号
int main()
{
char a[1000];
int i;
for(i=0;i<1000;i++)
{
a[i]=-1-i;
}
printf("%d",strlen(a));
return 0;
}
答案是255
四、 static
一般情况下,局部变量是存放在栈空间,生命周期在块语句执行结束时便结束了,但是如果用static进行修饰的话,该变量便存放在静态数据区,其生命周期一直持续到整个程序执行结束为止。
题目:用数组进行两个字符串的连接。
在自己封装的func函数中,定义的c数组在函数结束后就会被释放,因此无法进行return。使用static延长该局部变量的生命周期,使其能够返回输出。
#include<stdio.h>
#include<stdlib.h>
char*func(char a[50],char b[50]){
int i=0;
int j=0;
int x=0;
static char c[1024]={0};
while(a[i]!='\0'){
c[x]=a[i];
x++;
i++;
}
while(b[j]!='\0'){
c[x+j]=b[j];
j++;
}
printf("%s\n",c);
return c;
}
int main(){
char s1[100], s2[100];
printf("Please Enter the first:");
gets(s1);
printf("Please enter the second:");
gets(s2);
char *ch=func(s1,s2);
printf("---%s\n",ch);
system("pause");
return 0;
}
五、extern
表示外部引用,用extern修饰的变量可以在其它文件中使用
在一个文件中定义的全局变量可以在当前文件中使用extern获取到指定的变量。
六、const
用const来修饰,就意味着该变量里的值可以被访问,不能被修改。
int main(int argc, const char *argv[])
{
int a = 100,b = 100;
//int *p = &a;
//*p = 800;
//printf("a = %d\n",a);
//const int *p = &a;
//const修饰的是*p,表示指针指向的地址里面保存的值是一个常量
//int const *p =&a; //const修饰的是*p,表示指针指向的地址里面保存的值是一个常量
//int * const p = &a; //const修饰的是p,表示指针变量是一个常量,无法修改指针的指 向,但是可以修改指针指向的地址的值
const int * const p = &a; //表示指针指向的地址里面保存的值是一个常量,指针的值也是一 个常量(不能改变指针的指向)
//*p = 200;
p = &b;
printf("*p = %d\n",*p);
return 0;
}
七、auto
C语言程序是面向过程的,在代码中会出现大量的函数模块,每个模块都有生命周期,在函数生命周期中声明的变量通常叫作局部变量,也叫作自动变量。
int fun()
{
int a=10; //auto int a=10
//do something
return 0;
}
auto的出现意味着当前变量的作用域为当前函数或代码段的局部变量,意味着当前变量会在内存栈上进行分配。
八、register
用register来修饰,该变量会变成一个寄存器变量,让该变量的访问速度达到最快。
例:一个程序逻辑中有一个很大的循环,循环中有几个变量要频繁进行访问,这些变量可以声明为register类型。
九、volatile
十、typedef
- 给变量一个易记期望意义明确的新名字。
- 简化一些比较复杂的类型声明。
例1:
typedef long byte_4;
作用:
给已知数据类型long起个新名字,叫做byte_4
例2:
typedef struct tag_my_struct
{
int i_num;
long l_length;
}my_struct;
作用:
定义一个新的结构类型
struct tagMyStruct
{
int i_num;
long l_length;
};
typedef为这个新的结构起了一个名字,叫作my_struct
typedef struct tag_my_struct my_struct
- typedef与#define的问题
通常来讲,typedef要比#define要好,特别是在有指针的场合。
其根本原因在于#define只是简单的文本替换,而typedef则是为一个类型起新名字
#define 宏定义有一个特别的长处:可以使用#ifdef、#ifndef 来进行逻辑判断,还可以使用#undef来取消定义。
typedef也有一个长处:它符合范围规范,使用typedef定义的变量类型,其作用范围限制在所定义的函数或者文件内(取决于此变量定义的位置),而宏定义没有这种特性。
十一、复合数据类型
1.struct结构体
#include <stdio.h>
struct Student
{
int num;
char name[20];
int age;
//结构体中不能保存函数,但可以用函数指针
void(*func)();
};
void func()
{
struct Student stu;
}
void print()
{
pritnf("hello world!\n");
}
int main(int argc,char **argv)
{
//初始化
struct Student stu={1,"zhangsan",12};
struct Student stu2={.num=1,.name="lesi",.age=15,.func=print};
//如何访问结构体变量
printf("stu2 num=%d\n",stu2.num);
printf("stu.name=%s\n",stu.name);
//结构体指针
struct Student *pstu=&stu;//*pstu=stu;
printf("stu.num=%d\n",(*pstu).num);
printf("stu.name=%s\n",(*pstu).name);
printf("stu.num=%d\n",pstu->num);//(*pstu).num;
printf("stu.num=%d\n",(&stu)->num);
//通过键盘给结构体赋值
scanf("%d",&(stu.num));
scanf("%s",stu.name);
scanf("%d",&(stu.age));
}
- 当结构体使用指针时要记得释放。
#include <stdio.h>
struct Student
{
int num;
char *name;
int age;
};
int mian(int argc ,char **argv)
{
struct Student stu;
scanf("%d",&(stu.num));
stu.name=(char*)malloc(sizeof(char)*100);
if(NULL==stu.name)
{
printf("malloc error!");
exit(1);
}
scanf("%s",stu.name);
scanf("%d",&(stu.age));
//释放
free(stu.name);
stu.name=NULL;
}
- struct对齐方式:
struct node
{
char ch1;
int num;
char ch;
}
ch1—4,num—4,ch—4
共12个字节
struct node
{
char ch;
char ch1;
int num;
};
ch—4,ch1占用ch开辟的4个字节中剩下的,num—4
共8个字节
- 强制修改字节
#pragma pack(4)
2. 共用体union
union Student
{
int num;
char ch;
};
int main(int agc,char **argv)
{
union Student stu={.num=1,.ch='a'};
union Student *pstu=&stu;
printf("stu.num=%d\n",stu.num);
printf("stu.ch=%c\n",pstu->num);
}
输出结果:
stu.num=97
stu.ch=a
存储方式:所有成员公用同一段内存空间(最长字节的变量)(数据覆盖)
- union的大小
//保存的成员中最大字节长度的成员所决定;
union Student
{
int num;
char ch[7];
}
共8个字节
- 题目
p.num=0;
p.ch[0]=0;
pch[1]=1;
pritnf("p.num=%d\n",p.num);
输出:256
- 判断大小端字节序
大端CPU把高子杰写到低地址上,小端反之。
union Student stu2;
stu2.num=0x12345678;
if(stu2.ch==0x78)
{
printf("Little!\n");
}
else
{
printf("big!\n");
}
3.enum枚举
//定义一系列的整数宏
enum Result
{
OK=1;
NO;
SUCCESS,
FAILED
}
int main(int argc,char**argv)
{
int num=SUCCESS;
printf("num=%d\n",num);
return 0;
}
打印为3
- enum VS #define
enum优点:定义一系列整数宏更加便捷,在编译阶段。
#define:预处理、不安全,做傻瓜式替换。