C语言初级

本节关键字

内核空间,栈,堆,数据段,代码段,不可访问区
常量,变量,数据类型,整型,实型,布尔型,字符型,无类型,指针类型
组合数据类型:结构体,共用体,枚举
运算符,算数运算符,关系运算符,逻辑运算符,位运算符,特殊运算符,运算符优先级

一、内存管理

1. 内存管理概述

学习C语言入门,主要看是否搞清楚了内存分配,因为从定义一个变量开始,就已经涉及了到内存的申请,如果申请内存后不及时释放,就会导致内存泄漏,最终使得程序停止运行。因此,学习C语言,其实就是学习内存管理。

2. Linux内存空间分布

为什么学习Linux系统的内存空间,而不是windows或mac操作系统下的?最主要的原因是Linux操作系统是开源的,网上相关的资料也比较多,学习Linux内存管理足够我们去理解C语言的内存管理。

下图是Linux内存空间的结构图:

图1.1 Linux内存管理

序号

分区

释义

1

内核空间

kernel,Linux操作系统的核心

2

说明:系统自动分配的空间,最大一般是8MB大小

操作方法:后进先出

举例:环境变量、命令行参数、局部变量(调用时申请占空间,使用完毕立刻释放空间)

3

说明:用户申请的空间,最大值取决于系统的物理内存

举例:malloccallocreallocfree分配和释放的内存

4

数据段

.bss段 :存放未初始化的静态数据,它们都将被初始化为0 ,

举例:全局变量static修饰的局部变量

.data段:存放已初始化的静态数据 ,初始值从程序文件中拷贝而来

举例:全局变量static修饰的局部变量

.rodata段常量

5

代码段

.text段:存放用户程序代码,包括main函数在内的所有用户自定义函数

.init段:存储每一个程序执行前的“初始化”代码

6

不可访问区

<1>栈&堆

栈和堆都是动态变化的,分别向下和向上增长,大小随着进程的运行不断变大变小。

<2>全局变量&局部变量

局部变量存放在栈空间,用在功能函数中,在函数执行完毕后会被立即释放,再次调用函数时会重新定义该局部变量,系统会根据当前的内存空间进行动态分配,局部变量所存放的地址也肯定会改变;

而全局变量被存放在数据段的静态存储区(.bss/.data),直到整个进程结束时,全局变量所占的空间才会被释放掉,在整个进程里全局变量的存放地址是恒定的。

<3>静态数据

静态数据指的是:所有的全局变量,以及 static型局部变量(存放地址不会改变,直到进程结束才释放资源)。

尽量避免使用静态数据,因为它所占用的空间后直到进程结束才会被释放掉,程序运行期间在没有用到该静态数据时,它会无条件一直占用共享资源(内存)。

<4>环境变量、命令行参数

每一个程序运行前都需要有一段初始化代码,这些代码就包含了环境变量的准备,命令行参数的组织和传递,它们存放在栈空间的底部(紧挨内核的地方)。且在进程在整个运行期间不再发生变化,假如进程运行时对环境变量的个数或者值做了修改,则为了能够容纳修改后的内容,新的环境变量将会被拷贝放置到堆中。

图1.2 栈区的内存分配

3. 堆空间操作函数

堆空间是唯一一块用户可以自主定义的空间,下面介绍堆空间的申请和释放函数:

头文件:#include<stdlib.h>

<1>malloc申请一块堆空间

函数定义

void *malloc(size_t size);

参数

malloc(内存大小(字节))

功能

在堆中申请一块大小为size的连续的内存,申请的内存是未初始化的

返回值

成功,新申请的内存基地址 ;失败,NULL

<2>calloc申请多块堆空间

函数定义

void *calloc(size_t n, size_t size);

参数

calloc(块数,每块内存大小(字节))

功能

在堆中申请一个具有n个元素的匿名数组,每个元素大小为size,该函数申请的内存将被初始化为0

返回值

成功,新申请的内存基地址 ;失败,NULL

<3>realloc放大堆空间

函数定义

void *realloc(void*ptr, size_t size);

参数

realloc(堆内存地址,内存大小(字节))

功能

将ptr所指向的堆内存大小扩展为size

(1)返回的基地址可能跟原地址 ptr相同,也可能不同(即发生了迁移)

(2)当size为0时,该函数相当于相当于free(ptr);

返回值

成功,扩展后内存的基地址 ;失败,NULL

<4>free释放堆空间

函数定义

void free(void *ptr);

参数

free(堆内存地址)

功能

将指针ptr所指向的堆内存释放

参数ptr 必须是malloc( )/calloc( )/realloc()的返回值

返回值

二、数据类型

编程初期我们必须要定义一个对象(申请对象的空间),对这个对象进行操作,才能实现对应的功能,例如定义一个对象:

int a ;  //定义一个int类型的对象a

这里的“int”就是所谓的数据类型,“a”就是对象,它可以是常量或者变量。

1. 常量

存放在数据段的.rodata段,用const关键字修饰,一旦初始化(定义+赋值)后,其值不能改变,例如:

