C语言学习笔记--一文读懂C语言基本概念

打印

编程中的打印指的是输出
printf:格式化输出函数

转义符

所有的ASCII码都可以用“\”加数字(一般是8进制数字)来表示。而C中定义了一些字母前加""来表示常见的那些不能显示的ASCII字符,如\0,\t,\n等,就称为转义字符,因为后面的字符,都不是它本来的ASCII字符意思了。
在这里插入图片描述

变量

变量的意义就是确定目标并提供存放的空间
C语言的变量命名规则
大小写字母(a-z),数字(0-9) 下划线(_)
注意,大小写字母不一样…即a1和A1 是两个不同的标识符;
长度任意 但是最少一个字符

C语言中封装好/内置的变量

在这里插入图片描述
C99新增了5个关键字
1、inline关键字用来定义一个类的内联函数,引入它的主要原因是用它替代C中表达式形式的宏定义

引入原因:C语言是一个效率很高的语言,这种宏定义在形式及使用上像一个函数,但它使用预处理器实现,没有了参数压栈,代码生成等一系列的操作

2、restrict关键字只用于限定指针;该关键字用于告知编译器,所有修改该指针所指向内容的操作全部都是基于(base on)该指针的,即不存在其它进行修改操作的途径;这样的后果是帮助编译器进行更好的代码优化,生成更有效率的汇编代码。

3、_Bool关键字是用于表示布尔值。包含标准头文件 stdbool.h 后,我们可以用 bool 代替 _Bool ,true 代替 1 ,false 代替 0 。

4、_Complexand_Imaginary关键字

C99标准中定义的复数类型如下:float_Complex; float_Imaginary; double_Complex; double_Imaginary; long double_Complex; long double_Imaginary.

头文件中定义了complex和imaginary宏,并将它们扩展为_Complex和_Imaginary,因此在编写新的应用程序时,应该使用头文件中的complex和imaginary宏。

C11新增7个关键字
_Alignas:内存对齐指示符
以前往往通过编译器的编译参数指定内存对齐方式。

_Alignof:对齐处理操作符
函数 aligned_alloc(),以及 头文件 <stdalign.h>。
对齐内存有利于提高程序运行效率,内存访问效率。
alignof(Foo) //值为4,对齐长度
sizeof(Foo) //结构体的总大小:12

_Atomic:原子操作
_Atomic int i; //原子类型的变量
atomic_store(&i,12); //stdatomic.h中的宏

_Generic:泛型
_Generic((var), type1:…, type2: …, ……, default:…)
#define GENERAL_ABS(x) _Generic((x),int:abs,float:fabsf,double:fabs)(x) 根据类型不同调用不同的函数,类似函数模板。

_Noreturn:指定该函数不返回到其调用点。
_Static_assert:静态断言
在编译时刻进行,断言表达式必须是在编译时期可以计算的表达式

_Thread_local:限定了变量不能在多线程之间共享

变量的命名区分大小写
变量的命名不能与C语言中的关键字重复
变量的命名中的第一个字母必须是字母或者下划线的开头

c语言的数据类型

在这里插入图片描述

常量

在程序的运行过程中,它的值不能被改变,这样的量我们称之为常量
整形常量,由整数组成的常量,对应python中的整形
实型常量,由浮点数组成的常量,对应python中的浮点型
字符常量,普通字符,如’G’,转义字符,如’\n’,’\t’,’\b’
字符串常量,对应python中的字符串
符号常量,使用之前必须先定义,格式:#define 标识符 常量
比如 #define GW Greatwall→定义Greatwall为GW

预处理命令,有"#"开头的命令都被称之为预处理命令

取值范围

比特位:CPU能读懂的最小数据单位即bit又写为b
字节:内存机构的最小寻址单位即byte又写为B
约定俗成,规定一个B的大小为8个b的大小之和
即一个字节由八个比特位构成

进制

