C语言基础总结篇(究极避坑)

1.typedef:类型重命名

一切合法的变量名的定义(普通变量、指针变量、数组变量、函数指针、结构体)都可以用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");
}

结果:

C语言基础总结篇(究极避坑)_第1张图片

结构体经典写法:

#include 

struct Student
{
     
    ...;
    
}stu, *pstu;

//这样是定义了stu结构体变量和pstu结构体变量指针

//前面加上typedef后
typedef struct Student
{
     
   ...; 
    
}stu, *pstu;
//stu便成了自定义的结构体类型
//pstu变成了自定义的结构体指针的类型

//从而可以使用该类型进行定义变量

2.请问p和q的类型

int* p, q;

结果:

*和变量名结合,不是与类型名结合,所以p是int指针类型,q是int类型;
在这里插入图片描述

结合1,想同时定义p和q两个指针:

#include 
typedef int *PTR;

int main()
{
     
 	PTR p,q;
    return 0;
}

结果:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iHyypOrk-1635073955441)(C:\Users\小小怪咯\AppData\Roaming\Typora\typora-user-images\image-20211014154707137.png)]

3.关键字sizeof

  1. sizeof是一个关键字,在编译期间确定类型和大小;
#include 

int main()
{
     
    int a = 0;
    int x;
    
    //在编译期确定
    x = sizeof(++a);
    //等价于x = 4;
    printf("a = %d\n", a);
    return 0;
}

//结果a = 0
  1. sizeof和strlen()的区别
  • 调用时机不同:sizeof是关键字,编译期间确定类型和大小

    ​ strlen()是函数,在运行期间调用函数

  • 功能不同:strlen()是专门计算字符串的长度;

    ​ sizeof在计算字符串所占用的空间大小;

char buff[] = {
     "helloworld"};
int len = strlen(buff); //len = 10;
int size = sizeof(buff); // size = 11;

4、进制数转换的贪心算法

博客

5、c/c++的常变量不同侧重点

vs2019的全局变量未初始化默认为0,局部变量未初始化是随机值,使用该值编译不通过

  • c中的常变量侧重与"变量",不能使用常变量定义数组,编译期不通过;

  • c++中的常变量侧重于"常",可以使用该常变量定义数组;

  • C++常变量类似于宏,却有不同与宏

    • 编译时期不同
      • 与宏有所不同:宏在预编译时进行宏替换;使用常变量定义数组是在编译期进行确定的;
    • 是否存在类型和占用空间
      • 宏不存在类型,不占用空间
      • 常变量有类型,占用空间
    • 安全性
      • 宏不存在类型,没有类型检查,不安全
      • 常变量有数据类型,有类型检查,比较安全

6、’ ’ 和" "

’ ‘是字符的定界符, 在’前面加上\后转义变成单引号字符–》\’

例如:

