c语言学习(6)

一、字符串

    字符:人能看得懂的符号或图案,在内存中以整数形式存储,根据ASCII码表中的对应关系显示出相应的符号或图案

    '\0'    0   空字符

    '0'     48

    'A'     65

    'a'     97

    串:是一种数据结构,存储类型相同的若干个数据

        对于串型结构的处理是批量性的,会从头开始直到遇到结束标志停止

   

    字符串:

        由字符组成的串型结构,结束标志是 '\0'

二、字符串的存在形式

    字符数组:

        char str[10] = {'a','b','c',...};

        由char组成的数组,注意要为'\0'预留位置,初始化麻烦

        使用的是栈内存,数据可以修改

   

    字符串字面值:

        "由双引号包含的若干个字符"

        末尾会隐藏一个'\0',定义也方便

        字符串字面值就是以地址形式存在的,是常量,数据存储在代码段中,不能修改,否则段错误

        注意:相同内容的多份字符串字面值,在代码段中只会存在一份

        注意:sizeof("xxxx") 计算出 字符个数+1

   

    常用方式:

        字符数组[] = "字符串字面值";

        会自动为'\0'预留位置

        注意:赋值完成后,该字符串在内存中有两份,一份在代码段,另一份在栈内存(可修改)

三、字符串的输入和输出

    scanf %s 地址

        缺点:不能输入空格

   

    char *gets(char *s);

    功能:输入字符串到s中 能够输入空格

    返回值:s 链式调用

    缺点:有警告,输入的长度不受限制,有风险

    char *fgets(char *s, int size, FILE *stream);

    功能:输入长度最多为 size-1 的字符串,会自动为'\0'预留位置

        超出部分不接收,不足时最后的'\n'也会一起接收

    输出:

    printf %s 地址

    int puts(const char *s);

    功能:输出一个字符串,并且会自动在末尾打印一个'\n'

    功能:成功输出的字符个数

四、输出缓冲区

    缓冲区机制可以提高数据的读写速度,还可以让低速的设备与高速的CPU之间系统工作

    程序要显示的数据并不会立即显示到屏幕上,而是先存储到输出缓冲区中,当满足一定条件时才会从输出缓冲区显示到屏幕上

        1、遇到'\n'

        2、遇到输入语句

        3、当缓冲区满了4k

        4、程序正常结束时  

        5、fflush(stdout); 手动刷新输出缓冲区

五、输入缓冲区

    程序中输入的数据并不会立即从键盘接收到变量中,而是当按下回车后先存储到输入缓冲区中,然后再从缓冲区中读取到变量内存中

    情况1:需要输入的是整型\浮点型时,而缓冲区中的数据是字符型或符号时,此时读取会失败,并且该数据会继续残留在输入缓冲区中,会继续影响剩下的输入

        解决:根据scanf的返回值判断输入是否有问题,如果读取失败,则先清理输入缓冲区后重新输入,直到读取成功为止

    情况2:通过fgets可以指定读取size-1个字符,但是如果输入超过size-1那么字符会残留在输入缓冲区中,继续影响接下来的输入

        解决方法1:

        int len = 0;

        while(str1[len]) len++; //len是'\0'的下标

        if('\n' != str1[len-1])// '\0'前面不是'\n'则清理

        {    

            scanf("%*[^\n]");

        //从缓冲区中读取任意类型数据并丢弃,直到遇到'\n'停止

            scanf("%*c");

        //从缓冲区中读取任意字符类型数据并丢弃

        }

        方法2:

        stdin->_IO_read_ptr = stdin->_IO_read_end;

        //  把输入缓冲区的位置指针从当前位置,移动到末尾,相当于清理输入缓冲区

        注意:只能在Linux系统下使用

    情况3:当先输入整型或浮点型,再输入字符型时,输入完整型或浮点型后按下的回车或空格,会残留在输入缓冲区,刚好被后面的字符型接收,影响输入

        解决:在%c或者gets()前面加空格

            scanf(" %c");

六、字符串相关函数

    #include <string.h>

    size_t strlen(const char *s);

    功能:计算字符串的长度,不包括'\0'

    char *strcpy(char *dest, const char *src);

    功能:把src拷贝给dest,相当于给dest赋值 =

    返回值:dest的首地址,链式调用

    char *strcat(char *dest, const char *src);

    功能:把src追加到dest的末尾 相当于+=

    返回值:dest的首地址,链式调用

    int strcmp(const char *s1, const char *s2);

    功能:比较两个字符串,根据字典序,谁出现早谁小,一旦比较出结果就立即返回

    返回值:

        s1 > s2 正数

        s1 == s2 0

        s1 < s2 负数      

       

    char *strncpy(char *dest, const char *src, size_t n);

    char *strncat(char *dest, const char *src, size_t n);

    int strncmp(const char *s1, const char *s2, size_t n);

    int atoi(const char *nptr);

    功能:把字符串转换成int类型

    double atof(const char *nptr);

    功能:把字符串转换成double类型

    char *strstr(const char *haystack,const char *needle);

    功能:在haystack中查找是否存在子串needle

    返回值:needle在haystack中第一次出现的位置,如果找不到返回NULL

   

    int sprintf(char *str, const char *format, ...);

    功能:把各种类型的数据转换成字符串并输入到str中

    int sscanf(const char *str, const char *format, ...);

    功能:从字符串中解析出各种类型的数据,并存储到对应的变量中

    void *memcpy(void *dest, const void *src, size_t n);

    功能:把src内存的数据拷贝n个字节到dest中