在这里插入图片描述
补码:
计算机是通过补码的方式来存放整数的值
整数的补码是该数的二进制形式
负数的补码需要通过以下几步获得:
1,取得该数绝对值的二进制形式
2,将第一步中的值按位取反
3,将取反后的值加一

数据类型对应的取值范围

在这里插入图片描述

字符和字符串

将输入的数值指定为%c时,它输入的字符会自动对应到ASCII字符表中的字符
在这里插入图片描述

故字符类型是一个特殊的整形

以下操作类似python中的列表


声明字符串
char name[5]
给字符串赋值
name[0] = 'F';
name[1] = 'I';
name[2] = 'J';
name[3] = 'F';
name[4] = 'K';

一般来说当字符过多的时候,要加一个"\0"在句尾表示结束
也有偷懒的办法,比如name[5]写成name[]就可以无限添加字符且不用加一个"\0"在句尾表示结束

算术运算符

在这里插入图片描述
操作数是运算符作用于的实体,是表达式中的一个组成部分,它规定了指令中进行数字运算的量 。
表达式是操作数与操作符的组合。

一个运算符号能同时应对两个操作数,称之为双目运算符
比如1+1,这里的加号是双目运算符
当加号只用来表示正数的时候,就是单目运算符
比如+1,此时加号是单目运算符

运算符的优先级和结合性
在这里插入图片描述
在这里插入图片描述

C语言的数据类型强制转换
强制类型转换是把变量从一种类型转换为另一种数据类型。

例如,如果想存储一个long类型的值到一个简单的整型中,需要把long类型强制转换为int类型。可以使用强制类型转换运算符来把值显式地从一种类型转换为另一种类型。

发生在同一个编译系统中,比如说把int转化为longint,在VC++6.0里面,longint和int都是4个字节,不会有问题的,但是如果把int类型转化为short类型那就装不下了,简而言之就是长类型不能转化为短类型。

int类型如果要转化为float、double、longdouble类型的时候只是末尾多了几个0,但是反之转化,就会出现数据的损失,小数部位都被省略掉了。

赋值中的类型转换

当赋值运算符两边的运算对象类型不同时,将要发生类型转换, 转换的规则是:把赋值运算符右侧表达式的类型转换为左侧变量的类型。

由于C语言中的浮点值总是用双精度表示的,所以float 型数据只是在尾部加0延长为double型数据参加运算,然后直接赋值。double型数据转换为float型时,通过截尾数来实现,截断前要进行四舍五入操作。

关系运算符

用于比较两个数的大小关系的运算符
在这里插入图片描述

逻辑运算符

在这里插入图片描述

短路求值
作为"&&“和”||"操作符的操作数表达式,这些表达式在进行求值时,只要最终的结果已经可以确定是真或假,求值过程便告终止,这称之为短路求值。这是这两个操作符的一个重要属性。假如expr1和expr2都是表达式,并且expr1的值为0,在下面这个逻辑表达式的求值过程中:
  expr1 && expr2
  expr2将不会进行求值,因为整个逻辑表达式的值已经可以确定为0。 类似地,如果expr1的值不是0,那么在下面的这个逻辑表达式的求值过程中:
  expr1 || expr2
  expr2将不会进行求值,因为整个逻辑表达式的值已经确定为1。

分支结构

代码示例

#include <stdio.h>

int main()
{
	int i;
	printf("今年多大?");
	scanf("%d", &i);
	if (i >= 18)
	{
		
		printf("进门左拐\n");
	}
	return 0;


}

C语言中的分支结构有三种:

1、单分支结构

单分支结构的基本形式是:

if( <条件表达式> )

{

(语句块)

}

2、双分支结构

双分支结构的基本形式是:

if( <条件表达式> )

{

语句块1;

}

else

{

语句块2;

}

3、多分支结构

多分支结构又分为:“分支结构的嵌套”与“switch语句”

(1)分支结构的嵌套的基本形式是:

if( <条件表达式> )

{

if( <条件表达式> )

{

语句块1

}

else

{

语句块2

}

}

