一切合法的变量名的定义(普通变量、指针变量、数组变量、函数指针、结构体)都可以用typedef转换成类型名
加typedef之前
unsigned int UINT; //普通变量
int* PINT; //指针变量
int Array[10]; //数组变量
void (*pfun)(); //函数指针
struct Student stu; //定义结构体变量stu
struct Student *pstu;//定义结构体指针pstu
加上typedef之后
typedef unsigned int UINT; //UINT类型名
typedef int* PINT; //PINT指针类型名
typedef int Array[10]; //Array数组类型名
typedef void (*pfun)(); //函数指针类型名
typedef struct Student stu; //stu类型
//使用stu s1;
typedef struct Student *pstu; //结构体指针类型
//使用:pstu p1 = NULL;
(1)给已有的类型名起别名
typedef unsigned char u_int8;
typedef unsigned short u_int16;
typedef unsigned int u_int32;
typedef unsigned double u_int64;
(2)对已有的声明,变量名的定义加上typedef 变成类型名
#include
typedef int Arr[10];
int main()
{
Arr a = {
1, 2, 3, 4, 5 };
for (int i = 0; i < 5; i++)
{
printf("%4d ", a[i]);
}
printf("\n");
}
结果:
结构体经典写法:
#include
struct Student
{
...;
}stu, *pstu;
//这样是定义了stu结构体变量和pstu结构体变量指针
//前面加上typedef后
typedef struct Student
{
...;
}stu, *pstu;
//stu便成了自定义的结构体类型
//pstu变成了自定义的结构体指针的类型
//从而可以使用该类型进行定义变量
int* p, q;
结果:
*和变量名结合,不是与类型名结合,所以p是int指针类型,q是int类型;
结合1,想同时定义p和q两个指针:
#include
typedef int *PTR;
int main()
{
PTR p,q;
return 0;
}
结果:
#include
int main()
{
int a = 0;
int x;
//在编译期确定
x = sizeof(++a);
//等价于x = 4;
printf("a = %d\n", a);
return 0;
}
//结果a = 0
调用时机不同:sizeof是关键字,编译期间确定类型和大小
strlen()是函数,在运行期间调用函数
功能不同:strlen()是专门计算字符串的长度;
sizeof在计算字符串所占用的空间大小;
char buff[] = {
"helloworld"};
int len = strlen(buff); //len = 10;
int size = sizeof(buff); // size = 11;
博客
vs2019的全局变量未初始化默认为0,局部变量未初始化是随机值,使用该值编译不通过
c中的常变量侧重与"变量",不能使用常变量定义数组,编译期不通过;
c++中的常变量侧重于"常",可以使用该常变量定义数组;
C++常变量类似于宏,却有不同与宏
’ ‘是字符的定界符, 在’前面加上\后转义变成单引号字符–》\’
例如:
char ch = '''; //error ''是定界符,想使用单引号字符需要转义
char ch = '\''; //true
""是字符串的定界符
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IgEV0wBA-1635073955443)(C:\Users\小小怪咯\AppData\Roaming\Typora\typora-user-images\image-20211023164550726.png)]
int main()
{
char cha = 0; //ASCII值为0对应的字符就是空字符
char chb = '0'; //字符0对应的ASCII值48
char chc = '\0'; //等于cha == 》空字符
char chd = ' '; //空格字符ascii值是32
return 0;
}
\000将八进制数000转换成十进制对应的ascii码值,码值对应的字符;
char str[] = {
"pzj\141hello"};
//八进制的141转换成十进制的97 //pzjahello
//如果是"pzj\1411hello" //pzja1hello
//如果是"pzj\148hello" //只会转义\14因为8超出0~7
//如果是"pzj\889hello" //此时的\就会被省略
int len = strlen(str); //len = 9
\x00将十六进制的00转换成十进制对应的ASCII码值,码值对应的字符;
其中十六进制数的有效范围是0~ff,因为char最大255,对应的十六进制数就是ff;十六进制数超出该范围也会报错
char str[] = {
"hello\61xworld"}; //helloaworld
字符串的printf("%s")打印、strcpy拷贝、strcat连接、等函数都是以字符串的\0作为结束条件
char str[] = {
"hello\0world"};
int size = sizeof(str); //size = 12
int len = strlen(str); //len = 5
printf("%s\n", str); //hello
#define MAX 1000
int main()
{
char str[] = {
"helloMAX"};
printf("%s\n", str); //helloMAX
}
C++有一个叫做“多字符文字”的东西。'1234'
就是一个例子。他们有类型int
,它是实现–定义了它们所具有的值以及它们可以包含多少字符。
那算不了什么直接与字符被表示为整数的事实有关,但在实现中,很有可能'1234'
定义为:
'1' + 256 * '2' + 256 * 256 * '3' + 256 * 256 * 256 * '4'
或:
'4' + 256 * '3' + 256 * 256 * '2' + 256 * 256 * 256 * '1'
编译错误:g_value未定的标识符
#include
void Test()
{
int a = g_value;
}
int g_value = 10;
int main()
{
Test();
return 0;
}
错误理解:误认为程序一边编译一边运行,g_value存在于.data段
优先级 | 运算符 | 名称或含义 | 使用形式 | 结合方向 | 说明 |
---|---|---|---|---|---|
1 | [] | 数组下标 | 数组名[常量表达式] | 左到右 | |
() | 圆括号 | (表达式) 函数名(形参表) | |||
. | 成员选择(对象) | 对象.成员名 | |||
-> | 成员选择(指针) | 对象指针->成员名 | |||
2 | - | 负号运算符 | -表达式 | 右到左 | 单目运算符 |
(类型) | 强制类型转换 | (数据类型)表达式 | |||
++ | 自增运算符 | ++变量名 变量名++ | 单目运算符 | ||
– | 自减运算符 | –变量名 变量名– | 单目运算符 | ||
* | 取值运算符 | *指针变量 | 单目运算符 | ||
& | 取地址运算符 | &变量名 | 单目运算符 | ||
! | 逻辑非运算符 | !表达式 | 单目运算符 | ||
~ | 按位取反运算符 | ~表达式 | 单目运算符 | ||
sizeof | 长度运算符 | sizeof(表达式) | |||
3 | / | 除 | 表达式 / 表达式 | 左到右 | 双目运算符 |
* | 乘 | 表达式*表达式 | 双目运算符 | ||
% | 余数(取模) | 整型表达式%整型表达式 | 双目运算符 | ||
4 | + | 加 | 表达式+表达式 | 左到右 | 双目运算符 |
- | 减 | 表达式-表达式 | 双目运算符 | ||
5 | << | 左移 | 变量<<表达式 | 左到右 | 双目运算符 |
>> | 右移 | 变量>>表达式 | 双目运算符 | ||
6 | > | 大于 | 表达式>表达式 | 左到右 | 双目运算符 |
>= | 大于等于 | 表达式>=表达式 | 双目运算符 | ||
< | 小于 | 表达式<表达式 | 双目运算符 | ||
<= | 小于等于 | 表达式<=表达式 | 双目运算符 | ||
7 | == | 等于 | 表达式==表达式 | 左到右 | 双目运算符 |
!= | 不等于 | 表达式!= 表达式 | 双目运算符 | ||
8 | & | 按位与 | 表达式&表达式 | 左到右 | 双目运算符 |
9 | ^ | 按位异或 | 表达式^表达式 | 左到右 | 双目运算符 |
10 | | | 按位或 | 表达式|表达式 | 左到右 | 双目运算符 |
11 | && | 逻辑与 | 表达式&&表达式 | 左到右 | 双目运算符 |
12 | || | 逻辑或 | 表达式||表达式 | 左到右 | 双目运算符 |
13 | ?: | 条件运算符 | 表达式1? 表达式2: 表达式3 | 右到左 | 三目运算符 |
14 | = | 赋值运算符 | 变量=表达式 | 右到左 | |
/= | 除后赋值 | 变量/=表达式 | |||
*= | 乘后赋值 | 变量*=表达式 | |||
%= | 取模后赋值 | 变量%=表达式 | |||
+= | 加后赋值 | 变量+=表达式 | |||
-= | 减后赋值 | 变量-=表达式 | |||
<<= | 左移后赋值 | 变量<<=表达式 | |||
>>= | 右移后赋值 | 变量>>=表达式 | |||
&= | 按位与后赋值 | 变量&=表达式 | |||
^= | 按位异或后赋值 | 变量^=表达式 | |||
|= | 按位或后赋值 | 变量|=表达式 | |||
15 | , | 逗号运算符 | 表达式,表达式,… | 左到右 |
易错点:
int main()
{
int a = 1, b = 2;
a *= b + 5; //+的优先级高于 *= 所以 a = a * (b + 5) --> a = 7
printf("%d\n", a); //7
}
小端存储:高位数存放在高地址,低位数存放在低地址;
数值存储和地址存储都遵循小端存储
当一个程序开始运行时,默认会打开这三个文件;
程序案例:从键盘获取字符输出字符个数
#define PINT int* //宏替换,不考虑类型和大小
typedef int* TINT; //类型重命名,会进行类型和大小识别
int main()
{
PING a, b; //int* a, b;
TINT p, q; //int* p; int* q;
}
extern用在全局变量或者函数的声明之前,用来说明“此变量、函数是在别处定义的,要在此处引用”;
使用情景:同一个工程下的不同文件
文件fun.c
int g_max = 10;
void fun()
{
g_max +=10;
printf("%d\n", g_max);
}
文件main.c
#include
extern int g_max;
extern void fun();
int main()
{
int a = g_max;
fun();
}
C++中的extern的其他用法;
记忆函数:该函数中含有静态局部变量;
静态局部变量:当函数第一次被调用,函数中的局部静态变量被初始化,当这个函数被再次调用时,不会对该静态变量进行初始化,会保留上次函数执行结束后局部变量的值(作用域不变,生存期改变)
注意:C语言的静态局部变量只能用常量进行初始化一次;
C++可以用常量和变量进行初始化一次
问题解答:
形参能否加上static
答:加上,编译通过,但是该变量是一个“坏”存储类;
所以形参不加static
记忆函数是怎样实现第一次初始化的时候调用,后面不调用?
答:在编译阶段,编译器将记忆函数中的静态局部变量存放在.data段中并给该变量一个记录值val = 1,当程序执行到定义静态局部变量的语句时,先对记录值进行判断,如果val == 1说明第一次调用,执行完毕后val–;否则val == 0 ,则跳过这条语句;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-adZpltRu-1635073955445)(C:\Users\小小怪咯\AppData\Roaming\Typora\typora-user-images\image-20211024161744487.png)]
静态全局变量:静态全局变量只能在当前文件中使用(作用域受限制,生存期不变)
注意1:当全局变量、函数加上static后,作用域受限于本文件,其他文件无法访问;就算其他文件加上extern关键字声明也无法使用
main.c文件
#include
extern int g_max;
extern void fun();
int main()
{
int a = g_max; //编译报错,无法解析的命令g_max
fun();
return 0;
}
fun.c文件
static int g_max = 10;
static void fun()
{
printf("%d\n", g_max);
}
注意2:希望fun.c文件中的const int a = 10; 常变量被其他文件调用,就在该变量定义前加上extern,同时使用的文件也要加上该变量的extern声明
main.c文件
#include
extern int g_max;
int main()
{
int a = g_max;
printf("%d\n", a);
return 0;
}
fun.c文件
extern const int g_max = 10; //外部可见的常变量
//extern static int g_max = 10;
//extern外部可见与static本文件可见矛盾
静态函数:static说明的函数字可以在当前c文件中使用(作用域受限,生存期不变)
#include
int g_maxa = 10
int g_maxb;
int g_maxc = 0;
static int g_maxd; //默认是0
static int g_maxe = 0;
static int g_maxf = 10;
int main()
{
int maxa = 10
int maxb;
int maxc = 0;
static int maxd;
static int maxe = 0;
static int maxf = 10;
}
处理对象不同:const修饰的是定义的变量,而宏替换定义的是常量
处理时期不同:const修饰的变量是在编译期间确定,宏替换是在预编译期进行替换;
是否占用空间和有类型:const修饰的变量有大小和类型,宏替换的常量不占空间、不具有类型检查
就是单纯的替换
#include
#define SUM(x, y) x*y
int main()
{
int a = 3, b = 4;
int c = SUM(a + 1, b + 2);
//int c = a+1*b+2
printf("%d\n", c);
return 0;
}
//
解决方案
/
#define SUM(x, y) (x)*(y)
哼哼~啊啊啊啊啊~结束啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦!!!!!!!!!!!!!!!!!!!!!!!!!!!