c语言初级内容总结

c语言初级内容总结,第1张

C 语言教程 | 菜鸟教程

华为C语言编程规范(精华总结)_不脱发的程序猿的博客-CSDN博客_华为c语言编程规范

01

环境配置C 环境设置 | 菜鸟教程

02

基本结构C 程序结构 | 菜鸟教程

C语言写代码步骤:
第一步:编辑源代码(使用vi或者其他编辑器)
第二步:编译。编译就是用编译器把源程序转化成可执行程序的过程,编译要用到编译器。我们在linux中使用编译器一般是gcc。
譬如:gcc hello.c    把当前目录下hello.c文件编译,得到的可执行文件名字叫a.out
也可以自己指定编译后生成的可执行程序的名字,使用gcc hello.c -o hello
第三步:执行编译生成的可执行程序,执行方式是./hello
第四步:调试。当你执行后发现程序结果不对,不是自己想要的,这时候就是返回来看源代码哪里不对。然后修改,再编译执行,再看结果。如此循环直接结果正确。

03

基本语法C 基本语法 | 菜鸟教程 

关于C语言的关键字:

C语言关键字及其解释[共32个]_阿强打代码的博客-CSDN博客_c语言的32个关键字及其含义

问:Include和main是c语言的关键字吗? - 知乎

输出语句:

【C语言】- printf 和scanf 函数详解!_C语言编程俱乐部的博客-CSDN博客_c printf scanf

格式符补充c语言printf()输出格式大全(转载)_18790970257的博客-CSDN博客

注释/转义字符/接续字符:

c语言学习笔记(6)注释符号,接续符号和转义符号的用法_漫步的风暴的博客-CSDN博客

C语言 接续符和转义符_华哥-2088的博客-CSDN博客_连接符

看了这两篇文章,没明白\ddd和\xhh啥意思。

首先了解计算机进制:【最强干货】详解二进制,八进制,十进制,十六进制的相互转换_哔哩哔哩_bilibili

进制转换(二进制、八进制、十进制、十六进制)涵盖整数与小数部分,超详细_DJL微光的博客-CSDN博客_进制转化表

在线进制转换器 | 菜鸟工具

比较好理解的几个转换:十进制到其他进制,其他进制到十进制,十六进制八进制和二进制之间

再了解ASCLL码ASCII_百度百科

04

数据类型C 数据类型 | 菜鸟教程

b、B、KB、MB、GB 的关系?_ITROOKIEIS的博客-CSDN博客_b kb mb,可结合ASCLL码进行思考,一个字符占1个字节,即8位。汉字占两个字节。

C语言:超详细的C语言中的数据类型_Upupup6的博客-CSDN博客_c语音数据类型

有符号数和无符号数
数学中数是有符号的,有整数和负数之分。所以计算机中的数据类型也有符号,分为有符号数和无符号数。
    
有符号数:
    整形:signed int(简写为 int)
          signed long,也写作signed long int,(简写为long)
          signed short,也写作signed short int(简写为short)
          signed(表示signed int)
    浮点型:
          signed float(简写为float)
          signed double(简写为double)
    字符型:
          signed char(简写为char)
        
无符号数:
    整形:整形有无符号数,用来表示一些编码编号之类的东西。譬如身份z号,房间号
        unsigned int(没有简写)
        unsigned long int(简写unsigned long)
        unsigned short int(简写为unsigned short)
    
    浮点数:没有无符号浮点数。也就是说,小数一般只用在数学概念中,都是有符号的。
    
    字符型:字符型有无符号数
        unsigned char(没有简写)

注意:对于整形和字符型来说,有符号数和无符号数表示的范围是不同的。
譬如字符型,有符号数范围是-128~127,无符号数的范围是0~255

C语言数据类型转换详解_风叶翩翩的博客-CSDN博客_c语言数据类型转换

C语言中double\float类型默认输出几位小数_山鹿有角的博客-CSDN博客_float默认保留几位小数

c语言字符串_viafcccy的博客-CSDN博客_c语言字符串