else

{

if( <条件表达式> )

{

语句块3  

}

else

{

 语句块4  

}

}

(2)switch语句

(2)switch语句的基本形式是:

switch(表达式)

{

case 常量1:语句1;break;

case 常量2:语句2;break;

case 常量3:语句3;break;

……

case 常量n:语句n;break;

default : 语句n+1;

}

这三种分支结构的区别在于:

1、单分支结构在满足条件时执行,不满足条件不执行;

2、在双分支结构中,如果条件表达式成立,则执行语句块1,否则,执行语句块2,所以双分支结构至少执行一次;

3、多分支结构的分支结构的嵌套中,分支结构的嵌套包含了单分支结构与双分支结构的特性,可以用单分支结构嵌套双分支结构,也可用双分支结构嵌套单分支结构,通俗的来讲就是分支结构中放入一个分支结构,当条件成立则执行分支结构中嵌套的分支结构;

4、switch语句可以理解成其他的分支结构的总和,switch集成了上述所有分支结构的特性,在有多种情况的时候才使用switch,当switch(条件)满足case 后面的常量,则执行对应的语句,若满足的条件未能匹配到对应的常量,则执行default后的语句,有default的switch语句是至少执行一次的,当然default也可省略不写,这样switch语句也可以一次都不执行。

几个常见的bug

1悬挂esle
在C语言中有这样一种形式的代码:

if (a > 8)  
     if (b > 8)  
         printf("===========");  
 else  
     printf("。。。。。。。。。。。。。。。。。");  

由于C编译器会忽略空白
上面的语句本来想是else与第一个if搭配的,事实上else却是和内部的if搭配了

解决方案
把对应的语句块加入到同一个大括号{}里

循环结构

while循环

执行顺序

while 循环的执行顺序非常简单,它的格式是:

while (表达式)
{
        语句;
}

当表达式为真,则执行下面的语句;语句执行完之后再判断表达式是否为真,如果为真,再次执行下面的语句;然后再判断表达式是否为真……就这样一直循环下去,直到表达式为假,跳出循环。这个就是 while 的执行顺序。

do-while循环
do-while 循环的格式如下。

do{
    Statement _1;
    Statement _2;
}while(Exp_cntrl);

当循环体为一条简单语句时,可以省略 {},即:

do
    Simp1e_Statement;//循环体
while(Exp_cntrl);

在 do-while 结构中,while 括号后的分号不能丟

do-while 循环的执行流程是:首先无条件地执行一次循环体,然后再根据循环控制表达式的值来判断是否继续执行循环体。若为真,则继续执行;若为假,则停止执行,退出 do-while 循环。即do-while 循环至少执行一次循环体。

for语句

c语言中for语句用法:


int i;

for(i=0; i<10; i++)   

// i=0是初始化部分;i<10是循环判断条件部分(当满足此条件时才进入执行for循环中的语句);i++是执行完循环体语句后的操作

{

printf("这是for循环的使用示例");

}




我们一般不写死循环,但是死循环也是有用处的,比如游戏里面不断随机刷出的小怪

循环嵌套

这里的循环嵌套是先执行里面再执行外面的

#include <stdio.h>
int main()
{
    int i, j;
    for(i=1; i<=4; i++){  //外层
        for(j=1; j<=4; j++){  //内层
            printf("i=%d, j=%d\n", i, j);
        }
        printf("\n");
    }
    return 0;
}

break语句和continue语句

break用于跳出switch结构。在循环语句中,break语句用于直接跳出循环,break往往和if语句配合使用,当检测到满足某个条件时,强行结束循环。在循环中,break只是结束其所在层的循环,对外层循环没有影响。

ontinue语句与break语句不同,在循环体内遇到continue语句时,将跳过本层循环体内continue语句之后的部分循环体,并开始下一轮循环,即只结束本轮循环。continue语句也通常和if语句配合使用,以控制在特定的条件下,仅执行循环体的一部分。需要注意的是,使用continue和使用break类似,它只能控制本层循环,并不影响外层循环。

