栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

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

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

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");
}

结果:

结构体经典写法:

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

结果:

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

int main()
{
    int a = 0;
    int x;
    
    //在编译期确定
    x = sizeof(++a);
    //等价于x = 4;
    printf("a = %dn", 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码值

8、转义字符

9、关于0 ‘0’ ‘’
int main()
{
    char cha = 0;		//ASCII值为0对应的字符就是空字符
    char chb = '0';		//字符0对应的ASCII值48
    char chc = '';	//等于cha == 》空字符
    char chd = ' ';		//空格字符ascii值是32
	return 0;    
}
10、关于00 和xff

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

  • 其中八进制数有效范围000~377,因为char一字节最大取值255,其对应的八进制数就是377;八进制数超出该范围编译器就会报错
char str[] = {"pzj141hello"};	
//八进制的141转换成十进制的97	//pzjahello
//如果是"pzj1411hello"	//pzja1hello
//如果是"pzj148hello"		//只会转义14因为8超出0~7
//如果是"pzj889hello"		//此时的就会被省略
	
int len = strlen(str);				//len = 9

x00将十六进制的00转换成十进制对应的ASCII码值,码值对应的字符;

  • 其中十六进制数的有效范围是0~ff,因为char最大255,对应的十六进制数就是ff;十六进制数超出该范围也会报错

    char str[] = {"hello61xworld"};	//helloaworld
    
10、字符串与

字符串的printf("%s")打印、strcpy拷贝、strcat连接、等函数都是以字符串的作为结束条件

char str[] = {"helloworld"};
int size = sizeof(str);		//size = 12
int len = strlen(str);		//len = 5
	
printf("%sn", str);		//hello
11、宏和字符串
#define MAX 1000
int main()
{
    char str[] = {"helloMAX"};
    
    printf("%sn", 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("%dn", a);		//7
}
15、指针存储——小端存储

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

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

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("%dn", 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 ,则跳过这条语句;

  • 注意: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 = 10;
static void fun()
{
    printf("%dn", 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("%dn", a);
    return 0;
}

fun.c文件

extern const int g_max = 10;	//外部可见的常变量

​ //extern static int g_max = 10;
​ //extern外部可见与static本文件可见矛盾

静态函数:static说明的函数字可以在当前c文件中使用(作用域受限,生存期不变)

20、4G的虚拟空间

21、数据在内存中存放的位置
#include 
int g_maxa = 10	//.data
int g_maxb;  	//.bss
int g_maxc = 0;	//.bss
static int g_maxd;    	//.bss   //默认是0
static int g_maxe = 0;	//.bss
static int g_maxf = 10;	//.data
int main()
{
	int maxa = 10;   //编译时期,当作指令存放在.text,运行阶段在栈上通过指令定义变量
    int maxb;		//.text
    int maxc = 0;	//.text
    static int maxd;  		//.bss
   	static int maxe = 0;	//.bss
    static int maxf = 10;	//.data
}
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("%dn", c);
    return 0;
}
//
解决方案
/
#define SUM(x, y) (x)*(y)

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

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/347639.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号