const int a = 10 ;   //定义一个int型的常量a,并给它赋值为10

常量包括整形常量,实型常量,字符常量和字符串常量。

2. 变量

如同它的名字,这是一个在初始化后仍可以随时再次改变数值的对象,例如:

int a = 10 ;    //定义一个int型的变量a,并把它赋值为10
a = 20 ;        //把变量a的值修改为20

变量按类型可以分为整形变量,实型变量等;按作用范围又可以分为局部变量和全局变量,其中局部变量存放在栈区,全局变量存放在数据段的静态数据区。

3. 常见的数据类型

(1)整型

数据类型

释义

大小

备注

int

整型(一切整数)

4byte

short

短整型

2byte

大小是int的一半

long

长整型

8byte

大小是int的两倍

(2)实型

数据类型

释义

大小

备注

float

实型(浮点型,一切实数,包括小数)

精确到小数点后六位

4byte

1.23e4 = 1.23×10^4

double

双精度实型

8byte

精度是float的两倍

long double

长双精度实型

16byte

精度是double的两倍

(3)布尔型

数据类型

释义

大小

备注

bool

布尔型(真(非0)、假(0),ture or false)

1byte

#include<stdbool.h>

(4)字符型

数据类型

释义

大小

备注

char

字符型

1byte

(5)无类型

数据类型

释义

大小

备注

void

无类型

1byte

(6)指针类型

数据类型

释义

大小

备注

int *

普通指针类型(还包括short *,long *,float *,double *,bool *等等)

8byte

void *

万能指针类型

8byte

验证代码

<1>验证数据类型大小的代码

#include <stdio.h>
#include <stdbool.h>

int main()
{     
    printf("int:%ld字节\n",sizeof(int)) ;
    printf("short:%ld字节\n",sizeof(short)) ;
    printf("long:%ld字节\n",sizeof(long)) ;
    printf("float:%ld字节\n",sizeof(float)) ;
    printf("double:%ld字节\n",sizeof(double)) ;
    printf("long double:%ld字节\n",sizeof(long double)) ;
    printf("bool:%ld字节\n",sizeof(bool)) ;
    printf("char:%ld字节\n",sizeof(char)) ;
    printf("void:%ld字节\n",sizeof(void)) ;

    return 0 ;
}

验证结果:

<2>验证指针类型的大小

#include <stdio.h>
#include <stdbool.h>

int main()
{    
    printf("int *:%ld字节\n",sizeof(int *)) ;
    printf("short *:%ld字节\n",sizeof(short *)) ;
    printf("long *:%ld字节\n",sizeof(long *)) ;
    printf("float *:%ld字节\n",sizeof(float *)) ;
    printf("double *:%ld字节\n",sizeof(double *)) ;
    printf("long double *:%ld字节\n",sizeof(long double *)) ;
    printf("bool *:%ld字节\n",sizeof(bool *)) ;
    printf("char *:%ld字节\n",sizeof(char *)) ;
    printf("void *:%ld字节\n",sizeof(void *)) ;

    return 0 ;
}

验证结果:

由此可见,所有指针类型大小都是8个字节(64位),而在32系统指针类型的大小是4个字节。

三、组合数据类型

组合数据类型,顾名思义,就是把一些相同类型或者不同类型的对象放到一起。

1. 结构体struct

<1>结构体练习

任务:定义一个学生结构体模板,再根据这个结构体模板生成两个结构体变量,分别存放两个学生的信息,并打印出来。

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

//定义一个模板结构体(不能赋值)
struct template
{
    char name[10]  ;  //姓名
    int age  ;        //年龄
    float height  ;   //身高
};

int main()
{    
    //利用上面的模板结构体template还可以定义新的结构体变量
    struct template stu1 ;    //根据template结构体衍生出stu1结构体
    struct template stu2 ;    //根据template结构体衍生出stu2结构体

    //给新的结构体的成员赋值
    strcpy(stu1.name,"小明") ;
    stu1.age = 10 ;
    stu1.height = 162 ;

    strcpy(stu2.name,"小红") ;
    stu2.age = 9 ;
    stu2.height = 151 ;

    //打印验证是否成功给新的结构体赋值
    printf("——————————————————————————————————————\n") ;
    printf("姓名:%s\n",stu1.name) ;
    printf("年龄:%d\n",stu1.age) ;
    printf("身高:%f\n",stu1.height) ;
    printf("——————————————————————————————————————\n") ;
    printf("姓名:%s\n",stu2.name) ;
    printf("年龄:%d\n",stu2.age) ;
    printf("身高:%f\n",stu2.height) ;

    return 0 ;
}

运行结果:

<2>计算结构体的大小

结构体的大小,取决与结构体成员中类型最大的那一个,类型最大的结构体成员是结构体的存储单位。比如求下图的结构体大小:

图3.1 结构体类型大小

上图的template模板结构体中,double是最大的类型,所以结构体的存储单位是8byte,值得注意的是,虽然name成员的类型最小(char类型),但是却占10byte!接下来就是给结构体成员分配空间(根据成员从上往下的顺序依次分配):