一点补充

i++和++i

对于变量i来说都是在原来基础上加1,重点区别在于式子本身的值;

1、i++式子值为i原来的值,即“先赋值再加1”;

2、而++i式子值为i变化后的值,即“先加1后赋值”;

for循环中,若表示递增,两种都可

逗号表达式
总的来说就是从左往右逐个计算表达式其运行的优先级为最低
在C语言中,多个表达式可以用逗号分开,其中用逗号分开的表达式的值分别结算,但整个表达式的值是最后一个表达式的值。
假设b=2,c=7,d=5,
a1=(++b,c–,d+3)
a2=++b,c–,d+3
对于第一行代码,有三个表达式,用逗号分开,所以最终的值应该是最后一个表达式的值,也就是d+3,为8,所以a1=8。
对于第二行代码,那么也是有三个表达式,这时的三个表达式为a2=++b、c–、d+3,(这是因为赋值运算符比逗号运算符优先级高)所以最终表达式的值虽然也为8,但a2=3。

条件运算符

goto语句
这是汇编语言的产物
C语言中goto又叫无条件转移语句,可以让程序直接跳转到任意标记的位置。用法就是“goto label……label

随意使用goto语句在代码里面跳来跳去会破坏代码原有的逻辑
只有在使用多层循环并且要在最里面的那层循环跳到最外层的时候这种情况使用goto语句是情有可原的

数组

数组的定义方式
类型 数组名[元素个数]

int a[6];
char b[24];
double c[23];

访问数组中的元素
数组名[下标] 这里的下标指的就是第几个,注意,0是第一个
类似python里面列表数据的提取,注意,计算机从0开始计数

数组的初始化

//将数组中的所有元素初始化为0
int b[100] = {0};//这里只是将第一个元素初始化为0


//如果是赋予不同的值那么只是用逗号分隔开即可

int b[5] = {0,1,2,3,4};

//给一部分元素赋值,未被赋值的元素将会被初始化为0
int b[100] = {0,1,2,3,4};


//可以只给各个元素的值,不指定数组的长度(编译器会根据值的个数自动判断数组的长度)
int b[] = {0,1,2,3,4};

//指定初始化元素,未被赋值的则会初始化为0
int b[5] = {[1] = 0,[2] = 1,[3] = 2,[4] = 3,[5] = 4};

字符串处理函数

获取字符串的长度:strlen

#include <stdio.h>
#include <string.h>

int main(){
    char str[150] = { 0 };
    size_t len;
    gets(str);
    len = strlen(str);
    printf("Length: %d\n", len);

    return 0;
}

拷贝字符串

strcpy和strncpy

strcpy

#include <stdio.h>
#include <string.h>

int main(){
    char dest[50] = { 0 };
    char src[50] = { "这是拷贝字符串的拷贝对象" };
    strcpy(dest, src);
    puts(dest);

    return 0;
}

strncpy

char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串复制到 dest,最多复制 n 个字符。当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。

char *strncpy(char *dest, const char *src, size_t n)
参数
dest -- 指向用于存储复制内容的目标数组。
src -- 要复制的字符串。
n -- 要从源中复制的字符数。

连接字符串

strcat和strncat

strcat

#include <stdio.h>
#include <string.h>

int main ()
{
    char str[80];
    strcpy (str,"A ");
    strcat (str,"B ");
    strcat (str,"C ");
    strcat (str,"D.");
    puts (str);
    return 0;
}

输出结果:ABCD.

strncat

char *strncat(char *str1, const char *str2, size_t n)

str1 – 目标字符串。

str2 – 附加在目标字符串str1末尾的源字符串。

n – 需要追加的源字符串str2的字符数。例如,如果这是 5,则只有源字符串str2的前 5 个字符将附加在目标字符串str1的末尾。

返回值:该函数返回指向目标字符串str1的指针。