预处理指令:

    程序员所编写的代码不能被真正的编译器所编译,需要先经过一段程序翻译一下

    翻译的过程称为预处理,负责翻译的程序称为预处理器,被翻译的语句叫做预处理指令,以#开头的都是预处理指令

    查看预处理的结果:

        gcc -E code.c   把预处理的结果显示到终端

        gcc -E code.c -o code.i 把预处理的结果存储到.i预处理文件

   

预处理指令的分类:

    #include    头文件导入(拷贝)

        #include <> 从系统指定路径查找头文件

        #include "" 从当前工作路径查找,找不到再从系统指定路径查找

        -I path 可以指定要查找的路径path

        还可以通过设置环境变量来指定路径

   

    #define 定义宏

        宏常量:

            #define MAX 50

            优点:提高代码可扩展性、提高可读性、提高了安全性、还可以与case配合

            注意:定义宏常量不要加分号,一般宏名全部大写

            预定义好的宏常量:

                printf("%s\n",__func__);    获取函数名

                printf("%s\n",__FILE__);    获取文件名

                printf("%d\n",__LINE__);    获取行号

                printf("%s\n",__DATE__);    获取日期

                printf("%s\n",__TIME__);    获取时间

        宏函数:

            是带参数的宏

            不是真正意义的函数,没有发生传参,也没有返回值,也不会去检查参数的类型

            #define SUM(a,b) a+b

            1、先把在代码中出现了宏函数的位置,替换成宏函数后面的语句

            2、再把代码中使用的参数替换成调用者的参数

            注意:宏的内容必须保证在同一行,如果要换行,要在每一行的末尾添加续行符 \

       

        宏函数的二义性:

            由于宏函数代码位置、附近的值、参数各种原因的影响,会导致宏函数有不同的解释,这叫做宏的二义性

            如何避免宏的二义性:

                每个参数都加小括号,整体也叫小括号,不要在宏函数的参数中使用自变运算符

预处理指令:

    #define

    1、简述#define与typedef的区别:

        如果是普通类型,它们在功能上无任何区别,但本质不同,一个是代码替换,一个是类型重定义

        #define INTP int*

            INTP p1,p2,p3;  //p1是指针 p2 p3是int

            int* p1,*p2,*p3

        typedef int* INTP;

            INTP p1,p2,p3;  //p1p2p3都是指针

            size_t s1,s2,s3;

   

    2、宏函数与普通函数的区别?

        是什么?

        普通函数:是一段觉有某项功能的代码集合,会被编译成二进制指令存储在代码段中,函数名就是它的首地址,有独立的栈内存

        宏函数:带参数的宏替换,不是真正的函数,用起来像函数,没有独立的栈内存

        有什么区别?

        函数:  返回值、类型检查、安全、入栈出栈调用、跳转、速度慢

        宏函数:运行结果、通用、危险、替换、冗余、速度快

    条件编译:

        根据条件决定让代码是否参与最终的编译

        版本控制:

        #if

        #elif

        #else

        #endif

        头文件卫士:防止头文件被重复包含,头文件必加

        #ifndef 宏名    //如果宏不存在为真

        #define 宏名

        //

        #endif

        判断、调试:

        #ifdef 宏名 //如果宏存在为真

        #else

        #endif

        在编译时添加宏DEBUG:gcc 02debug.c -DDEBUG

    打印调试信息:

        #ifdef DEBUG

            #define debug(...) printf(__VA_ARGS__)

        #else

            #define debug(...)

        #endif

    打印错误信息:

        #define error(...) printf("%s %s:%d %s %m %s %s\n",__FILE__,__func__,__LINE__,__VA_ARGS__,__DATE__,__TIME__)

       

头文件中应该写什么:

    头文件可能会被任意源文件包含,意味着头文件中的内容可能会在多个目标文件中存在,要保证合并时不要冲突

    重点:头文件只编写声明语句,不能有定义语句

        全局变量声明

        函数声明

        宏常量

        宏函数

        typedef 类型重定义

        结构、枚举、联合的类型设计声明

    头文件的编写规则:

        1、为每个.c文件写一份.h文件,.h文件是对它对应的.c文件的说明

        2、如果需要用到某个.c文件中的变量、函数、宏时,只需要把该文件的.h文件导入即可

        3、.c文件也要导入自己的.h文件,目的是为了让定义与声明保持一致

    头文件的相互包含:

        假如a.h包含了b.h的内容,而b.h中又包含了a.h的内容,这时就会产生头文件的相互包含,无法编译通过

        解决方案:把a.h中需要b.h的内容,和b.h中需要a.h的内容提取出来,额外再写另一个c.h

Makefile:

    Makefile是由一系列的编译器指令组成的可执行文件,叫做编译脚本

    在终端执行 make 命令就会自动执行Makefile脚本中的编译指令,它可以根据文件的修改时间、和依赖关系来判断哪些文件需要编译,哪些不需要编译

    需要一个名字叫做 Makefile 的编译文件

    Makefile的编译规则:

    1. 如果这个工程没有编译过,那么我们的所有c 文件都要编译并被链接。

    2. 如果这个工程的某几个c 文件被修改,那么我们只编译被修改的c 文件,并重新链接目标程序。

    3. 如果这个工程的头文件被改变了,那么引用了这几个头文件的c 文件都会重新编译,并链接目标程序。

    一个最简单的Makefile脚本格式:

    执行总目标:依赖

        编译指令

    被依赖的目标1:依赖的文件

        编译指令

    被依赖的目标2:依赖的文件

        编译指令

        ...

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值