C语言关于为什么字符串不能直接赋值_choujiao7447的博客-CSDN博客

05

变量C 变量 | 菜鸟教程

C语言中变量的作用域和生命周期_zsjalive的博客-CSDN博客_c语言变量的作用域和生命周期

06

常量C 常量 | 菜鸟教程

07

运算符C语言中的所有运算符用法及总结_June·D的博客-CSDN博客_c语言运算符

C语言中的++和--运算符_Anntonnia的博客-CSDN博客_c语言中++

08

程序结构 C语言之三种基本结构_WongKyunban的博客-CSDN博客_c语言的三种基本结构

C语言for循环详解_风叶翩翩的博客-CSDN博客_c语言for循环

09

函数C语言函数_C语言技术网-码农有道的博客-CSDN博客_c语言函数

史上最全C语言函数详解笔记_小赵小赵福星高照~的博客-CSDN博客_c语言函数

函数调用的过程,其实就是实参传递给形参的一个过程。这个传递实际是一次拷贝。实际参数的时候,实参(本质是一个变量)本身并没有进入到函数内,而是把自己的值复制了一份传给了函数中的形参,在函数中参与运算。这种传参方法,就叫做传值调用。 

C语言函数调用三种方式:传值调用,引用调用和传地址调用_yuyaweibest的博客-CSDN博客_传值调用

C语言常用库函数大全 - C语言网

10

数组c语言—数组详解(建议收藏)_bruin_du的博客-CSDN博客_c语言数组

创建一个数组  :char a[10] ;

a作为右值 , 很多人估计也在学习的时候,估计会把它作为数组的地址,这是错误的 !a作为右值时代表的意义和 &a[0]的意义是一样的, 代表数组首元素的首地址 ,而不是数组的地址。
注意:这里只是“当作”,并没有一个地方来存储这个地址。

上面说了a作为右值,我们清楚了其含义,那么a作为左值呢?

a不能作为左值 !!!编译器会认为数组名作为左值代表的是a的首元素的首地址,但是这个地址开始的一块内存是一个整体,我们只能访问数组的某个元素,而无法把数组数组当做一个整体来进行访问。所以,我们可以把a[i]当左值,无法把a当左值。也可以这么理解:a的内部是由很多小部分组成,我们只能通过访问这些小部分来达到访问 a的目的。

11

指针C语言指针详解(超级详细)_sycgogogo的博客-CSDN博客_c语言指针

C/C++中声明指针变量时星号是靠近变量名还是靠近类型?_weixin_34247155的博客-CSDN博客

指针定义的两种理解方法:
int *p;    
第一种:首先看到p,这个是变量名;其次,p前面有个*,说明这个变量p是一个指针变量;最后,*p前面有一个int,说明这个指针变量p所指向的是一个int型数据。

char *(*(*pfunc)[])(char *, char *) 类似的复杂表达式,可以用相同的分析方法得到

第二种:首先看到p,这个是变量名;其次,看到p前面的int *,把int *作为一个整体来理解,
int *是一种类型(复合类型),该类型表示一种指向int型数据的指针。

总结:第二种方法便于理解,但是不够本质;建议用第一种方法来理解,因为这种思维过程可以帮我们理解更复杂的表达式。

指针和数组: 

指针与数组的初步结合

数组名:做右值时,或者单独输出时,数组名表示数组的首元素首地址,因此可以直接赋值给指针。
如果有 int a[5];
则 a和&a[0]都表示数组首元素a[0]的首地址。
而&a则表示数组的首地址。

注意:数组首元素的首地址和数组的首地址是不同的。前者是数组元素的地址,而后者是数组整体的地址。两个东西的含义不同,但是数值上是相同的。

根据以上,我们知道可以用一个指针指向数组的第一个元素,这样就可以用间接访问的方式去逐个访问数组中各个元素。这样访问数组就有了两种方式。
有 int a[5];  int *p; p = a;
数组的方式依次访问:a[0]       a[1]            a[2]            a[3]            a[4]
指针的方式依次访问:*p        *(p+1)        *(p+2)        *(p+3)         *(p+4)