strcmp和strncmp

strcmp

头文件:string.h

语法/原型:
int strcmp(const char* stri1,const char* str2);

参数 str1 和 str2 是参与比较的两个字符串。

strcmp() 会根据 ASCII 编码依次比较 str1 和 str2 的每一个字符,直到出现不到的字符,或者到达字符串末尾(遇见\0)。

返回值:
如果返回值 < 0,则表示 str1 小于 str2。
如果返回值 > 0,则表示 str2 小于 str1。
如果返回值 = 0,则表示 str1 等于 str2。

strncmp

头文件:#include <string.h>

strncmp() 用来比较两个字符串的前n个字符,区分大小写,其原型为:
    int strncmp ( const char * str1, const char * str2, size_t n );


参数 str1, str2 为需要比较的两个字符串,n为要比较的字符的数目。

字符串大小的比较是以ASCII 码表上的顺序来决定,此顺序也为字符的值。strncmp()首先将s1 第一个字符值减去s2 第一个字符值,若差值为0 则再继续比较下个字符,直到字符结束标志’\0’,若差值不为0,则将差值返回。例如字符串"Ac"和"ba"比较则会返回字符"A"(65)和’b’(98)的差值(-33)。

注意:要比较的字符包括字符串结束标志’\0’,而且一旦遇到’\0’就结束比较,无论n是多少,不再继续比较后边的字符。

返回值 若str1与str2的前n个字符相同,则返回0;若s1大于s2,则返回大于0的值;若s1 若小于s2,则返回小于0的值。

注意:如果两个字符不同,GCC返回该字符对应的ASCII码的差值,VC返回-1或1。但是让人为难的是,strnicmp()、strcmp()、stricmp()在GCC和VC下都返回-1或1,而不是ASCII的差值

二维数组

二维数组定义的一般形式是:
类型说明符 数组名[常量表达式1][常量表达式2]
其中常量表达式1表示第一维下标的长度,常量表达式2 表示第二维下标的长度。

如:

int a[3][4];//三行四列

说明了一个三行四列的数组,数组名为a,其下标变量的类型为整型。该数组的下标变量共有3×4个,即:
a[0][0], a[0][1], a[0][2], a[0][3]
a[1][0], a[1][1], a[1][2], a[1][3]
a[2][0], a[2][1], a[2][2], a[2][3]

二维数组的访问

数组名[下标][下标]
a[1][3]//访问a数组中第2行第4列的元素

超出任何一个下标的访问都是越界访问

二维数组的初始化

二维数组的初始化是可以偷懒的,能让编译器根据元素的数量计算数组的长度但只有第一维的元素个数可以不写

int a[][4] = {{1,2,3,4},{5,6,7,8},{9,10,11,12}};

指针!!!!!!!!!!!!!!!!!!!!!

定义指针变量
类型名*指针变量名

类型名*指针变量名
char*pa;//定义一个指向字符型的指针变量
int*pb;//定义一个指向整型的指针变量

取地址运算符和取值运算符
需要获取某个变量的地址可以使用取地址运算符(&)

char *pa = &a;
int *pb = &f;

要访问指针变量指向的数据,可以使用取值运算符(*)

printf("%c,%d\n",*pa,*pb);

注意,要避免访问未初始化的指针,逻辑和语法上没有问题,但是一旦产生bug非常难排查

指针和数组

数组名其实是数组第一个元素的地址
当我们要用一个指针指向一个数组的时候

char*p
p = a;//语句1
p = &a[0];//语句2

即只要将指针指向数组第一个元素的首地址即可
当指针指向元素的时候,我们可以对指针变量进行加减运算,这样做的意义相当于指向距离指针所在位置向前或者向后第N个元素

指针和数组的区别

指针数组

int *p1[5];

指针数组是一个数组,每个数组元素存放一个指针变量

数组指针

int *p2[5];

数组指针是一个指针,它指向的是一个数组

待补充------------------------------------------------------------

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丰。。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值