char ch = ''';	//error ''是定界符,想使用单引号字符需要转义
char ch = '\'';	//true

""是字符串的定界符

7、ascii码值

C语言基础总结篇(究极避坑)_第2张图片

8、转义字符

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IgEV0wBA-1635073955443)(C:\Users\小小怪咯\AppData\Roaming\Typora\typora-user-images\image-20211023164550726.png)]

9、关于0 ‘0’ ‘\0’

C语言基础总结篇(究极避坑)_第3张图片

int main()
{
     
    char cha = 0;		//ASCII值为0对应的字符就是空字符
    char chb = '0';		//字符0对应的ASCII值48
    char chc = '\0';	//等于cha == 》空字符
    char chd = ' ';		//空格字符ascii值是32
	return 0;    
}

10、关于\000 和\xff

\000将八进制数000转换成十进制对应的ascii码值,码值对应的字符;

  • 其中八进制数有效范围000~377,因为char一字节最大取值255,其对应的八进制数就是377;八进制数超出该范围编译器就会报错
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
    

10、字符串与\0

字符串的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

11、宏和字符串

#define MAX 1000
int main()
{
     
    char str[] = {
     "helloMAX"};
    
    printf("%s\n", str);	//helloMAX
}
  • 原因:MAX是字符串的一部分,不是标识符,不会被宏替换

12、char ch = 'abcd’问题

C++有一个叫做“多字符文字”的东西。'1234'就是一个例子。他们有类型int,它是实现–定义了它们所具有的值以及它们可以包含多少字符。

那算不了什么直接与字符被表示为整数的事实有关,但在实现中,很有可能'1234'定义为:

'1' + 256 * '2' + 256 * 256 * '3' + 256 * 256 * 256 * '4'

或:

'4' + 256 * '3' + 256 * 256 * '2' + 256 * 256 * 256 * '1'

13、作用域(可见性)和生存期

  • 作用域:针对的是编译和链接的过程
    • 函数、全局变量从定义起(整个文件可见)全局可见,没有局部函数一说
  • 生存期(生命期):针对的是程序的执行过程
    • 局部变量的生存周期:函数被调用开始,函数执行结束时消亡,释放存储空间。存储在.stack区
    • 全局变量的生存期:从程序开始运行时开始,到程序执行结束时结束。存储在.data区
    • 动态生命期(堆区空间):标识符由特定的函数调用或运算来创建和释放,如果调用malloc()为变量分配存储空间开始,free()释放存储空间结束。存储在堆区.heap

编译错误:g_value未定的标识符

#include 
void Test()
{
     
 	int a = g_value;   
}
int g_value = 10;

int main()
{
     
    Test();
    return 0;
}

错误理解:误认为程序一边编译一边运行,g_value存在于.data段

14、C语言运算符优先级

优先级 运算符 名称或含义 使用形式 结合方向 说明
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
}

15、指针存储——小端存储

小端存储:高位数存放在高地址,低位数存放在低地址;

数值存储和地址存储都遵循小端存储

C语言基础总结篇(究极避坑)_第4张图片

16、标准输入文件0、标准输出文件1、标准错误输出文件2

当一个程序开始运行时,默认会打开这三个文件;

  • 标准输入文件stdin:对应的文件描述符为0,通过某种映射关系将键盘输入映射成标准输入文件;stdin在内存上是有行缓冲区的,当遇到换行(’\n’)才会输入到缓冲区;
  • 标准输出文件stdout:对应的文件描述符为1,通过某种映射关系将屏幕输出映射成标准输出文件;stdout在内存上是有行缓冲区的,当遇到换行(’\n’)才会输出到屏幕;
  • 标准错误文件stderr:对应的文件描述符为2,是无缓冲区的,是直接输出在屏幕上;

程序案例:从键盘获取字符输出字符个数

17、宏和typedef

#define PINT int*    //宏替换,不考虑类型和大小
typedef int* TINT;	//类型重命名,会进行类型和大小识别

int main()
{
     
	PING a, b;  //int* a, b;
	TINT p, q;  //int* p; int* q;
}

18、extern关键字

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的其他用法;

19、static关键字的使用

记忆函数:该函数中含有静态局部变量;

静态局部变量:当函数第一次被调用,函数中的局部静态变量被初始化,当这个函数被再次调用时,不会对该静态变量进行初始化,会保留上次函数执行结束后局部变量的值(作用域不变,生存期改变)

  • 注意:C语言的静态局部变量只能用常量进行初始化一次;

    C++可以用常量和变量进行初始化一次

问题解答:

  1. 形参能否加上static

    答:加上,编译通过,但是该变量是一个“坏”存储类;

    所以形参不加static

  2. 记忆函数是怎样实现第一次初始化的时候调用,后面不调用?

    答:在编译阶段,编译器将记忆函数中的静态局部变量存放在.data段中并给该变量一个记录值val = 1,当程序执行到定义静态局部变量的语句时,先对记录值进行判断,如果val == 1说明第一次调用,执行完毕后val–;否则val == 0 ,则跳过这条语句;

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-adZpltRu-1635073955445)(C:\Users\小小怪咯\AppData\Roaming\Typora\typora-user-images\image-20211024161744487.png)]

  • 注意:static int a = 10; 在多线程中需要考虑线程安全,多个线程同时执行该条语句,该值其中的val值会被同时拿到,这样就可能会多次执行该语句。单例模式的问题就需要考虑线程安全

静态全局变量:静态全局变量只能在当前文件中使用(作用域受限制,生存期不变)

  • 注意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 = 10static 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文件中使用(作用域受限,生存期不变)

20、4G的虚拟空间

C语言基础总结篇(究极避坑)_第5张图片

21、数据在内存中存放的位置

#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;
}

22、const修饰定义的变量和#define宏替换的区别(见5)

  • 处理对象不同:const修饰的是定义的变量,而宏替换定义的是常量

  • 处理时期不同:const修饰的变量是在编译期间确定,宏替换是在预编译期进行替换;

  • 是否占用空间和有类型:const修饰的变量有大小和类型,宏替换的常量不占空间、不具有类型检查

23、浅谈宏函数

就是单纯的替换

#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)

哼哼~啊啊啊啊啊~结束啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦啦!!!!!!!!!!!!!!!!!!!!!!!!!!!

你可能感兴趣的:(C++,每日复习,1024程序员节,c)