图3.2 结构体空间初分配

图中的绿框表示结构体的存储单位8byte,可以看到,name超出了2byte,age、sex、height都没能填满,money正好填满(因为就是以它为单位的)。

图3.4 结构体空间分配的结果

name超出去的部分怎么办?没关系,系统会进行动态分配,可以看到第2格有足够的空间,于是多出去的2byte就被分配到第2格里了,同时第5格的height也会被优化到第4格,以节省空间。

于是就可以算出结构体的大小为:8×4=32byte

最节省空间的方法是先把结构体成员进行从小到大排序。

验证代码

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

//定义一个模板结构体(不能赋值)
struct template
{
    char name[10]  ;    //姓名
    int age  ;          //年龄
    double money ;      //钱
    char sex ;          //性别
    float height  ;     //身高
};

int main()
{    
    //利用上面的模板结构体template还可以定义新的结构体变量
    struct template stu1 ;    //根据template结构体衍生出stu1结构体
    printf("stu1结构体的大小:%ld\n",sizeof(stu1)) ;

    return 0 ;
}

运行结果:

2. 共用体union

共用体也称联合体,与结构体类似,不同的是成员空间的分配方法。在结构体中,每个成员都根据自己的实际大小独占一份空间,而在共用体中,是所有成员共享一份空间,这个空间的大小是共用体中最大成员的大小。结构体中的所有成员在任何时刻都能够使用,而共用体在某一时刻只能使用一个成员。

<1>共用体练习

任务:定义一个共用体,并计算其大小。

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

//定义一个模板共用体(不能赋值)
union template
{
    char name[10]  ;    //姓名
    int age  ;          //年龄
    float height  ;     //身高
}x;                    

int main()
{    
    //利用上面的模板共用体template还可以定义新的共用体变量
    union template stu1 ;    //根据template共用体衍生出stu1共用体
    printf("stu1共用体的大小:%ld\n",sizeof(stu1)) ;
    printf("%ld\n",sizeof(x)) ;    //x表示共用体的大小

    return 0 ;
}

运行结果:

<2>计算共用体的大小

和结构体一样,共用体的大小也取决与共用体成员中类型最大的那一个,类型最大的成员是共用体的存储单位。比如计算下图中共用体的大小:

图3.5 计算共用体的大小

共用体中类型最大的是int和float都是4byte,所以共用体的存储单位是4byte,但是共用体中最大的成员name大小为10byte,共需要3个4byte空间存储,所以该共用体的大小为:4×3=12byte。(具体验证代码见上面)。

3. 枚举enum

枚举像一组宏定义,可以增强程序的可读性。如果枚举常量列表中的常量没有赋值,则默认从0开始递增。

任务:完成枚举应用的练习。

#include <stdio.h>

//定义一个枚举常量列表模板
enum template{red=1,green=2,blue=3} ;                   

int main()
{    
    //利用上面的枚举常量列表模板template,定义新的枚举常量列表变量
    enum template color ;    
    color = blue ;  //使用列表成员blue

    switch(color)
    {
        case red: printf("是红色\n") ; break ;
        case green: printf("是绿色\n") ; break ;
        case blue: printf("是蓝色\n") ; break ;
    }

    printf("枚举常量列表color的大小:%ld\n",sizeof(color)) ;

    return 0 ;
}

运行结果:

四、运算符

在编写代码时,我们经常会有一些计算方面的需求,下面罗列了C语言中常用的运算符和优先级排序。

1. 算数运算符

2. 关系运算符

3. 逻辑运算符

4. 位运算符

5. 特殊运算符

运算符

释义

=

赋值运算符

+=

复合赋值运算符,类似的还有-=,*=,/=,等等

( ) ? :

三目运算符,x=(a>b) ?a: b; //如果a>b,则x=a ;否则x=b

( , )

逗号运算符,比如有这个语句: int x =(a++,b+=1, a+b);则先运算a++,再运算b+=1,再把a+b的值作为整个逗号表达式的值赋给变量x。

6. 运算符优先级

五、随机数

1. rand

定义

rand ()

头文件

#include <stdlib.h>

注意:

(1)函数rand () 所产生的随机数是一个伪随机数,在每次执行程序时所产生的随机数都是一样的。

(2)调用函数rand () 生成的是一个在 0~32767 之间的整数。

控制计算机生成的随机数的取值范围的方法如下:

(1)利用 求余 运算rand ()%b 将函数rand ()生成的随机数变化到 [ 0,b-1 ] 之间。

(2)利用rand ()%b + a 将随机数的取值范围平移[ a,a+b-1 ] 之间。

2. srand

定义

srand ()

头文件

srand () 函数可以设置 随机数种子,从而使程序每次运行时产生不同的 随机数序列。

定义

srand (time(NULL)) ;

头文件

#include <time.h>

函数time () 可读取计算机的时钟值,并把该值设置为随机数种子

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值