指针的运算:

指针与++ --符号进行运算

指针本身也是一种变量,因此也可以进行运算。但是因为指针变量本身存的是某个其他变量的地址值,因此该值进行* / %等运算是无意义的。两个指针变量相加本身也无意义,相减有意义。指针变量+1,-1是有意义的。+1就代表指针所指向的格子向后挪一格,-1代表指针所指向的格子向前挪一格。

*p++就相当于*(p++),p先与++结合,然后p++整体再与*结合。

*p++解析:++先跟p结合,但是因为++后置的时候,本身含义就是先运算后增加1(运算指的是p++整体与前面的*进行运算;增加1指的是p+1),所以实际上*p++符号整体对外表现的值是*p的值,运算完成后p再加1.
所以*p++等同于:*p;   p += 1;

*++p等同于 p += 1;    *p;

(*p)++,使用()强制将*与p结合,只能先计算*p,然后对*p整体的值++。

++(*p),先*p取值,再前置++,该值+1后作为整个表达式的值。

总结:++符号和指针结合,总共有以上4种情况。--与++的情况很类似。

C语言中的++和--运算符_Anntonnia的博客-CSDN博客_c语言中++

指针与函数: 传值调用与传址调用C语言中函数调用中的传值与传址_ljx_csdn的博客-CSDN博客_函数传值和传地址

详解C语言指针函数、函数指针、函数指针数组_HUST_Miao的博客-CSDN博客_函数指针数组

深入理解C语言指针_ZackSock的博客-CSDN博客_c语言指针的概念

更多数据类型:

12

结构体C语言结构体(struct)最全的讲解(万字干货)_chooseboy的博客-CSDN博客_结构体c语言

13

共用体C语言之共用体(union)_小莱昂纳德的博客-CSDN博客_c语言共用体

 C语言之union共同体_姚镜堂的博客-CSDN博客_共同体c语言

14

