设为首页 加入收藏

TOP

c语言学习7(一)
2023-07-23 13:25:09 】 浏览:50
Tags:

函数传参
1、函数中定义的变量属于该函数,出了该函数就不能再被别的函数直接使用
2、实参与形参之间是以赋值的方式进行传递数据的,并且是单向值传递
3、return语句其实是把返回值数据放入公共区域内存中(调用者和被调用者都可以访问),调用者会从该区域获取返回值;如果不写return语句,该区域会是一个随机的垃圾数据,调用者也能拿到返回值但是无意义。
4、数组作为函数的参数传递时,数组的长度会丢失,需要额外增加一个变量把数组的长度传递过去
void func(int arr[],int len);
int arr[10];
func(arr,10)
5、数组作为参数传递时,是"址传递",相当于调用者与函数共享数组
练习1:实现一个函数,找出数组中的最大值
练习2:实现一个函数,对数组进行排序
练习3:实现一个函数,查找数组中是否存在某个值,如果存在返回该数值在数组中的下标

设计函数的准则:
1、一般一个函数最好不要超过50行,确保一个函数只负责完成一项功能,降低出错概率,提高可读性
2、数据一般要由调用者提供,只把结果返回给调用者,确保函数的通用性
3、考虑调用者提供的非法数据,可以先判断后使用,也可以通过注释或说明来写明情况,提高函数的健壮性

进程映像:
程序:存储在磁盘上的可执行文件(二进制文件、脚本文件)
进程:正在系统中运行的程序
进程映像:进程的内存分布情况 //注意:在内存中,从地址到高地址依次是:text、data、bss、heap、stack(其他映像都是从低地址往高地址走,而stack是从高地址往低地址走的,
//这样的原因是:1、空间利用率:设计使得堆和栈可以动态地使用剩余的内存
//2、防止冲突:防止相互覆盖溢出
//3、安全性:栈的增长方向通常是向下的,这是因为大多数系统中的栈是用来存储函数调用和局部变量的,这些数据的生命周期短且易变。
//如果栈向上增长,那么栈溢出可能会覆盖到静态数据或者程序代码,这会带来安全性问题。)
text 代码段:(代码段+只读段)
存储的是二进制指令、常量,权限是只读,如果强制修改会产生段错误
data 数据段:
初始化的全局变量、初始化过的静态局部变量
bss 静态数据段:
未初始化的全局变量、未初始化的静态局部变量(//需要注意的是这两种变量初始化的值为0时,也是存储在bss段中的)
在该段内存中的数据在程序开始前会自动清理为0
stack 栈:
局部变量和块变量,会随着程序的运行不断地申请、释放,由操作系统管理,使用方便,内存小
heap 堆:
该段内存由程序员手动管理,使用麻烦,足够大

局部变量和全局变量:
全局变量:定义在函数外的变量
存储位置:data(初始化) 或者 bss(未初始化)
生命周期:程序开始到程序结束
使用范围:程序的任意位置都可以使用
局部变量:定义在函数内的变量
存储位置:stack 栈内存
生命周期:从函数开始到函数结束
使用范围:只能在该函数内使用
块变量:定义在if/for/while等语句块内的变量
存储位置:stack 栈内存
生命周期:从语句块开始到语句块结束
使用范围:只能在语句内使用

注意:同名的局部变量会屏蔽同名的全局变量
    同名的块变量会屏蔽同名的全局、局部变量
    因此建议全局变量首字母大写,局部变量全部小写

类型限定符:
auto
用于定义自动申请、自动释放的变量(局部变量),不加就代表加了
注意:在C11语法标准中用于自动类型识别
auto num = 10; //int
auto num = 3.13;//double
注意:不用用它修饰全局变量
extern
用于声明外部变量,意思是告诉编译器此变量在程序的其他地方已经定义了,先让程序通过编译,如果在链接时找不到该变量依然会报错
不建议在extern时赋值,它只是声明

static
   改变存储位置:
        改变局部变量的存储位置,由stack改为data(初始化)或者bss(未初始化)
   延长生命周期:
        延长局部变量的生命周期,直到程序结束才释放
   限制作用范围: 
        限制全局变量、函数的使用范围,限制只能在本文件内使用
        注意:使用static修饰全局变量,可以防止该变量被别的文件使用,以及防止命名冲突

const
    "保护"变量的值不被显式地修改
    注意:如果通过内存进行修改,还是可以改的
    注意:使用const修饰data段数据,那么该数据会存储到text段中,如果强制修改会段错误

volatile
    C编译器会对普通变量的取值进行"取值优化",只要在使用变量过程中该变量没有显式改变,那么编译器会直接使用上一次的结果,而不会每次都去内存读取数据
    加上volatile修饰,让编译器不要对该变量进行"取值优化"
    一般在驱动编程、硬件编程、多线程编程时使用
    volatile int num = 10;
    if(num == num)
    {
        //  可能为假
    }

register
    存储介质:
        硬盘->内存->高级缓存->寄存器->CPU
    申请把变量的存储介质由内存改为寄存器,但是由于寄存器数量有限,不一定百分百成功
    注意:寄存器变量不能取地址

typedef
    类型重定义
    在定义变量前,加上typedef,那么原本的变量名就变成了这种数据类型,可以像数据类型一样定义变量
    typedef int num;
    num n1;

讨论:关于typedef和宏定义的区别
    例:
        typedef int INT
        INT n1;
        #define INT int 
        INT n1; //以上这两个其实在使用时是效果一样的,一个类型重定义,一个是宏替换。

    例:
        typedef int* INTP 
        INTP p1,p2,p3;
        define INTP int*
        INT p1,p2,p3;这次两个是有区别的,因为上面的那个还是定义了三个指针,但是第二个变成了int *p1,p2,p3;这样只是定义了一个p1指针,p2和p3是整型
        一、什么是指针
指针是一种特殊的数据类型,使用它可以定义指针变量,指针变量中存储的是整型数据,该数据代表了内存的编号(地址),可以通过这个编号访问到对应的内存

二、为什么要使用指针
1、函数之间内存是相互独立的,但有时候需要函数之间共享变量
普通传参是单向值传递
全局变量容易命名冲突
使用数组还需要额外传递长度
虽然函数之间内存空间和命名空间是相互独立的,但是地址空间是同一个,所以使用指针可以解决这个问题

2、由于函数之间普通变量是单向值传递(拷贝),因此对于一些字节数比较多的变量,值传递的效率很低,如果传递的是地址只需要4(32位)\8(64位)字节,可以提高传参效率

3、堆内存无法取名字,它不像data、bss、stack这些可以让变量名与对应的内存建立联系,只能使用指针变量记录堆内存的地址从而使用堆内存

三、如何使用指针
定义:
类型名* 变量名_p;
1、指针变量与普通变量的用法有很大区别,建议在取名时以p结尾加以区分
2、指针变量的类型表示它存储的是什么类型变量的地址,它决定了通过该指针变量能够连续访问的字节数
3、一个只能定义一个指针变量
int
a,b,c; // a是指针变量,bc是int类型变量
int p1,p2,*p3; //p1p2p3都是指针变量
4、指针变量与普通变量一样,默认初始值是随机的,一般初始化为NULL

2、赋值
    变量名_p = 地址;    //必须
首页 上一页 1 2 下一页 尾页 1/2/2
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇c语言学习8 下一篇[笔记]堆和规范的堆操作 //规范的..

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目