目录
1.常见集成开发环境(IDE/Integrated Development Environment )
C语言主体部分:
0.编译步骤:
1)预处理:宏定义展开、头文件展开、条件编译,删除注释。
整个预处理部分不进行任何的语法解析,只进行简单的文本拷贝。
比如#include先找当前目录,没有再找系统目录。
2)编译:检查语法,并将文件转化为汇编文件(将高级语言转化为汇编语言)
3)汇编:将汇编文件转化为目标文件(将汇编语言转化为机器语言)
4)链接:把依赖项及库链接到可执行程序中
1.常见集成开发环境(IDE/Integrated Development Environment )
是用于提供程序开发环境的应用程序,一般包括代码编辑器、编译器、调试器和图形用户界面等工具。集成了代码编写功能、分析功能、编译功能、调试功能等一体化的开发软件服务套。所有具备这一特性的软件或者软件套(组)都可以叫集成开发环境。
1) Visual Studio系列及MFC
2)Qt
2.32个关键字
- 数据类型关键字(12个)
char,short,int,long,float,double,
unsigned,signed,struct(结构体),union(共用体),enum(枚举),void
- 控制语句关键字(12个)
if,else,switch,case,default,for,do,while,break,continue,goto,return
- 存储类关键字(5个)
auto(自动变量),extern(外部类型变量),register(寄存器类型变量),static(静态类型变量),const
- 其他关键字(3个)
sizeof,typedef,volatile
3.数据类型
3.1常量
在程序运行过程中,其值不能被改变,成为常量。
两种定义方式
方式1:使用关键字define
#define identifier value
//eg:
#define P 3.1415
//C中最稳定的定义常量的方法
方式2:使用关键字const
在C中不稳定,可以通过指针来修改,但是在C++中是稳定和安全的
const type variable = value;
//eg:
const int test_int = 10;
字符常量:
在 C 中,有一些特定的字符,当它们前面有反斜杠时,它们就具有特殊的含义,被用来表示如换行符(\n)或制表符(\t)等。下表列出了一些这样的转义序列码:
转义序列 | 含义 |
---|---|
\\ | \ 字符 |
\' | ' 字符 |
\" | " 字符 |
\? | ? 字符 |
\a | 警报铃声 |
\b | 退格键 |
\f | 换页符 |
\n | 换行符 |
\r | 回车 |
\t | 水平制表符 |
\v | 垂直制表符 |
\ooo | 一到三位的八进制数 |
\xhh . . . | 一个或多个数字的十六进制数 |
3.2变量
在程序运行过程中,其值可以改变,称为变量 。
bit(比特) 一个二进制代表一位,一个位只能表示0或1两种状态
Byte(字节)计算机存储的最小单位是字节
1 Byte = 8 bit
3.2.1整数类型及字符类型
下表列出了关于标准整数类型的存储大小和值范围的细节:
类型 | 存储大小 | 值范围 |
---|---|---|
char | 1 字节 | -128 到 127 或 0 到 255 |
unsigned char | 1 字节 | 0 到 255 |
signed char | 1 字节 | -128 到 127 |
int | 2 或 4 字节 | -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647 |
unsigned int | 2 或 4 字节 | 0 到 65,535 或 0 到 4,294,967,295 |
short | 2 字节 | -32,768 到 32,767 |
unsigned short | 2 字节 | 0 到 65,535 |
long | 4 字节 | -2,147,483,648 到 2,147,483,647 |
unsigned long | 4 字节 | 0 到 4,294,967,295 |
3.2.2浮点类型
下表列出了关于标准浮点类型的存储大小、值范围和精度的细节:
类型 | 存储大小 | 值范围 | 精度 |
---|---|---|---|
float | 4 字节 | 1.2E-38 到 3.4E+38 | 6 位小数 |
double | 8 字节 | 2.3E-308 到 1.7E+308 | 15 位小数 |
long double | 16 字节 | 3.4E-4932 到 1.1E+4932 | 19 位小数 |
注意,各种类型的存储大小与系统位数有关,但目前通用的以64位系统为主。
以下列出了32位系统与64位系统的存储大小的差别(windows 相同):
注意:指针类型存储的是所指向变量的地址,所以32位机器只需要32bit,而64位机器需要64bit。
常与变量捆绑的关键字sizeof
sizeof() | 返回变量的大小。 | sizeof(a) 将返回 4,其中 a 是整数。 |
3.3字符、字符串及字符串处理函数
关于字符和字符串(字符数组):
字符串是有字符组成的,但是多了一个字符串结束符,而在赋值的方式也不一样,字符是单引号,字符串是双引号。
字符使用普通char变量,而字符串使用char类型的数组。
在 C 语言中,字符串实际上是使用 null 字符 '\0' 终止的一维字符数组。因此,一个以 null 结尾的字符串,包含了组成字符串的字符。
下面的声明和初始化创建了一个 "Hello" 字符串。由于在数组的末尾存储了空字符,所以字符数组的大小比单词 "Hello" 的字符数多一个。
char greeting[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
依据数组初始化规则,可以把上面的语句写成以下语句:
char greeting[] = "Hello";
char str1[12] = "Hello";
char str2[12] = "World";
printf("message: %s\n", str1 );
以下是 C/C++ 中定义的字符串的内存表示:
其中,‘\0’表示字符串结束符,很多字符串处理函数,对字符串的处理都是止步于字符串结束符'\0'。
关于字符串处理函数(17个):https://mp.csdn.net/postedit/99708133
3.4函数
函数由四个部分组成
- 函数的声明
- 函数的原型
- 函数的返回值
- 函数的参数
函数原型的语法结构如下,意在表明,本函数进行的到底是怎样的操作,根据需求,规定相应的返回值。
return_type function_name( parameter list )
{
body of the function
}
函数的声明,C中,需要放置于C中main()函数,C++中,需要放置于类声明中。
return_type function_name( parameter list );
而声明及原型中的parameter list称为形式参数
在该函数被调用的过程中,被赋予的参数称为实际参数,如下:
void main()
{
.....
function_name( parameter list );
.....
return 0;
}
注意:参数之间用逗号隔开,并且需要声明参数的数据类型,比如:
void function_test(int list_frist,double list_second,float list_third)
{
........
.
.
.
........
}
注意:数组名作为函数参数时会自动退化为指针
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
double a[10];
qDebug()<<"实际中的数组名占用空间:"<<sizeof(a);
Test(a);
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::Test(double a[])
{
qDebug()<<"函数中的数组名占用空间:"<<sizeof(a);
}
结果:
在Qt编译时,编译器会出现警告:
所以想通过函数名作为参数,在函数中计算数组长度不能使用常规的方式,要么是最后一位有特征标志位,要么把数组长度当参数一起传递。
字符串不用担心这个问题,什么字符串不论字符串数组还是字符串指针最后一位都是特征标志位,字符串结束符 \0
3.4.1函数与作用域
int * MyFunc()
{
int a = 10;
return &a;
}
void main()
{
int * p = MyFunc();
pringtf("*p = %d\n",*p);
}
a的作用域在MyFunC();调用结束后,就被释放了,返回的指针所指向的内存,其中保存的是不是10都不重要了,这块内容随时有可能会被重新利用,会被其他内容代替,所以在调用函数时一定要考虑到作用域的问题。指针指向的内存空间是否是确定的。
以上程序的操作是非常危险的。
3.4.2函数传参的具体工作过程
void allocataSpace(char *pp)
{
pp = malloc(100);
memset(pp,0,100);
strcpy(pp,"hello world");
}
void test02()
{
char * p = NULL;
allocataSpace(p);
printf("p = %s\n",p);
}
结果是无法输出理想中的hello world。
首先,malloc在堆区分配空间,地址为0x####%%,将这个地址赋给allocataSpace函数中的变量pp,pp保存是传递过来参数的值,及p的值,NULL;
之后因为要拷贝字符串,字符串自动在常量区进行创建,之后将内容拷贝给堆区,并将堆区存有字符串的首地址给pp。。
整个函数过程完成,之后回到test02中,函数完成调用,栈区凡是和allocataSpace相关的变量全部销毁。
最终整个过程中,test02中的p没有任何变化,且丢失了堆区内存的首地址,内存泄漏。
关于函数,会在函数执行中,将参数和函数内定义的变量在栈区开辟一片内存。结束后销毁,所以在函数传参的时候务必要注意
关于以上问题,该如何修改?如下
下面使用到了二级指针的部分,因为传递来的是一级指针的地址,需要使用二级指针进行接收,就如同传递普通变量的地址,使用一级指针来接收一样,关于二级指针后文有详细介绍。
void allocataSpace(char **pp)
{
char * temp = malloc(100);
memset(temp,0,100);
strcpy(temp,"hello world");
*pp = temp;
}
void test()
{
char * p = NULL;
allocataSpace(&p);
printf("p = %s\n",p);
}
首先,allocataSpace函数中的变量pp保存的是test函数中p的地址。不同于第一个函数,保存的是NULL。
malloc在堆区分配空间,地址为0x####%%,将这个地址赋给allocataSpace函数中的变量temp;
之后因为要拷贝字符串,字符串自动在常量区进行创建,之后将内容拷贝给堆区,并将堆区存有字符串的首地址给temp。
最后取pp中地址所指向的内存,并将其换成指向堆区的地址。
整个函数过程完成,之后回到test02中,函数完成调用,栈区凡是和allocataSpace相关的变量全部销毁。
二者最大的区别在于,函数对栈区内内存的改写,到底最后哪儿些部分是实打实影响到调用该函数的部分。
从以上两个例子中,也不难看出函数运行过程中对参数及函数内定义的变量分配空间及销毁的过程。
3.4.3函数运行过程中内存的分配
int * getSpace()
{
int * p = malloc(sizeof(int) * 5);
if( NULL == p )
return 0;
for(int i = 0;i<5;i++)
{
p[i] = 100 + i;
}
return p;
}
p:在栈上被创建
但p所指向的地址,是在堆区上。
getSpace()被调用后,p被释放,但是p所指向的内存,依旧在堆区上。
void main()
{
int * ret = getSpace();
for( int i = 0;i<5;++i)
{
printf("%d",ret[i]);
}
free(ret);
ret = NULL;
}
所以返回堆区的首地址后,依旧可以访问堆区,因为堆区释放是程序员人工释放,函数结束调用后自动释放的只是在栈区的变量。
注意:
1,堆区一定要拿到数据段的首地址才能释放,否则无法释放。
2,只要是连续的内存空间,都可以使用指针名【】来进行访问。