宏定义详解宏定义(#define)___Sunshine_的博客-CSDN博客_宏定义

C语言中宏定义的使用_冷冷的那一风的博客-CSDN博客_宏定义

15

枚举C语言基础教程之enum_kaikeba的博客-CSDN博客

C语言 枚举详解_我是唐的博客-CSDN博客_c 枚举

枚举类型的变量,只能接收枚举成员值,除此之外的所有值均不合法。

补充: 

01

数据内存大小与sizeof

C语言各数据类型大小和取值范围_Dream Flying Eagle的博客-CSDN博客_c语言int范围值的大小

c语言中sizeof详解_Nerazzur的博客-CSDN博客_sizeof 

02

数据类型、有符号数和无符号数

对于float和double这种浮点型数来说,只有有符号数,没有无符号数。

对于float和double这种浮点类型的数,它在内存中的存储方式和整形数不一样。所以float和
int相比,虽然都是4字节,但是在内存中存储的方式完全不同。所以同一个4字节的内存,如果存储时是按照int存放的,取的时候一定要按照int型方式去取。如果存的时候和取的时候理解的方式不同,那数据就完全错了。


存取方式上主要有两种,一种是整形一种是浮点型,这两种存取方式完全不同,没有任何关联,所以是绝对不能随意改变一个变量的存取方式。在整形和浮点型之内,譬如说4种整形char、short、int、long只是范围大小不同而已,存储方式是一模一样的。float和double存储原理是相同的,方式上有差异,导致了能表示的浮点型的范围和精度不同。

C语言系列(二)有符号数和无符号数详解_徐昊Xiho的博客-CSDN博客_有符号和无符号的区别

C语言中整形、浮点型数据在内存中的存储和使用_T_NaAIsi的博客-CSDN博客 

C语言中的void类型,代表任意类型,而不是空的意思。任意类型的意思不是说想变成谁就变成谁,而是说它的类型是未知的,是还没指定的。


void *    是void类型的指针。void类型的指针的含义是:这是一个指针变量,该指针指向一个
void类型的数。void类型的数就是说这个数有可能是int,也有可能是float,也有可能是个结构体,哪种类型都有可能,只是我当前不知道。

void型指针的作用就是,程序不知道那个变量的类型,但是程序员自己心里知道。程序员如何知道?当时给这个变量赋值的时候是什么类型,现在取的时候就还是什么类型。这些类型对不对,能否兼容,完全由程序员自己负责。编译器看到void就没办法帮你做类型检查了。

在函数的参数列表和返回值中,void代表的含义是:
一个函数形参列表为void,表示这个函数调用时不需要给它传参。


返回值类型是void,表示这个函数不会返回一个有意义的返回值。所以调用者也不要想着去使用该返回值。

C语言与bool类型
C语言中原生类型没有bool,C++中有。在C语言中如果需要使用bool类型,可以用int来代替。


很多代码体系中,用以下宏定义来定义真和假
#define TRUE    1
#define FALSE    0

除了0之外,其他全是真,包括负数。

03

数据类型转换

C语言数据类型转换详解_风叶翩翩的博客-CSDN博客_c语言数据类型转换

隐式转换:
隐式转换就是自动转换,是C语言默认会进行的,不用程序员干涉。
C语言的理念:隐式类型转换默认朝精度更高、范围更大的方向转换。

强制类型转换:
C语言默认不会这么做,但是程序员我想这么做,所以我强制这么做了。

转换是临时的,并不会永久改变数据的原有类型。

04

变量和常量

C语言中变量的作用域和生命周期_zsjalive的博客-CSDN博客_c语言变量的作用域和生命周期

判断一个变量能不能使用,有没有定义,必须注意两点:第一,该变量定义的作用域是否在当前有效,是否包含当前位置;第二,变量必须先定义后使用。所以变量引用一定要在变量定义之后。 

 局部变量和全局变量对比:

1、定义同时没有初始化,则局部变量的值是随机的,而全局变量的值是默认为0。
2、使用范围上:全局变量具有文件作用域,而局部变量只有代码块作用域(所谓代码块,就是用{}括起来的一段代码)
3、生命周期上:全局变量是在程序开始运行之前的初始化阶段就诞生,到整个程序结束退出的时候才死亡;而局部变量在进入局部变量所在的代码块时诞生,在该代码块退出的时候死亡。
4、变量分配位置:全局变量分配在数据段上,而局部变量分配在栈上。

普通局部变量(auto)
普通的局部变量定义时直接定义,或者在定义前加auto关键字

void func1(void)
{
    int i = 1;
    i++;
    printf("i = %d.\n", i);
}
局部变量i的解析:
在连续三次调用func1中,每次调用时,在进入函数func1后都会创造一个新的变量i,并且给它赋初值1,然后i++时加到2,然后printf输出时输出2,然后func1本次调用结束,结束时同时杀死本次创造的这个i。这就是局部变量i的整个生命周期。
下次再调用该函数func1时,又会重新创造一个i,经历整个程序运算,最终在函数运行完退出时再次被杀死。

静态局部变量:静态局部变量定义时前面加static关键字。

1、静态局部变量在第一次函数被调用时创造并初始化,但在函数退出时它不死亡,而是保持其值等待函数下一次被调用。下次调用时不再重新创造和初始化该变量,而是直接用上一次留下的值为基础来进行 *** 作。
2、静态局部变量的这种特性,和全局变量非常类似。它们的相同点是都创造和初始化一次,以后调用时值保持上次的不变。不同点在于作用域不同。

寄存器变量:

总结:register类型的局部变量表现上和auto是一样的,这东西基本没用,知道就可以了。register被称为:C语言中最快的变量。C语言的运行时环境承诺,会尽量将register类型的变量放到寄存器中去运行(普通的变量是在内存中),所以register类型的变量访问速度会快很多。但是它是有限制的:首先寄存器数目是有限的,所以register类型的变量不能太多;其次register类型变量在数据类型上有限制,譬如你就不能定义double类型的register变量。一般只在内核或者启动代码中,需要反复使用同一个变量这种情况下才会使用register类型变量。

 全局变量的定义和初始化,是在main函数之前进行的。

多文件C语言项目:

1、简单的C语言程序(项目)只有一个C文件(a.c),编译的时候gcc a.c -o a,执行的时候./a
2、复杂的C语言程序(项目)是由多个C文件构成的。譬如一个项目中包含2个c文件(a.c, b.c),编译的时候 gcc a.c b.c -o ab,执行的时候 ./ab 


实验:
在a.c和b.c中分别定义main函数,各自单独编译时没问题;但是两个文件作为一个项目来编译gcc a.c b.c -o ab的时候,就会报错。multiple definition of `main'
为什么报错?
因为a.c和b.c这时候组成了一个程序,而一个程序必须有且只能有一个main函数。


为什么需要多文件项目?为什么不在一个.c文件中写完所有的功能?
因为一个真正的C语言项目是很复杂的,包含很多个函数,写在一个文件中不利于查找、组织、识别,所以人为的将复杂项目中的很多函数,分成了一个一个的功能模块,然后分开放在不同的.c文件中,于是乎有了多文件项目。
所以,在b.c中定义的一个函数,很可能a.c中就会需要调用。你在任何一个文件中定义的任何一个函数,都有可能被其他任何一个文件中的函数来调用。但是大家最终都是被main函数调用的,有可能是直接调用,也可能是间接调用。

多文件项目中,跨文件调用函数:
在调用函数前,要先声明该被调用函数的原型。只要在调用前声明了该函数,那么调用时就好像这个函数是定义在本文件中的函数一样。


总结:函数使用的三大要素:函数定义、函数声明、函数调用
1、如果没有定义,只有声明和调用:编译时会报连接错误。undefined reference to `func_in_a'
2、如果没有声明,只有定义和调用:编译时一般会报警告,极少数情况下不会报警告。但是最好加上声明。
3、如果没有调用,只有定义和声明:编译时一般会报警告(有一个函数没有使用),有时不会报警告。这时候程序执行不会出错,只是你白白的写了几个函数,而没有使用浪费掉了而已。

实验:在一个项目的两个.c文件中,分别定义一个名字相同的函数,结果?
编译报错 multiple definition of `func_in_a'
结论:在一个程序中,不管是一个文件内,还是该程序的多个文件内,都不能出现函数名重复的情况,一旦重复,编译器就会报错。主要是因为编译器不知道你调用该函数时到底调用的是哪个函数,编译器在调用函数时是根据函数名来识别不同的函数的。

跨文件的变量引用:

(1)通过实验验证得出结论:在a.c中定义的全局变量,在a.c中可以使用,在b.c中不可以直接使用,编译时报错 error: ‘g_a’ undeclared (first use in this function)
(2)想在b.c中使用a.c中定义的全局变量,有一个间接的使用方式。在a.c中写一个函数,然后函数中使用a.c中定义的该全局变量,然后在b.c中先声明函数,再使用函数。即可达到在b.c中间接引用a.c中变量的目的。
(3)想在b.c中直接引用a.c中定义的全局变量g_a,则必须在b.c中引用前先声明g_a,如何声明变量? extern int g_a;    

extern关键字:
extern int g_a;    这句话是一个全局变量g_a的声明,这句话告诉编译器,我在外部(程序中
不是本文件的另一个文件)某个地方定义了一个全局变量 int g_a,而且我现在要在这里引用它
告诉你编译器一声,不用报错了。

问题:
1、我只在b.c中声明变量,但是别的文件中根本就没有定义这个变量,会怎么样?
答案是编译报错(连接错误)undefined reference to `g_b'
2、我在a.c中定义了全局变量g_a,但是b.c中没有声明g_a,引用该变量会怎么样?
答案是直接抱错了,未定义
3、在a.c中定义,在b.c中声明,a.c和b.c中都没有引用该变量,会怎么样?
答案是不会出错。只是白白的定义了一个变量没用,浪费了


结论:不管是函数还是变量,都有定义、声明、引用三要素。其中,定义是创造这个变量或者函数,声明是向编译器交代它的原型,引用是使用这个变量或函数。所以如果没有定义只有声明和引用,编译时一定会报错。undefined reference to `xxx'

在一个程序里面,一个函数可以定义一次,引用可以有无数次,声明可以有无数次。因为函数定义或者变量的定义实际上是创造了这个函数/变量,所以只能有一次。(多次创造同名的变量会造成变量名重复,冲突;多次创造同名的函数也会造成函数名重名冲突)。声明是告诉编译器变量/函数的原型,在每个引用了这个全局变量/函数的文件之前都要声明该变量/函数

局部变量能不能跨文件使用?
不能。因为局部变量属于代码块作用域。他的作用域只有他定义的那个函数内部。

静态局部变量能不能跨文件使用?
不能。因为本质上还是个局部变量。

讨论跨文件使用问题,只用讨论全局变量和函数就可以了。

函数和全局变量在C语言中可以跨文件引用,也就是说他们的连接范围是全局的,具有文件连接属性,总之意思就是全局变量和函数是可以跨文件看到的(直接影响就是,我在a.c和b.c中各自定义了一个函数func,名字相同但是内容不同,编译报错。)。

全局变量总结:

定义在函数外面的变量,就叫全局变量。
普通全局变量
普通全局变量就是平时使用的,定义前不加任何修饰词。普通全局变量可以在各个文件中使
用,可以在项目内别的.c文件中被看到,所以要确保不能重名。

静态全局变量
静态全局变量就是用来解决重名问题的。静态全局变量定义时在定义前加static关键字,告诉编译器这个变量只在当前本文件内使用,在别的文件中绝对不会使用。这样就不用担心重名问题。所以静态的全局变量就用在我定义这个全局变量并不是为了给别的文件使用,本来就是给我这个文件自己使用的。

常量:

常量,程序运行过程中不会改变的量。常量的值在程序运行之前初始化的时候给定一次,以后都不会变了,以后一直是这个值。
1、#define定义的常量
    #define N 20            // 符号常量
    int a[N];

2、const关键字
    const int i = 14
const和指针结合,共有4种形式
const int *p;    p是一个指针,指针指向一个int型数据。p所指向的是个常量。        
int const *p;    p是一个指针,指针指向一个int型数据。p所指向的是个常量。    
int *const p;    p是一个指针,指针指向一个int型数据。p本身是常量,p所指向的是个变量
const int *const p;    p是一个指针,指针指向一个int型数据。p本身是常量,指向的也是常量

结论和记忆方法:
1、const在*前面,就表示const作用于p所指向的量。所以这时候p所指向的是个常量。
2、const在*后面,表示p本身是常量,但是p指向的不一定是常量。

const型指针有什么用?
比如:char *strcpy(char *dst, const char *src);
字符串处理函数strcpy,它的函数功能是把src指向的字符串,拷贝到dst中。

3、枚举常量
枚举常量是宏定义的一种替代品,在某些情况下会比宏定义好用。
enum

05 

头文件

为什么需要头文件?
从之前可以看到,函数的声明是很重要的。当我们在一个庞大的项目中,有很多个源文件,每一个源文件中都有很多个函数,并且需要在各个文件中相互穿插引用函数。


怎么解决函数的声明问题?靠头文件。

#include包含头文件时,用<>和""的区别
<>用来包含系统自带的头文件,系统自带指的是不是你写的,是编译器或者库函数或者 *** 作系统提供的头文件。
""用来包含项目目录中的头文件,这些一般是我们自己写的。

防止重复包含头文件
#ifndef __A_H__
#define __A_H__
// C语言头文件中的声明
#endif

写程序时,最好不要在头文件中定义变量。因为这时该头文件被多个源文件包含时,就会出现重复定义问题。全局变量的定义就应该放在某个源文件中,然后在别的源文件中使用前是extern声明。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/1295060.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-06-10
下一篇 2022-06-10

发表评论

登录后才能评论

评论列表(0条)

保存